[FFmpeg-devel] [PATCH] ffmpeg: add -drop_deviant_frames option

Gyan ffmpeg at gyani.pro
Wed Mar 27 14:35:32 EET 2019


Essential in avoiding loss of output streams (audio) for me, when input 
has unreliable transport.
Tested with software decoding (h264, aac, mp1, mp2) pipeline.

Gyan
-------------- next part --------------
From ca04af063ee4a9406b6f09e12126827728aff875 Mon Sep 17 00:00:00 2001
From: Gyan Doshi <ffmpeg at gyani.pro>
Date: Mon, 25 Mar 2019 22:01:07 +0530
Subject: [PATCH] ffmpeg: add -drop_deviant_frames option

Decoded frames with changed parameters in inputs expected to be uniform
may indicate malformed/corrupted packets. Demuxer with flag discardcorrupt
set may not catch all such packets and neither may decoders with err_detect
flags set.

Upon parameter change, AVFilterLink of any filters receiving such frames has
to be reconfigured, destroying existing filter context. In case of a
change in audio sample rate, timestamps can go haywire, potentially
leaving the output stream to be "truncated".

Added option allows users to avoid sending such deviant frames onwards.
Default behaviour remains unchanged.
---
 doc/ffmpeg.texi      |  4 ++++
 fftools/ffmpeg.c     | 47 ++++++++++++++++++++++++++++++++++++++++++--
 fftools/ffmpeg.h     |  3 +++
 fftools/ffmpeg_opt.c |  5 +++++
 4 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index cd35eb49c8..338f98ecdf 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -1664,6 +1664,10 @@ Discard all frames excepts keyframes.
 Discard all frames.
 @end table
 
+ at item -drop_deviant_frames (@emph{input,per-stream})
+Allows discarding decoded frames whose parameters differ from initialized
+stream parameters. May be useful in  
+
 @item -abort_on @var{flags} (@emph{global})
 Stop and abort on various conditions. The following flags are available:
 
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index 544f1a1cef..5da75306e3 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -2283,7 +2283,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output,
 {
     AVFrame *decoded_frame;
     AVCodecContext *avctx = ist->dec_ctx;
-    int ret, err = 0;
+    int ret, err = 0, deviant = 0;
     AVRational decoded_frame_tb;
 
     if (!ist->decoded_frame && !(ist->decoded_frame = av_frame_alloc()))
@@ -2303,12 +2303,36 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output,
         ret = AVERROR_INVALIDDATA;
     }
 
+    if (*got_output && ret >= 0 && ist->frames_decoded && ist->nb_filters &&
+        (ist->filters[0]->sample_rate    != decoded_frame->sample_rate ||
+         ist->filters[0]->channels       != decoded_frame->channels ||
+         ist->filters[0]->channel_layout != decoded_frame->channel_layout ||
+         ist->filters[0]->format         != decoded_frame->format ||
+         ist->filters[0]->sample_rate    != avctx->sample_rate)) {
+        av_log(NULL, ist->drop_deviant_frames ? AV_LOG_INFO : AV_LOG_VERBOSE,
+               "Parameter change in decoded audio frame from stream %d.%d pts %s \n"
+               "filter rate:%d fmt:%s ch:%d layout:%"PRIX64" != frame rate:%d fmt:%s ch:%d layout:%"PRIX64", decoder context rate: %d \n",
+               ist->file_index, ist->st->index, av_ts2timestr(decoded_frame->pts, &ist->st->time_base),
+               ist->filters[0]->sample_rate, av_get_sample_fmt_name(ist->filters[0]->format),
+               ist->filters[0]->channels, ist->filters[0]->channel_layout,
+               decoded_frame->sample_rate, av_get_sample_fmt_name(decoded_frame->format),
+               decoded_frame->channels, decoded_frame->channel_layout,
+               avctx->sample_rate);
+        if (ist->drop_deviant_frames) {
+            deviant = 1;
+            av_log(NULL, AV_LOG_WARNING, "Dropping frame.\n");
+        }
+    }
+
     if (ret != AVERROR_EOF)
         check_decode_result(ist, got_output, ret);
 
     if (!*got_output || ret < 0)
         return ret;
 
