[FFmpeg-devel] [PATCH] ffmpeg: add an option to fix subtitles durations.

Nicolas George nicolas.george at normalesup.org
Mon Aug 6 14:40:39 CEST 2012


With this option, transcoding DVB subtitles becomes possible.

Signed-off-by: Nicolas George <nicolas.george at normalesup.org>
---
 doc/ffmpeg.texi |   19 +++++++++++++++++++
 ffmpeg.c        |   36 +++++++++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 1 deletion(-)


Updated according to Clément's comments.


diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
index 1ee42d8..8826a6b 100644
--- a/doc/ffmpeg.texi
+++ b/doc/ffmpeg.texi
@@ -620,6 +620,25 @@ Disable subtitle recording.
 Deprecated, see -bsf
 @end table
 
+ at section Advanced Subtitle options:
+
+ at table @option
+
+ at item -fix_sub_duration
+Fix subtitles durations. For each subtitle, wait for the next packet in the
+same stream and adjust the duration of the first to avoid overlap. This is
+necessary with some subtitles codecs, especially DVB subtitles, because the
+duration in the original packet is only a rough estimate and the end is
+actually marked by an empty subtitle frame. Failing to use this option when
+necessary can result in exaggerated durations or muxing failures due to
+non-monotonic timestamps.
+
+Note that this option will delay the output of all data until the next
+subtitle packet is decoded: it may increase memory consumption and latency a
+lot.
+
+ at end table
+
 @section Audio/Video grab options
 
 @table @option
diff --git a/ffmpeg.c b/ffmpeg.c
index 21516a6..6165c0e 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -249,6 +249,14 @@ typedef struct InputStream {
     int      resample_channels;
     uint64_t resample_channel_layout;
 
+    int fix_sub_duration;
+    struct { /* previous decoded subtitle and related variables */
+        int64_t pts;
+        int got_output;
+        int ret;
+        AVSubtitle subtitle;
+    } prev_sub;
+
     struct sub2video {
         int64_t last_pts;
         AVFilterBufferRef *ref;
@@ -459,6 +467,8 @@ typedef struct OptionsContext {
     int        nb_copy_initial_nonkeyframes;
     SpecifierOpt *filters;
     int        nb_filters;
+    SpecifierOpt *fix_sub_duration;
+    int        nb_fix_sub_duration;
 } OptionsContext;
 
 static void do_video_stats(AVFormatContext *os, OutputStream *ost, int frame_size);
@@ -2800,6 +2810,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
 static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
 {
     AVSubtitle subtitle;
+    int64_t pts = pkt->pts;
     int i, ret = avcodec_decode_subtitle2(ist->st->codec,
                                           &subtitle, got_output, pkt);
     if (ret < 0 || !*got_output) {
@@ -2808,6 +2819,26 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
         return ret;
     }
 
+    if (ist->fix_sub_duration) {
+        if (ist->prev_sub.got_output) {
+            int end = av_rescale_q(pts - ist->prev_sub.pts, ist->st->time_base,
+                                   (AVRational){ 1, 1000 });
+            if (end < ist->prev_sub.subtitle.end_display_time) {
+                av_log(ist->st->codec, AV_LOG_DEBUG,
+                       "Subtitle duration reduced from %d to %d\n",
+                       ist->prev_sub.subtitle.end_display_time, end);
+                ist->prev_sub.subtitle.end_display_time = end;
+            }
+        }
+        FFSWAP(int64_t,    pts,         ist->prev_sub.pts);
+        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
+        FFSWAP(int,        ret,         ist->prev_sub.ret);
+        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
+    }
+
+    if (!*got_output || !subtitle.num_rects)
+        return ret;
+
     rate_emu_sleep(ist);
 
     sub2video_update(ist, &subtitle, pkt->pts);
@@ -2818,7 +2849,7 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
         if (!check_output_constraints(ist, ost) || !ost->encoding_needed)
             continue;
 
-        do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pkt->pts);
+        do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pts);
     }
 
     avsubtitle_free(&subtitle);
@@ -4557,6 +4588,8 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
         case AVMEDIA_TYPE_SUBTITLE:
             if(!ist->dec)
                 ist->dec = avcodec_find_decoder(dec->codec_id);
+            MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st);
+
             break;
         case AVMEDIA_TYPE_ATTACHMENT:
         case AVMEDIA_TYPE_UNKNOWN:
@@ -6253,6 +6286,7 @@ static const OptionDef real_options[] = {
     { "sn", OPT_BOOL | OPT_SUBTITLE | OPT_OFFSET, {.off = OFFSET(subtitle_disable)}, "disable subtitle" },
     { "scodec", HAS_ARG | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_subtitle_codec}, "force subtitle codec ('copy' to copy stream)", "codec" },
     { "stag", HAS_ARG | OPT_EXPERT | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_old2new}, "force subtitle tag/fourcc", "fourcc/tag" },
+    { "fix_sub_duration", OPT_BOOL | OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC, {.off = OFFSET(fix_sub_duration)}, "fix subtitles duration" },
 
     /* grab options */
     { "vc", HAS_ARG | OPT_EXPERT | OPT_VIDEO | OPT_GRAB, {(void*)opt_video_channel}, "deprecated, use -channel", "channel" },
-- 
1.7.10.4



More information about the ffmpeg-devel mailing list