From: Stefano Sabatini <stefasab@gmail.com> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: Re: [FFmpeg-devel] [PATCH] avfilter: add sdlvsink for video display Date: Wed, 12 Jun 2024 21:52:43 +0200 Message-ID: <Zmn8i9PWFNVcOEeD@mariano> (raw) In-Reply-To: <CACW1+SZGD=yE-d3u4ToWDCHZLU8S3+nc_pguBvLDUWWYb3wAkQ@mail.gmail.com> [-- Attachment #1: Type: text/plain, Size: 2512 bytes --] On date Tuesday 2024-06-11 21:13:48 +0800, Shiqi Zhu wrote: > On Fri, 7 Jun 2024 at 19:55, Rémi Denis-Courmont <remi@remlab.net> wrote: > > Le 7 juin 2024 12:53:51 GMT+03:00, Michael Niedermayer <michael@niedermayer.cc> a écrit : > > >We can require anything from an API that we are able to change and extend > > >Of course we can decide not to allow such requirment even if optional > > >but we surely _could_ add such a feature if we choose to do so > > > > Sure. You can also require infinite memory or an oracle be provided. That's just not going to happen though. And having libraries depend on the main thread is a well-documented malpractice. > > > > I don't think we should add maintenance burden with code that can't be used safely. > > Thank you all for your attention to this patch; I greatly appreciate it. > > I'd like to provide a brief recap of the issue we've been discussing, > with the following points: > > 1. Addition of sink type in the filter: > This enhancement is primarily based on the existing avfilter mechanism > and serves as a strengthening module. Using SDL as the sink doesn't > seem to be a good fit, as I'll attempt to rectify in the following > patch. > https://patchwork.ffmpeg.org/project/ffmpeg/patch/20240611130310.1131755-1-hiccupzhu@gmail.com/ > > 2. Utilizing SDL as an implementation for the sink: > Before submitting the patch, I hadn't considered many aspects. During > the intense discussions, I retested the patch on different operating > systems, with the following results, hoping it may assist those > interested in this issue: > > Command: ./ffmpeg -lavfi > "testsrc2=size=300x200:rate=25:duration=500,format=yuv420p,sdlvsink" > -f null /dev/null In addition to this, I wonder if adding a vsink for each different output device is the correct way. We have a movie source which can be used to read from libavformat/libavdevice, probablhy we should have a movie sink to be used to write to libavformat/libavdevice, meaning that a single sink would enable access to all the supported libavformat/libavdevice outputs. I started having a look in that direction a looot of time ago. This was never finalized because I was not sure about ways to pass options to encoders and muxers, and about dealing with a variable number of outputs, I'm attaching this very old proof-of-concept patch for reference. This approach would be possibly much more complex, but should provide a single bridge in place of having a different sink for every output device or muxer. [-- Attachment #2: 0001-lavfi-add-moviesink-and-amoviesink-sinks.patch --] [-- Type: text/x-diff, Size: 15228 bytes --] From dc88e3f19ad0e481e0adc5e192ad3e2b4b249f55 Mon Sep 17 00:00:00 2001 From: Stefano Sabatini <stefasab@gmail.com> Date: Wed, 11 Apr 2012 15:10:14 +0200 Subject: [PATCH] lavfi: add moviesink and amoviesink sinks --- configure | 2 + libavfilter/Makefile | 4 + libavfilter/allfilters.c | 2 + libavfilter/sink_moviesink.c | 365 +++++++++++++++++++++++++++++++++++ 4 files changed, 373 insertions(+) create mode 100644 libavfilter/sink_moviesink.c diff --git a/configure b/configure index 81c7cc18a2..5e08fbbf6c 100755 --- a/configure +++ b/configure @@ -1674,6 +1674,7 @@ udp_protocol_deps="network" # filters aconvert_filter_deps="swresample" amovie_filter_deps="avcodec avformat" +amoviesink_filter_deps="avcodec avformat" aresample_filter_deps="swresample" ass_filter_deps="libass" asyncts_filter_deps="avresample" @@ -1690,6 +1691,7 @@ frei0r_src_filter_deps="frei0r dlopen" frei0r_src_filter_extralibs='$ldl' hqdn3d_filter_deps="gpl" movie_filter_deps="avcodec avformat" +moviesink_filter_deps="avcodec avformat" mp_filter_deps="gpl avcodec swscale postproc" mptestsrc_filter_deps="gpl" negate_filter_deps="lut_filter" diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 29345fc15e..a2bd404304 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -7,8 +7,10 @@ FFLIBS-$(CONFIG_RESAMPLE_FILTER) += avresample FFLIBS-$(CONFIG_ACONVERT_FILTER) += swresample FFLIBS-$(CONFIG_AMOVIE_FILTER) += avformat avcodec +FFLIBS-$(CONFIG_AMOVIESINK_FILTER) += avformat avcodec FFLIBS-$(CONFIG_ARESAMPLE_FILTER) += swresample FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec +FFLIBS-$(CONFIG_MOVIESINK_FILTER) += avformat avcodec FFLIBS-$(CONFIG_PAN_FILTER) += swresample FFLIBS-$(CONFIG_REMOVELOGO_FILTER) += avformat avcodec FFLIBS-$(CONFIG_MP_FILTER) += avcodec postproc @@ -65,6 +67,7 @@ OBJS-$(CONFIG_AMOVIE_FILTER) += src_movie.o OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o OBJS-$(CONFIG_ABUFFERSINK_FILTER) += sink_buffer.o +OBJS-$(CONFIG_AMOVIESINK_FILTER) += sink_moviesink.o OBJS-$(CONFIG_ANULLSINK_FILTER) += asink_anullsink.o OBJS-$(CONFIG_ASS_FILTER) += vf_ass.o @@ -133,6 +136,7 @@ OBJS-$(CONFIG_RGBTESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_TESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_BUFFERSINK_FILTER) += sink_buffer.o +OBJS-$(CONFIG_MOVIESINK_FILTER) += sink_moviesink.o OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o OBJS-$(CONFIG_MP_FILTER) += libmpcodecs/mp_image.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index b9d44f2fdf..a7cc9ad3de 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -56,6 +56,7 @@ void avfilter_register_all(void) REGISTER_FILTER (ABUFFERSINK, abuffersink, asink); REGISTER_FILTER (ANULLSINK, anullsink, asink); + REGISTER_FILTER (AMOVIESINK, amoviesink, asink); REGISTER_FILTER (ASS, ass, vf); REGISTER_FILTER (BBOX, bbox, vf); @@ -123,6 +124,7 @@ void avfilter_register_all(void) REGISTER_FILTER (TESTSRC, testsrc, vsrc); REGISTER_FILTER (BUFFERSINK, buffersink, vsink); + REGISTER_FILTER (MOVIESINK, moviesink, vsink); REGISTER_FILTER (NULLSINK, nullsink, vsink); /* those filters are part of public or internal API => registered diff --git a/libavfilter/sink_moviesink.c b/libavfilter/sink_moviesink.c new file mode 100644 index 0000000000..27c8cca078 --- /dev/null +++ b/libavfilter/sink_moviesink.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2012 Stefano Sabatini + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Movie video sink filter + */ + +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "libavformat/avformat.h" +#include "avcodec.h" +#include "avfilter.h" + +typedef struct { + const AVClass *class; + char *codec_name; + char *file_name; + char *format_name; + + AVOutputFormat *oformat; + AVFormatContext *oformat_ctx; + AVCodec *codec; + + int w, h; + AVFilterBufferRef *bufref; +} MovieSinkContext; + +#define OFFSET(x) offsetof(MovieSinkContext, x) + +static const AVOption moviesink_options[] = { +{"codec_name", "set codec name", OFFSET(codec_name), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MIN, CHAR_MAX }, +{"c", "set codec name" , OFFSET(codec_name), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MIN, CHAR_MAX }, +{"format_name", "set format name", OFFSET(format_name), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MIN, CHAR_MAX }, +{"f", "set format name", OFFSET(format_name), AV_OPT_TYPE_STRING, {.str = 0}, CHAR_MIN, CHAR_MAX }, +{NULL}, +}; + +static const char *moviesink_get_name(void *ctx) +{ + return "moviesink"; +} + +static const AVClass moviesink_class = { + "MovieSinkContext", + moviesink_get_name, + moviesink_options +}; + +static av_cold int moviesink_common_init(AVFilterContext *ctx, const char *args, void *opaque, + enum AVMediaType type) +{ + MovieSinkContext *movie = ctx->priv; + AVOutputFormat *oformat = NULL; + AVStream *st; + enum CodecID codec_id; + int ret; + + movie->class = &moviesink_class; + av_opt_set_defaults(movie); + + if (args) + movie->file_name = av_get_token(&args, ":"); + if (!movie->file_name || !*movie->file_name) { + av_log(ctx, AV_LOG_ERROR, "No filename provided!\n"); + return AVERROR(EINVAL); + } + + if (*args++ == ':' && (ret = av_set_options_string(movie, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); + return ret; + } + + av_register_all(); + movie->oformat_ctx = avformat_alloc_context(); + av_strlcpy(movie->oformat_ctx->filename, movie->file_name, + sizeof(movie->oformat_ctx->filename)); + + if (!(oformat = av_guess_format(movie->format_name, movie->file_name, NULL))) { + av_log(ctx, AV_LOG_ERROR, + "Unable to find a suitable output format for '%s'\n", movie->file_name); + return AVERROR(EINVAL); + } + movie->oformat_ctx->oformat = movie->oformat = oformat; + + /* add a stream */ + st = avformat_new_stream(movie->oformat_ctx, NULL); + if (!st) { + av_log(ctx, AV_LOG_ERROR, "Could not allocate stream\n"); + return AVERROR(ENOMEM); + } + + avcodec_get_context_defaults3(st->codec, NULL); + + if (movie->codec_name) { + movie->codec = avcodec_find_decoder_by_name(movie->codec_name); + } else { + codec_id = av_guess_codec(movie->oformat_ctx->oformat, NULL, + movie->oformat_ctx->filename, NULL, type); + movie->codec = avcodec_find_encoder(codec_id); + } + + if (!movie->codec) { + av_log(ctx, AV_LOG_ERROR, "Failed to find any codec\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static av_cold void moviesink_common_uninit(AVFilterContext *ctx) +{ + MovieSinkContext *movie = ctx->priv; + + av_freep(&movie->codec_name); + av_freep(&movie->file_name); + av_freep(&movie->format_name); + + if (movie->oformat_ctx) + avformat_close_input(&movie->oformat_ctx); + + avfilter_unref_bufferp(&movie->bufref); +} + +static int make_filter_formats(AVFilterFormats **ffmts, const int *fmts) +{ + int i, len; + const int *p = fmts; + for (len = 0; *p != -1; p++, len++) + ; + + if (!(*ffmts = av_mallocz(sizeof(AVFilterFormats)))) + return AVERROR(ENOMEM); + if ((!((*ffmts)->formats = av_malloc(sizeof((*ffmts)->formats) * len)))) { + av_free(*ffmts); + return AVERROR(ENOMEM); + } + + (*ffmts)->format_count = len; + p = fmts; + for (i = 0; i < len; i++, p++) + (*ffmts)->formats[i] = *p; + + return 0; +} + +static int moviesink_common_config_props(AVFilterLink *inlink, enum AVMediaType type) +{ + AVFilterContext *ctx = inlink->dst; + MovieSinkContext *movie = ctx->priv; + AVCodecContext *encctx; + AVStream *st = movie->oformat_ctx->streams[0]; + int ret; + + switch (type) { + case AVMEDIA_TYPE_VIDEO: + st->codec->pix_fmt = inlink->format; + st->codec->width = inlink->w; + st->codec->height = inlink->h; + st->codec->time_base = inlink->time_base; + break; + case AVMEDIA_TYPE_AUDIO: + st->codec->sample_fmt = inlink->format; + st->codec->sample_rate = inlink->sample_rate; + st->codec->time_base = inlink->time_base; + st->codec->channel_layout = inlink->channel_layout; + st->codec->channels = + av_get_channel_layout_nb_channels(inlink->channel_layout); + break; + } + encctx = avcodec_alloc_context3(movie->codec); + if (!encctx) { + av_log(ctx, AV_LOG_ERROR, "Error while allocating the codec context\n"); + return AVERROR(ENOMEM); + } + + if ((ret = avcodec_open2(st->codec, movie->codec, NULL)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error while opening the codec\n"); + return ret; + } + + av_dump_format(movie->oformat_ctx, 0, movie->file_name, 1); + + /* open the output file, if needed */ + if (!(movie->oformat->flags & AVFMT_NOFILE)) { + if ((ret = avio_open(&movie->oformat_ctx->pb, + movie->file_name, AVIO_FLAG_WRITE)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Could not open '%s'\n", movie->file_name); + return ret; + } + } + + if ((ret = avformat_write_header(movie->oformat_ctx, NULL)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Could not write header for '%s'\n", movie->file_name); + return ret; + } + + return 0; +} + +#if CONFIG_MOVIESINK_FILTER + +static av_cold int moviesink_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + return moviesink_common_init(ctx, args, opaque, AVMEDIA_TYPE_VIDEO); +} + +static int moviesink_query_formats(AVFilterContext *ctx) +{ + MovieSinkContext *movie = ctx->priv; + AVFilterFormats *ffmts; + int ret; + + if ((ret = make_filter_formats(&ffmts, movie->codec->pix_fmts))) + return ret; + + avfilter_set_common_pixel_formats(ctx, ffmts); + return 0; +} + +static int moviesink_config_props(AVFilterLink *inlink) +{ + return moviesink_common_config_props(inlink, AVMEDIA_TYPE_VIDEO); +} + +static void moviesink_start_frame(AVFilterLink *inlink, AVFilterBufferRef *bufref) { } + +static void moviesink_end_frame(AVFilterLink *inlink) +{ + MovieSinkContext *movie = inlink->dst->priv; + AVFilterBufferRef *picref = inlink->cur_buf; + AVFrame frame; + int ret, got_packet; + AVPacket packet; + + ret = avfilter_fill_frame_from_video_buffer_ref(&frame, picref); + if (ret < 0) { + av_log(inlink->dst, AV_LOG_ERROR, "Cannot convert picref to frame\n"); + return; + } + + av_init_packet(&packet); + packet.data = NULL; + packet.size = 0; + got_packet = 0; + ret = avcodec_encode_video2(movie->oformat_ctx->streams[0]->codec, + &packet, &frame, &got_packet); + if (ret < 0) { + av_log(inlink->dst, AV_LOG_ERROR, "Video encoding failed\n"); + return; + } + packet.stream_index = 0; + + av_interleaved_write_frame(movie->oformat_ctx, &packet); +} + +AVFilter avfilter_vsink_moviesink = { + .name = "moviesink", + .description = NULL_IF_CONFIG_SMALL("Output the video to a media file."), + .priv_size = sizeof(MovieSinkContext), + .query_formats = moviesink_query_formats, + .init = moviesink_init, + .uninit = moviesink_common_uninit, + + .inputs = (const AVFilterPad[]) { + { .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .start_frame = moviesink_start_frame, + .end_frame = moviesink_end_frame, + .config_props = moviesink_config_props, }, + { .name = NULL } + }, + .outputs = (const AVFilterPad[]) { + { .name = NULL } + }, +}; + +#endif /* CONFIG_MOVIESINK_FILTER */ + +#ifdef CONFIG_AMOVIESINK_FILTER + +static av_cold int amoviesink_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + return moviesink_common_init(ctx, args, opaque, AVMEDIA_TYPE_AUDIO); +} + +static int amoviesink_query_formats(AVFilterContext *ctx) +{ + MovieSinkContext *movie = ctx->priv; + AVFilterFormats *ffmts; + int ret; + + if ((ret = make_filter_formats(&ffmts, movie->codec->sample_fmts))) + return ret; + + avfilter_set_common_sample_formats(ctx, ffmts); + return 0; +} + +static int amoviesink_config_props(AVFilterLink *inlink) +{ + return moviesink_common_config_props(inlink, AVMEDIA_TYPE_AUDIO); +} + +static void amoviesink_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples) +{ + MovieSinkContext *movie = inlink->dst->priv; + AVFrame frame; + AVPacket packet; + int ret, got_packet; + + av_init_packet(&packet); + packet.data = NULL; + packet.size = 0; + got_packet = 0; + ret = avcodec_encode_audio2(movie->oformat_ctx->streams[0]->codec, + &packet, &frame, &got_packet); + if (ret < 0) { + av_log(inlink->dst, AV_LOG_ERROR, "Audio encoding failed\n"); + return; + } + packet.stream_index = 0; + + av_interleaved_write_frame(movie->oformat_ctx, &packet); +} + +AVFilter avfilter_asink_amoviesink = { + .name = "amoviesink", + .description = NULL_IF_CONFIG_SMALL("Output audio to a movie file."), + .priv_size = sizeof(MovieSinkContext), + .init = amoviesink_init, + .uninit = moviesink_common_uninit, + .query_formats = amoviesink_query_formats, + + .inputs = (const AVFilterPad[]) { + { .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = amoviesink_config_props, + .filter_samples = amoviesink_filter_samples, + }, + { .name = NULL } + }, + .outputs = (const AVFilterPad[]) { + { .name = NULL } + }, +}; + +#endif /* CONFIG_AMOVIESINK_FILTER */ -- 2.34.1 [-- Attachment #3: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next prev parent reply other threads:[~2024-06-12 19:52 UTC|newest] Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top 2024-06-06 11:51 Shiqi Zhu 2024-06-06 12:20 ` Zhao Zhili 2024-06-07 2:12 ` Hiccup Zhu 2024-06-07 2:32 ` Zhao Zhili 2024-06-07 3:33 ` Shiqi Zhu 2024-06-07 6:45 ` Rémi Denis-Courmont 2024-06-07 9:53 ` Michael Niedermayer 2024-06-07 10:46 ` Anton Khirnov 2024-06-07 11:54 ` Rémi Denis-Courmont 2024-06-11 13:13 ` Shiqi Zhu 2024-06-12 19:52 ` Stefano Sabatini [this message] 2024-06-12 20:14 ` Paul B Mahol 2024-06-13 1:49 ` Shiqi Zhu 2024-06-13 1:54 ` Shiqi Zhu
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=Zmn8i9PWFNVcOEeD@mariano \ --to=stefasab@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git