Browse code

ffmpeg: add an option to fix subtitles durations.

With this option, transcoding DVB subtitles becomes possible.

Nicolas George authored on 2012/08/09 18:44:17
Showing 4 changed files
... ...
@@ -620,6 +620,25 @@ Disable subtitle recording.
620 620
 Deprecated, see -bsf
621 621
 @end table
622 622
 
623
+@section Advanced Subtitle options:
624
+
625
+@table @option
626
+
627
+@item -fix_sub_duration
628
+Fix subtitles durations. For each subtitle, wait for the next packet in the
629
+same stream and adjust the duration of the first to avoid overlap. This is
630
+necessary with some subtitles codecs, especially DVB subtitles, because the
631
+duration in the original packet is only a rough estimate and the end is
632
+actually marked by an empty subtitle frame. Failing to use this option when
633
+necessary can result in exaggerated durations or muxing failures due to
634
+non-monotonic timestamps.
635
+
636
+Note that this option will delay the output of all data until the next
637
+subtitle packet is decoded: it may increase memory consumption and latency a
638
+lot.
639
+
640
+@end table
641
+
623 642
 @section Audio/Video grab options
624 643
 
625 644
 @table @option
... ...
@@ -1649,6 +1649,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
1649 1649
 static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
1650 1650
 {
1651 1651
     AVSubtitle subtitle;
1652
+    int64_t pts = pkt->pts;
1652 1653
     int i, ret = avcodec_decode_subtitle2(ist->st->codec,
1653 1654
                                           &subtitle, got_output, pkt);
1654 1655
     if (ret < 0 || !*got_output) {
... ...
@@ -1657,6 +1658,26 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
1657 1657
         return ret;
1658 1658
     }
1659 1659
 
1660
+    if (ist->fix_sub_duration) {
1661
+        if (ist->prev_sub.got_output) {
1662
+            int end = av_rescale_q(pts - ist->prev_sub.pts, ist->st->time_base,
1663
+                                   (AVRational){ 1, 1000 });
1664
+            if (end < ist->prev_sub.subtitle.end_display_time) {
1665
+                av_log(ist->st->codec, AV_LOG_DEBUG,
1666
+                       "Subtitle duration reduced from %d to %d\n",
1667
+                       ist->prev_sub.subtitle.end_display_time, end);
1668
+                ist->prev_sub.subtitle.end_display_time = end;
1669
+            }
1670
+        }
1671
+        FFSWAP(int64_t,    pts,         ist->prev_sub.pts);
1672
+        FFSWAP(int,        *got_output, ist->prev_sub.got_output);
1673
+        FFSWAP(int,        ret,         ist->prev_sub.ret);
1674
+        FFSWAP(AVSubtitle, subtitle,    ist->prev_sub.subtitle);
1675
+    }
1676
+
1677
+    if (!*got_output || !subtitle.num_rects)
1678
+        return ret;
1679
+
1660 1680
     rate_emu_sleep(ist);
1661 1681
 
1662 1682
     sub2video_update(ist, &subtitle, pkt->pts);
... ...
@@ -1667,7 +1688,7 @@ static int transcode_subtitles(InputStream *ist, AVPacket *pkt, int *got_output)
1667 1667
         if (!check_output_constraints(ist, ost) || !ost->encoding_needed)
1668 1668
             continue;
1669 1669
 
1670
-        do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pkt->pts);
1670
+        do_subtitle_out(output_files[ost->file_index]->ctx, ost, ist, &subtitle, pts);
1671 1671
     }
1672 1672
 
1673 1673
     avsubtitle_free(&subtitle);
... ...
@@ -158,6 +158,8 @@ typedef struct OptionsContext {
158 158
     int        nb_copy_initial_nonkeyframes;
159 159
     SpecifierOpt *filters;
160 160
     int        nb_filters;
161
+    SpecifierOpt *fix_sub_duration;
162
+    int        nb_fix_sub_duration;
161 163
 } OptionsContext;
162 164
 
163 165
 typedef struct InputFilter {
... ...
@@ -223,6 +225,14 @@ typedef struct InputStream {
223 223
     int      resample_channels;
224 224
     uint64_t resample_channel_layout;
225 225
 
226
+    int fix_sub_duration;
227
+    struct { /* previous decoded subtitle and related variables */
228
+        int64_t pts;
229
+        int got_output;
230
+        int ret;
231
+        AVSubtitle subtitle;
232
+    } prev_sub;
233
+
226 234
     struct sub2video {
227 235
         int64_t last_pts;
228 236
         AVFilterBufferRef *ref;
... ...
@@ -593,6 +593,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic)
593 593
         case AVMEDIA_TYPE_SUBTITLE:
594 594
             if(!ist->dec)
595 595
                 ist->dec = avcodec_find_decoder(dec->codec_id);
596
+            MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st);
596 597
             break;
597 598
         case AVMEDIA_TYPE_ATTACHMENT:
598 599
         case AVMEDIA_TYPE_UNKNOWN:
... ...
@@ -2276,6 +2277,7 @@ const OptionDef options[] = {
2276 2276
     { "sn", OPT_BOOL | OPT_SUBTITLE | OPT_OFFSET, {.off = OFFSET(subtitle_disable)}, "disable subtitle" },
2277 2277
     { "scodec", HAS_ARG | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_subtitle_codec}, "force subtitle codec ('copy' to copy stream)", "codec" },
2278 2278
     { "stag", HAS_ARG | OPT_EXPERT | OPT_SUBTITLE | OPT_FUNC2, {(void*)opt_old2new}, "force subtitle tag/fourcc", "fourcc/tag" },
2279
+    { "fix_sub_duration", OPT_BOOL | OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC, {.off = OFFSET(fix_sub_duration)}, "fix subtitles duration" },
2279 2280
 
2280 2281
     /* grab options */
2281 2282
     { "vc", HAS_ARG | OPT_EXPERT | OPT_VIDEO | OPT_GRAB, {(void*)opt_video_channel}, "deprecated, use -channel", "channel" },