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