+    if (deviant)
+        goto fail;
+
     ist->samples_decoded += decoded_frame->nb_samples;
     ist->frames_decoded++;
 
@@ -2335,6 +2359,7 @@ static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output,
     ist->nb_samples = decoded_frame->nb_samples;
     err = send_frame_to_filters(ist, decoded_frame);
 
+fail:
     av_frame_unref(ist->filter_frame);
     av_frame_unref(decoded_frame);
     return err < 0 ? err : ret;
@@ -2344,7 +2369,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_
                         int *decode_failed)
 {
     AVFrame *decoded_frame;
-    int i, ret = 0, err = 0;
+    int i, ret = 0, err = 0, deviant = 0;
     int64_t best_effort_timestamp;
     int64_t dts = AV_NOPTS_VALUE;
     AVPacket avpkt;
@@ -2413,11 +2438,29 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_
                 ist->dec_ctx->height,
                 ist->dec_ctx->pix_fmt);
         }
+        if (ist->nb_filters && ist->frames_decoded &&
+            (ist->filters[0]->width    != decoded_frame->width ||
+             ist->filters[0]->height   != decoded_frame->height ||
+             ist->filters[0]->format   != decoded_frame->format)) {
+            av_log(NULL, ist->drop_deviant_frames ? AV_LOG_INFO : AV_LOG_VERBOSE,
+               "Parameter change in decoded video frame from stream %d.%d pts %s \n"
+               "filter width:%d height:%d fmt:%s != frame width:%d height:%d fmt:%s \n",
+               ist->file_index, ist->st->index, av_ts2timestr(decoded_frame->pts, &ist->st->time_base),
+               ist->filters[0]->width, ist->filters[0]->height, av_get_pix_fmt_name(ist->filters[0]->format),
+               decoded_frame->width, decoded_frame->height, av_get_pix_fmt_name(decoded_frame->format));
+            if (ist->drop_deviant_frames) {
+                deviant = 1;
+                av_log(NULL, AV_LOG_WARNING, "Dropping frame.\n");
+            }
+        }
     }
 
     if (!*got_output || ret < 0)
         return ret;
 
+    if (deviant)
+        goto fail;
+
     if(ist->top_field_first>=0)
         decoded_frame->top_field_first = ist->top_field_first;
 
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index eb1eaf6363..b0a71ff8c7 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -133,6 +133,8 @@ typedef struct OptionsContext {
     int        nb_hwaccel_output_formats;
     SpecifierOpt *autorotate;
     int        nb_autorotate;
+    SpecifierOpt *drop_deviant_frames;
+    int        nb_drop_deviant_frames;
 
     /* output options */
     StreamMap *stream_maps;
@@ -388,6 +390,7 @@ typedef struct InputStream {
     int nb_dts_buffer;
 
     int got_output;
+    int drop_deviant_frames;    /* drop decoded frames whose parameters differ from first filter's inlink parameters */
 } InputStream;
 
 typedef struct InputFile {
diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c
index 53d688b764..932a995439 100644
--- a/fftools/ffmpeg_opt.c
+++ b/fftools/ffmpeg_opt.c
@@ -773,6 +773,9 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
             exit_program(1);
         }
 
+        ist->drop_deviant_frames = 0;
+        MATCH_PER_STREAM_OPT(drop_deviant_frames, i, ist->drop_deviant_frames, ic ,st);
+
         ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE;
 
         ist->dec_ctx = avcodec_alloc_context3(ist->dec);
@@ -3532,6 +3535,8 @@ const OptionDef options[] = {
     { "discard",        OPT_STRING | HAS_ARG | OPT_SPEC |
                         OPT_INPUT,                                   { .off = OFFSET(discard) },
         "discard", "" },
+    { "drop_deviant_frames",    OPT_BOOL | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_INPUT,   { .off = OFFSET(drop_deviant_frames) },
+        "discard decoded frames whose parameters differ from initial stream parameters" },
     { "disposition",    OPT_STRING | HAS_ARG | OPT_SPEC |
                         OPT_OUTPUT,                                  { .off = OFFSET(disposition) },
         "disposition", "" },
-- 
2.19.2


More information about the ffmpeg-devel mailing list