mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-26 19:01:44 +02:00
timecode: move timecode muxer options to metadata.
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.
This commit is contained in:
parent
0f0f3bd1e0
commit
6b35f1a2a6
11
ffmpeg.c
11
ffmpeg.c
@ -5618,6 +5618,16 @@ static int opt_deinterlace(const char *opt, const char *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int opt_timecode(OptionsContext *o, const char *opt, const char *arg)
|
||||||
|
{
|
||||||
|
char *tcr = av_asprintf("timecode=%s", arg);
|
||||||
|
int ret = parse_option(o, "metadata:g", tcr, options);
|
||||||
|
if (ret >= 0)
|
||||||
|
ret = opt_default("gop_timecode", arg);
|
||||||
|
av_free(tcr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_cpuflags(int argc, char **argv, const OptionDef *options)
|
static void parse_cpuflags(int argc, char **argv, const OptionDef *options)
|
||||||
{
|
{
|
||||||
int idx = locate_option(argc, argv, options, "cpuflags");
|
int idx = locate_option(argc, argv, options, "cpuflags");
|
||||||
@ -5752,6 +5762,7 @@ static const OptionDef options[] = {
|
|||||||
{ "sameq", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant}, "use same quantizer as source (implies VBR)" },
|
{ "sameq", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant}, "use same quantizer as source (implies VBR)" },
|
||||||
{ "same_quant", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant},
|
{ "same_quant", OPT_BOOL | OPT_VIDEO, {(void*)&same_quant},
|
||||||
"use same quantizer as source (implies VBR)" },
|
"use same quantizer as source (implies VBR)" },
|
||||||
|
{ "timecode", HAS_ARG | OPT_VIDEO | OPT_FUNC2, {(void*)opt_timecode}, "set initial TimeCode value.", "hh:mm:ss[:;.]ff" },
|
||||||
{ "pass", HAS_ARG | OPT_VIDEO, {(void*)opt_pass}, "select the pass number (1 or 2)", "n" },
|
{ "pass", HAS_ARG | OPT_VIDEO, {(void*)opt_pass}, "select the pass number (1 or 2)", "n" },
|
||||||
{ "passlogfile", HAS_ARG | OPT_VIDEO, {(void*)&opt_passlogfile}, "select two pass log file name prefix", "prefix" },
|
{ "passlogfile", HAS_ARG | OPT_VIDEO, {(void*)&opt_passlogfile}, "select two pass log file name prefix", "prefix" },
|
||||||
{ "deinterlace", OPT_EXPERT | OPT_VIDEO, {(void*)opt_deinterlace},
|
{ "deinterlace", OPT_EXPERT | OPT_VIDEO, {(void*)opt_deinterlace},
|
||||||
|
@ -931,8 +931,7 @@ static void mpeg1_encode_block(MpegEncContext *s,
|
|||||||
#define OFFSET(x) offsetof(MpegEncContext, x)
|
#define OFFSET(x) offsetof(MpegEncContext, x)
|
||||||
#define VE AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
|
#define VE AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM
|
||||||
#define COMMON_OPTS\
|
#define COMMON_OPTS\
|
||||||
{AV_TIMECODE_OPTION(MpegEncContext, tc_opt_str, \
|
{ "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 },\
|
||||||
AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)},\
|
|
||||||
{ "intra_vlc", "Use MPEG-2 intra VLC table.", OFFSET(intra_vlc_format), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE },\
|
{ "intra_vlc", "Use MPEG-2 intra VLC table.", OFFSET(intra_vlc_format), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE },\
|
||||||
{ "drop_frame_timecode", "Timecode is in drop frame format.", OFFSET(drop_frame_timecode), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE}, \
|
{ "drop_frame_timecode", "Timecode is in drop frame format.", OFFSET(drop_frame_timecode), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE}, \
|
||||||
{ "scan_offset", "Reserve space for SVCD scan offset user data.", OFFSET(scan_offset), AV_OPT_TYPE_INT, { 0 }, 0, 1, VE },
|
{ "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 {
|
|||||||
int has_audio; /* frame under contruction has audio */
|
int has_audio; /* frame under contruction has audio */
|
||||||
int has_video; /* frame under contruction has video */
|
int has_video; /* frame under contruction has video */
|
||||||
uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under contruction */
|
uint8_t frame_buf[DV_MAX_FRAME_SIZE]; /* frame under contruction */
|
||||||
char *tc_opt_str; /* timecode option string */
|
|
||||||
AVTimecode tc; /* timecode context */
|
AVTimecode tc; /* timecode context */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -356,6 +355,7 @@ static int dv_write_header(AVFormatContext *s)
|
|||||||
{
|
{
|
||||||
AVRational rate;
|
AVRational rate;
|
||||||
DVMuxContext *dvc = s->priv_data;
|
DVMuxContext *dvc = s->priv_data;
|
||||||
|
AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
|
||||||
|
|
||||||
if (!dv_init_mux(s)) {
|
if (!dv_init_mux(s)) {
|
||||||
av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n"
|
av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n"
|
||||||
@ -366,9 +366,16 @@ static int dv_write_header(AVFormatContext *s)
|
|||||||
}
|
}
|
||||||
rate.num = dvc->sys->ltc_divisor;
|
rate.num = dvc->sys->ltc_divisor;
|
||||||
rate.den = 1;
|
rate.den = 1;
|
||||||
if (dvc->tc_opt_str)
|
if (!tcr) { // no global timecode, look into the streams
|
||||||
return av_timecode_init_from_string(&dvc->tc, rate,
|
int i;
|
||||||
dvc->tc_opt_str, s);
|
for (i = 0; i < s->nb_streams; i++) {
|
||||||
|
tcr = av_dict_get(s->streams[i]->metadata, "timecode", NULL, 0);
|
||||||
|
if (tcr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tcr)
|
||||||
|
return av_timecode_init_from_string(&dvc->tc, rate, tcr->value, s);
|
||||||
return av_timecode_init(&dvc->tc, rate, 0, 0, s);
|
return av_timecode_init(&dvc->tc, rate, 0, 0, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,16 +405,6 @@ static int dv_write_trailer(struct AVFormatContext *s)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const AVClass class = {
|
|
||||||
.class_name = "dv",
|
|
||||||
.item_name = av_default_item_name,
|
|
||||||
.version = LIBAVUTIL_VERSION_INT,
|
|
||||||
.option = (const AVOption[]){
|
|
||||||
{AV_TIMECODE_OPTION(DVMuxContext, tc_opt_str, AV_OPT_FLAG_ENCODING_PARAM)},
|
|
||||||
{NULL},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
AVOutputFormat ff_dv_muxer = {
|
AVOutputFormat ff_dv_muxer = {
|
||||||
.name = "dv",
|
.name = "dv",
|
||||||
.long_name = NULL_IF_CONFIG_SMALL("DV video format"),
|
.long_name = NULL_IF_CONFIG_SMALL("DV video format"),
|
||||||
@ -418,5 +415,4 @@ AVOutputFormat ff_dv_muxer = {
|
|||||||
.write_header = dv_write_header,
|
.write_header = dv_write_header,
|
||||||
.write_packet = dv_write_packet,
|
.write_packet = dv_write_packet,
|
||||||
.write_trailer = dv_write_trailer,
|
.write_trailer = dv_write_trailer,
|
||||||
.priv_class = &class,
|
|
||||||
};
|
};
|
||||||
|
@ -41,7 +41,6 @@ typedef struct GXFTimecode{
|
|||||||
int ff;
|
int ff;
|
||||||
int color;
|
int color;
|
||||||
int drop;
|
int drop;
|
||||||
char *str;
|
|
||||||
} GXFTimecode;
|
} GXFTimecode;
|
||||||
|
|
||||||
typedef struct GXFStreamContext {
|
typedef struct GXFStreamContext {
|
||||||
@ -655,11 +654,11 @@ static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc)
|
|||||||
sc->fields = vsc->fields;
|
sc->fields = vsc->fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, int fields)
|
static int gxf_init_timecode(AVFormatContext *s, GXFTimecode *tc, const char *tcstr, int fields)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
if (sscanf(tc->str, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) {
|
if (sscanf(tcstr, "%d:%d:%d%c%d", &tc->hh, &tc->mm, &tc->ss, &c, &tc->ff) != 5) {
|
||||||
av_log(s, AV_LOG_ERROR, "unable to parse timecode, "
|
av_log(s, AV_LOG_ERROR, "unable to parse timecode, "
|
||||||
"syntax: hh:mm:ss[:;.]ff\n");
|
"syntax: hh:mm:ss[:;.]ff\n");
|
||||||
return -1;
|
return -1;
|
||||||
@ -681,6 +680,7 @@ static int gxf_write_header(AVFormatContext *s)
|
|||||||
GXFStreamContext *vsc = NULL;
|
GXFStreamContext *vsc = NULL;
|
||||||
uint8_t tracks[255] = {0};
|
uint8_t tracks[255] = {0};
|
||||||
int i, media_info = 0;
|
int i, media_info = 0;
|
||||||
|
AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
|
||||||
|
|
||||||
if (!pb->seekable) {
|
if (!pb->seekable) {
|
||||||
av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome");
|
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)
|
|||||||
"gxf muxer only accepts PAL or NTSC resolutions currently\n");
|
"gxf muxer only accepts PAL or NTSC resolutions currently\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (!tcr)
|
||||||
|
tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
|
||||||
avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den);
|
avpriv_set_pts_info(st, 64, gxf->time_base.num, gxf->time_base.den);
|
||||||
if (gxf_find_lines_index(st) < 0)
|
if (gxf_find_lines_index(st) < 0)
|
||||||
sc->lines_index = -1;
|
sc->lines_index = -1;
|
||||||
@ -792,9 +794,8 @@ static int gxf_write_header(AVFormatContext *s)
|
|||||||
if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0)
|
if (ff_audio_interleave_init(s, GXF_samples_per_frame, (AVRational){ 1, 48000 }) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (gxf->tc.str) {
|
if (tcr)
|
||||||
gxf_init_timecode(s, &gxf->tc, vsc->fields);
|
gxf_init_timecode(s, &gxf->tc, tcr->value, vsc->fields);
|
||||||
}
|
|
||||||
|
|
||||||
gxf_init_timecode_track(&gxf->timecode_track, vsc);
|
gxf_init_timecode_track(&gxf->timecode_track, vsc);
|
||||||
gxf->flags |= 0x200000; // time code track is non-drop frame
|
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
|
|||||||
ff_interleave_packet_per_dts, gxf_compare_field_nb);
|
ff_interleave_packet_per_dts, gxf_compare_field_nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const AVOption options[] = {
|
|
||||||
{ AV_TIMECODE_OPTION(GXFContext, tc.str, AV_OPT_FLAG_ENCODING_PARAM) },
|
|
||||||
{ NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
static const AVClass gxf_muxer_class = {
|
|
||||||
.class_name = "GXF muxer",
|
|
||||||
.item_name = av_default_item_name,
|
|
||||||
.option = options,
|
|
||||||
.version = LIBAVUTIL_VERSION_INT,
|
|
||||||
};
|
|
||||||
|
|
||||||
AVOutputFormat ff_gxf_muxer = {
|
AVOutputFormat ff_gxf_muxer = {
|
||||||
.name = "gxf",
|
.name = "gxf",
|
||||||
.long_name = NULL_IF_CONFIG_SMALL("GXF format"),
|
.long_name = NULL_IF_CONFIG_SMALL("GXF format"),
|
||||||
@ -1006,5 +995,4 @@ AVOutputFormat ff_gxf_muxer = {
|
|||||||
.write_packet = gxf_write_packet,
|
.write_packet = gxf_write_packet,
|
||||||
.write_trailer = gxf_write_trailer,
|
.write_trailer = gxf_write_trailer,
|
||||||
.interleave_packet = gxf_interleave_packet,
|
.interleave_packet = gxf_interleave_packet,
|
||||||
.priv_class = &gxf_muxer_class,
|
|
||||||
};
|
};
|
||||||
|
@ -189,7 +189,6 @@ typedef struct MXFContext {
|
|||||||
unsigned body_partitions_count;
|
unsigned body_partitions_count;
|
||||||
int last_key_index; ///< index of last key frame
|
int last_key_index; ///< index of last key frame
|
||||||
uint64_t duration;
|
uint64_t duration;
|
||||||
char *tc_opt_str; ///< timecode option string
|
|
||||||
AVTimecode tc; ///< timecode context
|
AVTimecode tc; ///< timecode context
|
||||||
AVStream *timecode_track;
|
AVStream *timecode_track;
|
||||||
int timecode_base; ///< rounded time code base (25 or 30)
|
int timecode_base; ///< rounded time code base (25 or 30)
|
||||||
@ -1403,6 +1402,7 @@ static int mxf_write_header(AVFormatContext *s)
|
|||||||
const int *samples_per_frame = NULL;
|
const int *samples_per_frame = NULL;
|
||||||
AVDictionaryEntry *t;
|
AVDictionaryEntry *t;
|
||||||
int64_t timestamp = 0;
|
int64_t timestamp = 0;
|
||||||
|
AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
|
||||||
|
|
||||||
if (!s->nb_streams)
|
if (!s->nb_streams)
|
||||||
return -1;
|
return -1;
|
||||||
@ -1443,9 +1443,10 @@ static int mxf_write_header(AVFormatContext *s)
|
|||||||
}
|
}
|
||||||
rate = (AVRational){mxf->time_base.den, mxf->time_base.num};
|
rate = (AVRational){mxf->time_base.den, mxf->time_base.num};
|
||||||
avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den);
|
avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den);
|
||||||
if (mxf->tc_opt_str)
|
if (!tcr)
|
||||||
ret = av_timecode_init_from_string(&mxf->tc, rate,
|
tcr = av_dict_get(st->metadata, "timecode", NULL, 0);
|
||||||
mxf->tc_opt_str, s);
|
if (tcr)
|
||||||
|
ret = av_timecode_init_from_string(&mxf->tc, rate, tcr->value, s);
|
||||||
else
|
else
|
||||||
ret = av_timecode_init(&mxf->tc, rate, 0, 0, s);
|
ret = av_timecode_init(&mxf->tc, rate, 0, 0, s);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -1877,26 +1878,6 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *out, AVPacket *pkt, int
|
|||||||
mxf_interleave_get_packet, mxf_compare_timestamps);
|
mxf_interleave_get_packet, mxf_compare_timestamps);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const AVClass mxf_class = {
|
|
||||||
.class_name = "mxf",
|
|
||||||
.item_name = av_default_item_name,
|
|
||||||
.version = LIBAVUTIL_VERSION_INT,
|
|
||||||
.option = (const AVOption[]){
|
|
||||||
{AV_TIMECODE_OPTION(MXFContext, tc_opt_str, AV_OPT_FLAG_ENCODING_PARAM)},
|
|
||||||
{NULL}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const AVClass mxf_d10_class = {
|
|
||||||
.class_name = "mxf_d10",
|
|
||||||
.item_name = av_default_item_name,
|
|
||||||
.version = LIBAVUTIL_VERSION_INT,
|
|
||||||
.option = (const AVOption[]){
|
|
||||||
{AV_TIMECODE_OPTION(MXFContext, tc_opt_str, AV_OPT_FLAG_ENCODING_PARAM)},
|
|
||||||
{NULL}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
AVOutputFormat ff_mxf_muxer = {
|
AVOutputFormat ff_mxf_muxer = {
|
||||||
.name = "mxf",
|
.name = "mxf",
|
||||||
.long_name = NULL_IF_CONFIG_SMALL("Material eXchange Format"),
|
.long_name = NULL_IF_CONFIG_SMALL("Material eXchange Format"),
|
||||||
@ -1910,7 +1891,6 @@ AVOutputFormat ff_mxf_muxer = {
|
|||||||
.write_trailer = mxf_write_footer,
|
.write_trailer = mxf_write_footer,
|
||||||
.flags = AVFMT_NOTIMESTAMPS,
|
.flags = AVFMT_NOTIMESTAMPS,
|
||||||
.interleave_packet = mxf_interleave,
|
.interleave_packet = mxf_interleave,
|
||||||
.priv_class = &mxf_class,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AVOutputFormat ff_mxf_d10_muxer = {
|
AVOutputFormat ff_mxf_d10_muxer = {
|
||||||
@ -1925,5 +1905,4 @@ AVOutputFormat ff_mxf_d10_muxer = {
|
|||||||
.write_trailer = mxf_write_footer,
|
.write_trailer = mxf_write_footer,
|
||||||
.flags = AVFMT_NOTIMESTAMPS,
|
.flags = AVFMT_NOTIMESTAMPS,
|
||||||
.interleave_packet = mxf_interleave,
|
.interleave_packet = mxf_interleave,
|
||||||
.priv_class = &mxf_d10_class,
|
|
||||||
};
|
};
|
||||||
|
@ -32,12 +32,6 @@
|
|||||||
|
|
||||||
#define AV_TIMECODE_STR_SIZE 16
|
#define AV_TIMECODE_STR_SIZE 16
|
||||||
|
|
||||||
#define AV_TIMECODE_OPTION(ctx, string_field, flags) \
|
|
||||||
"timecode", "set timecode value following hh:mm:ss[:;.]ff format, " \
|
|
||||||
"use ';' or '.' before frame number for drop frame", \
|
|
||||||
offsetof(ctx, string_field), \
|
|
||||||
AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, flags
|
|
||||||
|
|
||||||
enum AVTimecodeFlag {
|
enum AVTimecodeFlag {
|
||||||
AV_TIMECODE_FLAG_DROPFRAME = 1<<0, ///< timecode is drop frame
|
AV_TIMECODE_FLAG_DROPFRAME = 1<<0, ///< timecode is drop frame
|
||||||
AV_TIMECODE_FLAG_24HOURSMAX = 1<<1, ///< timecode wraps after 24 hours
|
AV_TIMECODE_FLAG_24HOURSMAX = 1<<1, ///< timecode wraps after 24 hours
|
||||||
|
Loading…
Reference in New Issue
Block a user