With this option, transcoding DVB subtitles becomes possible.
Nicolas George authored on 2012/08/09 18:44:17... | ... |
@@ -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" }, |