diff mbox series

[FFmpeg-devel,27/33] fftools/ffmpeg: rework initializing encoders with no frames

Message ID 20230713105553.21052-27-anton@khirnov.net
State New
Headers show
Series [FFmpeg-devel,01/33] fftools/ffmpeg_mux_init: return errors from of_open() instead of aborting | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Anton Khirnov July 13, 2023, 10:55 a.m. UTC
When no frames were passed from a filtergraph to an encoder, but the
filtergraph is configured (i.e. has output parameters), encoder flush
code will use those parameters to initialize the encoder in a last-ditch
effort to produce some useful output.

Rework this process so that it is triggered by the filtergraph, which
now sends a dummy frame with parameters, but no data, to the encoder,
rather than the encoder reaching backwards into the filter.

This approach is more in line with the natural data flow from filters to
encoders and will allow to reduce encoder-filter interactions in
following commits.

This code is tested by fate-adpcm-ima-cunning-trunc-t2-track1, which (as
confirmed by Zane) is supposed to produce empty output.
---
 fftools/ffmpeg_enc.c    | 22 ++---------------
 fftools/ffmpeg_filter.c | 54 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 52 insertions(+), 24 deletions(-)
diff mbox series

Patch

diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c
index b35dbb6290..390f1b3b57 100644
--- a/fftools/ffmpeg_enc.c
+++ b/fftools/ffmpeg_enc.c
@@ -1142,26 +1142,8 @@  void enc_flush(void)
         AVCodecContext *enc = ost->enc_ctx;
         OutputFile      *of = output_files[ost->file_index];
 
-        if (!enc)
-            continue;
-
-        // Try to enable encoding with no input frames.
-        // Maybe we should just let encoding fail instead.
-        if (!e->opened) {
-            FilterGraph *fg = ost->filter->graph;
-
-            av_log(ost, AV_LOG_WARNING,
-                   "Finishing stream without any data written to it.\n");
-
-            if (!fg->graph)
-                continue;
-
-            ret = enc_open(ost, NULL);
-            if (ret < 0)
-                exit_program(1);
-        }
-
-        if (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO)
+        if (!enc || !e->opened ||
+            (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO))
             continue;
 
         ret = submit_encode_frame(of, ost, NULL);
diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 67a5f48245..f8e64ce6cc 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -143,11 +143,17 @@  typedef struct OutputFilterPriv {
     int sample_rate;
     AVChannelLayout ch_layout;
 
+    AVRational time_base;
+    AVRational sample_aspect_ratio;
+
     // those are only set if no format is specified and the encoder gives us multiple options
     // They point directly to the relevant lists of the encoder.
     const int *formats;
     const AVChannelLayout *ch_layouts;
     const int *sample_rates;
+
+    // set to 1 after at least one frame passed through this output
+    int got_frame;
 } OutputFilterPriv;
 
 static OutputFilterPriv *ofp_from_ofilter(OutputFilter *ofilter)
@@ -1576,6 +1582,9 @@  static int configure_filtergraph(FilterGraph *fg)
         ofp->width  = av_buffersink_get_w(sink);
         ofp->height = av_buffersink_get_h(sink);
 
+        ofp->time_base           = av_buffersink_get_time_base(sink);
+        ofp->sample_aspect_ratio = av_buffersink_get_sample_aspect_ratio(sink);
+
         ofp->sample_rate    = av_buffersink_get_sample_rate(sink);
         av_channel_layout_uninit(&ofp->ch_layout);
         ret = av_buffersink_get_ch_layout(sink, &ofp->ch_layout);
@@ -1688,6 +1697,7 @@  int reap_filters(int flush)
 {
     /* Reap all buffers present in the buffer sinks */
     for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) {
+        OutputFilterPriv *ofp;
         FilterGraphPriv *fgp;
         AVFrame *filtered_frame;
         AVFilterContext *filter;
@@ -1697,6 +1707,7 @@  int reap_filters(int flush)
             continue;
         filter = ost->filter->filter;
         fgp    = fgp_from_fg(ost->filter->graph);
+        ofp    = ofp_from_ofilter(ost->filter);
 
         filtered_frame = fgp->frame;
 
@@ -1749,6 +1760,7 @@  int reap_filters(int flush)
 
             enc_frame(ost, filtered_frame);
             av_frame_unref(filtered_frame);
+            ofp->got_frame = 1;
         }
     }
 
@@ -1961,6 +1973,7 @@  int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_reference)
 
 int fg_transcode_step(FilterGraph *graph, InputStream **best_ist)
 {
+    FilterGraphPriv *fgp = fgp_from_fg(graph);
     int i, ret;
     int nb_requests, nb_requests_max = 0;
     InputStream *ist;
@@ -1988,10 +2001,43 @@  int fg_transcode_step(FilterGraph *graph, InputStream **best_ist)
         return reap_filters(0);
 
     if (ret == AVERROR_EOF) {
-        ret = reap_filters(1);
-        for (i = 0; i < graph->nb_outputs; i++)
-            close_output_stream(graph->outputs[i]->ost);
-        return ret;
+        reap_filters(1);
+        for (int i = 0; i < graph->nb_outputs; i++) {
+            OutputFilter *ofilter = graph->outputs[i];
+            OutputFilterPriv *ofp = ofp_from_ofilter(ofilter);
+
+            // we are finished and no frames were ever seen at this output,
+            // at least initialize the encoder with a dummy frame
+            if (!ofp->got_frame) {
+                AVFrame *frame = fgp->frame;
+
+                frame->time_base   = ofp->time_base;
+                frame->format      = ofp->format;
+
+                frame->width               = ofp->width;
+                frame->height              = ofp->height;
+                frame->sample_aspect_ratio = ofp->sample_aspect_ratio;
+
+                frame->sample_rate = ofp->sample_rate;
+                if (ofp->ch_layout.nb_channels) {
+                    ret = av_channel_layout_copy(&frame->ch_layout, &ofp->ch_layout);
+                    if (ret < 0)
+                        return ret;
+                }
+
+                av_assert0(!frame->buf[0]);
+
+                av_log(ofilter->ost, AV_LOG_WARNING,
+                       "No filtered frames for output stream, trying to "
+                       "initialize anyway.\n");
+
+                enc_open(ofilter->ost, frame);
+                av_frame_unref(frame);
+            }
+
+            close_output_stream(ofilter->ost);
+        }
+        return 0;
     }
     if (ret != AVERROR(EAGAIN))
         return ret;