Some demuxers set a timecode in the format or streams metadata. The
muxers now make use of this metadata instead of a duplicated private
option.
This makes possible transparent copy of the timecode when transmuxing
and transcoding.
-timecode option for MPEG1/2 codec is also renamed to -gop_timecode. The
global ffmpeg -timecode option will set it anyway so no option change
visible for the user.
| ... | ... |
@@ -5618,6 +5618,16 @@ static int opt_deinterlace(const char *opt, const char *arg) |
| 5618 | 5618 |
return 0; |
| 5619 | 5619 |
} |
| 5620 | 5620 |
|
| 5621 |
+static int opt_timecode(OptionsContext *o, const char *opt, const char *arg) |
|
| 5622 |
+{
|
|
| 5623 |
+ char *tcr = av_asprintf("timecode=%s", arg);
|
|
| 5624 |
+ int ret = parse_option(o, "metadata:g", tcr, options); |
|
| 5625 |
+ if (ret >= 0) |
|
| 5626 |
+ ret = opt_default("gop_timecode", arg);
|
|
| 5627 |
+ av_free(tcr); |
|
| 5628 |
+ return ret; |
|
| 5629 |
+} |
|
| 5630 |
+ |
|
| 5621 | 5631 |
static void parse_cpuflags(int argc, char **argv, const OptionDef *options) |
| 5622 | 5632 |
{
|
| 5623 | 5633 |
int idx = locate_option(argc, argv, options, "cpuflags"); |
| ... | ... |
@@ -5752,6 +5762,7 @@ static const OptionDef options[] = {
|
| 5752 | 5752 |
{ "sameq", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant}, "use same quantizer as source (implies VBR)" },
|
| 5753 | 5753 |
{ "same_quant", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant},
|
| 5754 | 5754 |
"use same quantizer as source (implies VBR)" }, |
| 5755 |
+ { "timecode", HAS_ARG | OPT_VIDEO | OPT_FUNC2, {(void*)opt_timecode}, "set initial TimeCode value.", "hh:mm:ss[:;.]ff" },
|
|
| 5755 | 5756 |
{ "pass", HAS_ARG | OPT_VIDEO, {(void*)opt_pass}, "select the pass number (1 or 2)", "n" },
|
| 5756 | 5757 |
{ "passlogfile", HAS_ARG | OPT_VIDEO, {(void*)&opt_passlogfile}, "select two pass log file name prefix", "prefix" },
|
| 5757 | 5758 |
{ "deinterlace", OPT_EXPERT | OPT_VIDEO, {(void*)opt_deinterlace},
|
| ... | ... |
@@ -931,8 +931,7 @@ static void mpeg1_encode_block(MpegEncContext *s, |
| 931 | 931 |
#define OFFSET(x) offsetof(MpegEncContext, x) |
| 932 | 932 |
#define VE AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM |
| 933 | 933 |
#define COMMON_OPTS\ |
| 934 |
- {AV_TIMECODE_OPTION(MpegEncContext, tc_opt_str, \
|
|
| 935 |
- AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)},\ |
|
| 934 |
+ { "gop_timecode", "MPEG GOP Timecode in hh:mm:ss[:;.]ff format", OFFSET(tc_opt_str), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, VE },\
|
|
| 936 | 935 |
{ "intra_vlc", "Use MPEG-2 intra VLC table.", OFFSET(intra_vlc_format), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE },\
|
| 937 | 936 |
{ "drop_frame_timecode", "Timecode is in drop frame format.", OFFSET(drop_frame_timecode), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE}, \
|
| 938 | 937 |
{ "scan_offset", "Reserve space for SVCD scan offset user data.", OFFSET(scan_offset), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE },
|
| ... | ... |
@@ -52,7 +52,6 @@ struct DVMuxContext {
|
| 52 | 52 |
int has_audio; /* frame under contruction has audio */ |
| 53 | 53 |
int has_video; /* frame under contruction has video */ |
| 54 | 54 |
uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under contruction */ |
| 55 |
- char *tc_opt_str; /* timecode option string */ |
|
| 56 | 55 |
AVTimecode tc; /* timecode context */ |
| 57 | 56 |
}; |
| 58 | 57 |
|
| ... | ... |
@@ -356,6 +355,7 @@ static int dv_write_header(AVFormatContext *s) |
| 356 | 356 |
{
|
| 357 | 357 |
AVRational rate; |
| 358 | 358 |
DVMuxContext *dvc = s->priv_data; |
| 359 |
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); |
|
| 359 | 360 |
|
| 360 | 361 |
if (!dv_init_mux(s)) {
|
| 361 | 362 |
av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n" |
| ... | ... |
@@ -366,9 +366,16 @@ static int dv_write_header(AVFormatContext *s) |
| 366 | 366 |
} |
| 367 | 367 |
rate.num = dvc->sys->ltc_divisor; |
| 368 | 368 |
rate.den = 1; |
| 369 |
- if (dvc->tc_opt_str) |
|
| 370 |
- return av_timecode_init_from_string(&dvc->tc, rate, |
|
| 371 |
- dvc->tc_opt_str, s); |
|
| 369 |
+ if (!tcr) { // no global timecode, look into the streams
|
|
| 370 |
+ int i; |
|
| 371 |
+ for (i = 0; i < s->nb_streams; i++) {
|
|
| 372 |
+ tcr = av_dict_get(s->streams[i]->metadata, "timecode", NULL, 0); |
|
| 373 |
+ if (tcr) |
|
| 374 |
+ break; |
|
| 375 |
+ } |
|
| 376 |
+ } |
|
| 377 |
+ if (tcr) |
|
| 378 |
+ return av_timecode_init_from_string(&dvc->tc, rate, tcr->value, s); |
|
| 372 | 379 |
return av_timecode_init(&dvc->tc, rate, 0, 0, s); |
| 373 | 380 |
} |
| 374 | 381 |
|
| ... | ... |
@@ -398,16 +405,6 @@ static int dv_write_trailer(struct AVFormatContext *s) |
| 398 | 398 |
return 0; |
| 399 | 399 |
} |
| 400 | 400 |
|
| 401 |
-static const AVClass class = {
|
|
| 402 |
- .class_name = "dv", |
|
| 403 |
- .item_name = av_default_item_name, |
|
| 404 |
- .version = LIBAVUTIL_VERSION_INT, |
|
| 405 |
- .option = (const AVOption[]){
|
|
| 406 |
- {AV_TIMECODE_OPTION(DVMuxContext, tc_opt_str, AV_OPT_FLAG_ENCODING_PARAM)},
|
|
| 407 |
- {NULL},
|
|
| 408 |
- }, |
|
| 409 |
-}; |
|
| 410 |
- |
|
| 411 | 401 |
AVOutputFormat ff_dv_muxer = {
|
| 412 | 402 |
.name = "dv", |
| 413 | 403 |
.long_name = NULL_IF_CONFIG_SMALL("DV video format"),
|
| ... | ... |
@@ -418,5 +415,4 @@ AVOutputFormat ff_dv_muxer = {
|
| 418 | 418 |
.write_header = dv_write_header, |
| 419 | 419 |
.write_packet = dv_write_packet, |
| 420 | 420 |
.write_trailer = dv_write_trailer, |
| 421 |
- .priv_class = &class, |
|
| 422 | 421 |
}; |
| ... | ... |
@@ -41,7 +41,6 @@ typedef struct GXFTimecode{
|
| 41 | 41 |
int ff; |
| 42 | 42 |
int color; |
| 43 | 43 |
int drop; |
| 44 |
- char *str; |
|
| 45 | 44 |
} GXFTimecode; |
| 46 | 45 |
|
| 47 | 46 |
typedef struct GXFStreamContext {
|
| ... | ... |
@@ -655,11 +654,11 @@ static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc) |
| 655 | 655 |
sc->fields = vsc->fields; |
| 656 | 656 |
} |
| 657 | 657 |
|
| 658 |
-static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, int fields) |
|
| 658 |
+static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, const char *tcstr, int fields) |
|
| 659 | 659 |
{
|
| 660 | 660 |
char c; |
| 661 | 661 |
|
| 662 |
- if (sscanf(tc->str, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) {
|
|
| 662 |
+ if (sscanf(tcstr, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) {
|
|
| 663 | 663 |
av_log(s, AV_LOG_ERROR, "unable to parse timecode, " |
| 664 | 664 |
"syntax: hh:mm:ss[:;.]ff\n"); |
| 665 | 665 |
return -1; |
| ... | ... |
@@ -681,6 +680,7 @@ static int gxf_write_header(AVFormatContext *s) |
| 681 | 681 |
GXFStreamContext *vsc = NULL; |
| 682 | 682 |
uint8_t tracks[255] = {0};
|
| 683 | 683 |
int i, media_info = 0; |
| 684 |
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); |
|
| 684 | 685 |
|
| 685 | 686 |
if (!pb->seekable) {
|
| 686 | 687 |
av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome"); |
| ... | ... |
@@ -741,6 +741,8 @@ static int gxf_write_header(AVFormatContext *s) |
| 741 | 741 |
"gxf muxer only accepts PAL or NTSC resolutions currently\n"); |
| 742 | 742 |
return -1; |
| 743 | 743 |
} |
| 744 |
+ if (!tcr) |
|
| 745 |
+ tcr = av_dict_get(st->metadata, "timecode", NULL, 0); |
|
| 744 | 746 |
avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den); |
| 745 | 747 |
if (gxf_find_lines_index(st) < 0) |
| 746 | 748 |
sc->lines_index = -1; |
| ... | ... |
@@ -792,9 +794,8 @@ static int gxf_write_header(AVFormatContext *s) |
| 792 | 792 |
if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0)
|
| 793 | 793 |
return -1; |
| 794 | 794 |
|
| 795 |
- if (gxf->tc.str) {
|
|
| 796 |
- gxf_init_timecode(s, &gxf->tc, vsc->fields); |
|
| 797 |
- } |
|
| 795 |
+ if (tcr) |
|
| 796 |
+ gxf_init_timecode(s, &gxf->tc, tcr->value, vsc->fields); |
|
| 798 | 797 |
|
| 799 | 798 |
gxf_init_timecode_track(&gxf->timecode_track, vsc); |
| 800 | 799 |
gxf->flags |= 0x200000; // time code track is non-drop frame |
| ... | ... |
@@ -983,18 +984,6 @@ static int gxf_interleave_packet(AVFormatContext *s, AVPacket *out, AVPacket *pk |
| 983 | 983 |
ff_interleave_packet_per_dts, gxf_compare_field_nb); |
| 984 | 984 |
} |
| 985 | 985 |
|
| 986 |
-static const AVOption options[] = {
|
|
| 987 |
- { AV_TIMECODE_OPTION(GXFContext, tc.str, AV_OPT_FLAG_ENCODING_PARAM) },
|
|
| 988 |
- { NULL }
|
|
| 989 |
-}; |
|
| 990 |
- |
|
| 991 |
-static const AVClass gxf_muxer_class = {
|
|
| 992 |
- .class_name = "GXF muxer", |
|
| 993 |
- .item_name = av_default_item_name, |
|
| 994 |
- .option = options, |
|
| 995 |
- .version = LIBAVUTIL_VERSION_INT, |
|
| 996 |
-}; |
|
| 997 |
- |
|
| 998 | 986 |
AVOutputFormat ff_gxf_muxer = {
|
| 999 | 987 |
.name = "gxf", |
| 1000 | 988 |
.long_name = NULL_IF_CONFIG_SMALL("GXF format"),
|
| ... | ... |
@@ -1006,5 +995,4 @@ AVOutputFormat ff_gxf_muxer = {
|
| 1006 | 1006 |
.write_packet = gxf_write_packet, |
| 1007 | 1007 |
.write_trailer = gxf_write_trailer, |
| 1008 | 1008 |
.interleave_packet = gxf_interleave_packet, |
| 1009 |
- .priv_class = &gxf_muxer_class, |
|
| 1010 | 1009 |
}; |
| ... | ... |
@@ -189,7 +189,6 @@ typedef struct MXFContext {
|
| 189 | 189 |
unsigned body_partitions_count; |
| 190 | 190 |
int last_key_index; ///< index of last key frame |
| 191 | 191 |
uint64_t duration; |
| 192 |
- char *tc_opt_str; ///< timecode option string |
|
| 193 | 192 |
AVTimecode tc; ///< timecode context |
| 194 | 193 |
AVStream *timecode_track; |
| 195 | 194 |
int timecode_base; ///< rounded time code base (25 or 30) |
| ... | ... |
@@ -1403,6 +1402,7 @@ static int mxf_write_header(AVFormatContext *s) |
| 1403 | 1403 |
const int *samples_per_frame = NULL; |
| 1404 | 1404 |
AVDictionaryEntry *t; |
| 1405 | 1405 |
int64_t timestamp = 0; |
| 1406 |
+ AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); |
|
| 1406 | 1407 |
|
| 1407 | 1408 |
if (!s->nb_streams) |
| 1408 | 1409 |
return -1; |
| ... | ... |
@@ -1443,9 +1443,10 @@ static int mxf_write_header(AVFormatContext *s) |
| 1443 | 1443 |
} |
| 1444 | 1444 |
rate = (AVRational){mxf->time_base.den, mxf->time_base.num};
|
| 1445 | 1445 |
avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den); |
| 1446 |
- if (mxf->tc_opt_str) |
|
| 1447 |
- ret = av_timecode_init_from_string(&mxf->tc, rate, |
|
| 1448 |
- mxf->tc_opt_str, s); |
|
| 1446 |
+ if (!tcr) |
|
| 1447 |
+ tcr = av_dict_get(st->metadata, "timecode", NULL, 0); |
|
| 1448 |
+ if (tcr) |
|
| 1449 |
+ ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s); |
|
| 1449 | 1450 |
else |
| 1450 | 1451 |
ret = av_timecode_init(&mxf->tc, rate, 0, 0, s); |
| 1451 | 1452 |
if (ret < 0) |
| ... | ... |
@@ -1877,26 +1878,6 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int |
| 1877 | 1877 |
mxf_interleave_get_packet, mxf_compare_timestamps); |
| 1878 | 1878 |
} |
| 1879 | 1879 |
|
| 1880 |
-static const AVClass mxf_class = {
|
|
| 1881 |
- .class_name = "mxf", |
|
| 1882 |
- .item_name = av_default_item_name, |
|
| 1883 |
- .version = LIBAVUTIL_VERSION_INT, |
|
| 1884 |
- .option = (const AVOption[]){
|
|
| 1885 |
- {AV_TIMECODE_OPTION(MXFContext, tc_opt_str, AV_OPT_FLAG_ENCODING_PARAM)},
|
|
| 1886 |
- {NULL}
|
|
| 1887 |
- }, |
|
| 1888 |
-}; |
|
| 1889 |
- |
|
| 1890 |
-static const AVClass mxf_d10_class = {
|
|
| 1891 |
- .class_name = "mxf_d10", |
|
| 1892 |
- .item_name = av_default_item_name, |
|
| 1893 |
- .version = LIBAVUTIL_VERSION_INT, |
|
| 1894 |
- .option = (const AVOption[]){
|
|
| 1895 |
- {AV_TIMECODE_OPTION(MXFContext, tc_opt_str, AV_OPT_FLAG_ENCODING_PARAM)},
|
|
| 1896 |
- {NULL}
|
|
| 1897 |
- }, |
|
| 1898 |
-}; |
|
| 1899 |
- |
|
| 1900 | 1880 |
AVOutputFormat ff_mxf_muxer = {
|
| 1901 | 1881 |
.name = "mxf", |
| 1902 | 1882 |
.long_name = NULL_IF_CONFIG_SMALL("Material eXchange Format"),
|
| ... | ... |
@@ -1910,7 +1891,6 @@ AVOutputFormat ff_mxf_muxer = {
|
| 1910 | 1910 |
.write_trailer = mxf_write_footer, |
| 1911 | 1911 |
.flags = AVFMT_NOTIMESTAMPS, |
| 1912 | 1912 |
.interleave_packet = mxf_interleave, |
| 1913 |
- .priv_class = &mxf_class, |
|
| 1914 | 1913 |
}; |
| 1915 | 1914 |
|
| 1916 | 1915 |
AVOutputFormat ff_mxf_d10_muxer = {
|
| ... | ... |
@@ -1925,5 +1905,4 @@ AVOutputFormat ff_mxf_d10_muxer = {
|
| 1925 | 1925 |
.write_trailer = mxf_write_footer, |
| 1926 | 1926 |
.flags = AVFMT_NOTIMESTAMPS, |
| 1927 | 1927 |
.interleave_packet = mxf_interleave, |
| 1928 |
- .priv_class = &mxf_d10_class, |
|
| 1929 | 1928 |
}; |
| ... | ... |
@@ -32,12 +32,6 @@ |
| 32 | 32 |
|
| 33 | 33 |
#define AV_TIMECODE_STR_SIZE 16 |
| 34 | 34 |
|
| 35 |
-#define AV_TIMECODE_OPTION(ctx, string_field, flags) \ |
|
| 36 |
- "timecode", "set timecode value following hh:mm:ss[:;.]ff format, " \ |
|
| 37 |
- "use ';' or '.' before frame number for drop frame", \ |
|
| 38 |
- offsetof(ctx, string_field), \ |
|
| 39 |
- AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, flags
|
|
| 40 |
- |
|
| 41 | 35 |
enum AVTimecodeFlag {
|
| 42 | 36 |
AV_TIMECODE_FLAG_DROPFRAME = 1<<0, ///< timecode is drop frame |
| 43 | 37 |
AV_TIMECODE_FLAG_24HOURSMAX = 1<<1, ///< timecode wraps after 24 hours |