mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-26 19:01:44 +02:00
movenc: add timecode track support.
This commit is contained in:
parent
9846a9c701
commit
9fb2e234d0
@ -893,7 +893,7 @@ performance on systems without hardware floating point support).
|
||||
@item AVI @tab X @tab X
|
||||
@item DV @tab X @tab X
|
||||
@item GXF @tab X @tab X
|
||||
@item MOV @tab X @tab
|
||||
@item MOV @tab X @tab X
|
||||
@item MPEG1/2 @tab X @tab X
|
||||
@item MXF @tab X @tab X
|
||||
@end multitable
|
||||
|
@ -1095,6 +1095,26 @@ static int mov_write_video_tag(AVIOContext *pb, MOVTrack *track)
|
||||
return update_size(pb, pos);
|
||||
}
|
||||
|
||||
static int mov_write_tmcd_tag(AVIOContext *pb, MOVTrack *track)
|
||||
{
|
||||
int64_t pos = avio_tell(pb);
|
||||
int frame_duration = track->enc->time_base.num;
|
||||
int nb_frames = (track->timescale + frame_duration/2) / frame_duration;
|
||||
|
||||
avio_wb32(pb, 0); /* size */
|
||||
ffio_wfourcc(pb, "tmcd"); /* Data format */
|
||||
avio_wb32(pb, 0); /* Reserved */
|
||||
avio_wb32(pb, 1); /* Data reference index */
|
||||
avio_wb32(pb, 0); /* Flags */
|
||||
avio_wb32(pb, track->timecode_flags); /* Flags (timecode) */
|
||||
avio_wb32(pb, track->timescale); /* Timescale */
|
||||
avio_wb32(pb, frame_duration); /* Frame duration */
|
||||
avio_w8(pb, nb_frames); /* Number of frames */
|
||||
avio_wb24(pb, 0); /* Reserved */
|
||||
/* TODO: source reference string */
|
||||
return update_size(pb, pos);
|
||||
}
|
||||
|
||||
static int mov_write_rtp_tag(AVIOContext *pb, MOVTrack *track)
|
||||
{
|
||||
int64_t pos = avio_tell(pb);
|
||||
@ -1130,6 +1150,8 @@ static int mov_write_stsd_tag(AVIOContext *pb, MOVTrack *track)
|
||||
mov_write_subtitle_tag(pb, track);
|
||||
else if (track->enc->codec_tag == MKTAG('r','t','p',' '))
|
||||
mov_write_rtp_tag(pb, track);
|
||||
else if (track->enc->codec_tag == MKTAG('t','m','c','d'))
|
||||
mov_write_tmcd_tag(pb, track);
|
||||
return update_size(pb, pos);
|
||||
}
|
||||
|
||||
@ -1262,9 +1284,32 @@ static int mov_write_nmhd_tag(AVIOContext *pb)
|
||||
return 12;
|
||||
}
|
||||
|
||||
static int mov_write_gmhd_tag(AVIOContext *pb)
|
||||
static int mov_write_tcmi_tag(AVIOContext *pb, MOVTrack *track)
|
||||
{
|
||||
avio_wb32(pb, 0x4C); /* size */
|
||||
int64_t pos = avio_tell(pb);
|
||||
const char *font = "Lucida Grande";
|
||||
avio_wb32(pb, 0); /* size */
|
||||
ffio_wfourcc(pb, "tcmi"); /* timecode media information atom */
|
||||
avio_wb32(pb, 0); /* version & flags */
|
||||
avio_wb16(pb, 0); /* text font */
|
||||
avio_wb16(pb, 0); /* text face */
|
||||
avio_wb16(pb, 12); /* text size */
|
||||
avio_wb16(pb, 0); /* (unknown, not in the QT specs...) */
|
||||
avio_wb16(pb, 0x0000); /* text color (red) */
|
||||
avio_wb16(pb, 0x0000); /* text color (green) */
|
||||
avio_wb16(pb, 0x0000); /* text color (blue) */
|
||||
avio_wb16(pb, 0xffff); /* background color (red) */
|
||||
avio_wb16(pb, 0xffff); /* background color (green) */
|
||||
avio_wb16(pb, 0xffff); /* background color (blue) */
|
||||
avio_w8(pb, strlen(font)); /* font len (part of the pascal string) */
|
||||
avio_write(pb, font, strlen(font)); /* font name */
|
||||
return update_size(pb, pos);
|
||||
}
|
||||
|
||||
static int mov_write_gmhd_tag(AVIOContext *pb, MOVTrack *track)
|
||||
{
|
||||
int64_t pos = avio_tell(pb);
|
||||
avio_wb32(pb, 0); /* size */
|
||||
ffio_wfourcc(pb, "gmhd");
|
||||
avio_wb32(pb, 0x18); /* gmin size */
|
||||
ffio_wfourcc(pb, "gmin");/* generic media info */
|
||||
@ -1295,7 +1340,14 @@ static int mov_write_gmhd_tag(AVIOContext *pb)
|
||||
avio_wb32(pb, 0x00004000);
|
||||
avio_wb16(pb, 0x0000);
|
||||
|
||||
return 0x4C;
|
||||
if (track->enc->codec_tag == MKTAG('t','m','c','d')) {
|
||||
int64_t tmcd_pos = avio_tell(pb);
|
||||
avio_wb32(pb, 0); /* size */
|
||||
ffio_wfourcc(pb, "tmcd");
|
||||
mov_write_tcmi_tag(pb, track);
|
||||
update_size(pb, tmcd_pos);
|
||||
}
|
||||
return update_size(pb, pos);
|
||||
}
|
||||
|
||||
static int mov_write_smhd_tag(AVIOContext *pb)
|
||||
@ -1338,6 +1390,9 @@ static int mov_write_hdlr_tag(AVIOContext *pb, MOVTrack *track)
|
||||
if (track->tag == MKTAG('t','x','3','g')) hdlr_type = "sbtl";
|
||||
else hdlr_type = "text";
|
||||
descr = "SubtitleHandler";
|
||||
} else if (track->enc->codec_tag == MKTAG('t','m','c','d')) {
|
||||
hdlr_type = "tmcd";
|
||||
descr = "TimeCodeHandler";
|
||||
} else if (track->enc->codec_tag == MKTAG('r','t','p',' ')) {
|
||||
hdlr_type = "hint";
|
||||
descr = "HintHandler";
|
||||
@ -1389,8 +1444,10 @@ static int mov_write_minf_tag(AVIOContext *pb, MOVTrack *track)
|
||||
else if (track->enc->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
mov_write_smhd_tag(pb);
|
||||
else if (track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) {
|
||||
if (track->tag == MKTAG('t','e','x','t')) mov_write_gmhd_tag(pb);
|
||||
if (track->tag == MKTAG('t','e','x','t')) mov_write_gmhd_tag(pb, track);
|
||||
else mov_write_nmhd_tag(pb);
|
||||
} else if (track->tag == MKTAG('t','m','c','d')) {
|
||||
mov_write_gmhd_tag(pb, track);
|
||||
} else if (track->tag == MKTAG('r','t','p',' ')) {
|
||||
mov_write_hmhd_tag(pb);
|
||||
}
|
||||
@ -2147,6 +2204,14 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
|
||||
mov->tracks[mov->tracks[i].src_track].track_id;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < mov->nb_streams; i++) {
|
||||
if (mov->tracks[i].tag == MKTAG('t','m','c','d')) {
|
||||
int src_trk = mov->tracks[i].src_track;
|
||||
mov->tracks[src_trk].tref_tag = mov->tracks[i].tag;
|
||||
mov->tracks[src_trk].tref_id = mov->tracks[i].track_id;
|
||||
mov->tracks[i].track_duration = mov->tracks[src_trk].track_duration;
|
||||
}
|
||||
}
|
||||
|
||||
mov_write_mvhd_tag(pb, mov);
|
||||
if (mov->mode != MODE_MOV && !mov->iods_skip)
|
||||
@ -3151,12 +3216,48 @@ static void mov_create_chapter_track(AVFormatContext *s, int tracknum)
|
||||
}
|
||||
}
|
||||
|
||||
static int mov_create_timecode_track(AVFormatContext *s, int index, int src_index, const char *tcstr)
|
||||
{
|
||||
MOVMuxContext *mov = s->priv_data;
|
||||
MOVTrack *track = &mov->tracks[index];
|
||||
AVStream *src_st = s->streams[src_index];
|
||||
AVTimecode tc;
|
||||
AVPacket pkt = {.stream_index = index, .flags = AV_PKT_FLAG_KEY, .size = 4};
|
||||
AVRational rate = {src_st->codec->time_base.den, src_st->codec->time_base.num};
|
||||
|
||||
/* compute the frame number */
|
||||
int ret = av_timecode_init_from_string(&tc, rate, tcstr, s);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* tmcd track based on video stream */
|
||||
track->mode = mov->mode;
|
||||
track->tag = MKTAG('t','m','c','d');
|
||||
track->src_track = src_index;
|
||||
track->timescale = src_st->codec->time_base.den;
|
||||
if (tc.flags & AV_TIMECODE_FLAG_DROPFRAME)
|
||||
track->timecode_flags |= MOV_TIMECODE_FLAG_DROPFRAME;
|
||||
|
||||
/* encode context: tmcd data stream */
|
||||
track->enc = avcodec_alloc_context3(NULL);
|
||||
track->enc->codec_type = AVMEDIA_TYPE_DATA;
|
||||
track->enc->codec_tag = track->tag;
|
||||
track->enc->time_base = src_st->codec->time_base;
|
||||
|
||||
/* the tmcd track just contains one packet with the frame number */
|
||||
pkt.data = av_malloc(pkt.size);
|
||||
AV_WB32(pkt.data, tc.start);
|
||||
ret = ff_mov_write_packet(s, &pkt);
|
||||
av_free(pkt.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mov_write_header(AVFormatContext *s)
|
||||
{
|
||||
AVIOContext *pb = s->pb;
|
||||
MOVMuxContext *mov = s->priv_data;
|
||||
AVDictionaryEntry *t;
|
||||
int i, hint_track = 0;
|
||||
AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0);
|
||||
int i, hint_track = 0, tmcd_track = 0;
|
||||
|
||||
/* Set the FRAGMENT flag if any of the fragmentation methods are
|
||||
* enabled. */
|
||||
@ -3213,6 +3314,17 @@ static int mov_write_header(AVFormatContext *s)
|
||||
}
|
||||
}
|
||||
|
||||
if (mov->mode == MODE_MOV) {
|
||||
/* Add a tmcd track for each video stream with a timecode */
|
||||
tmcd_track = mov->nb_streams;
|
||||
for (i = 0; i < s->nb_streams; i++) {
|
||||
AVStream *st = s->streams[i];
|
||||
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||
(global_tcr || av_dict_get(st->metadata, "timecode", NULL, 0)))
|
||||
mov->nb_streams++;
|
||||
}
|
||||
}
|
||||
|
||||
mov->tracks = av_mallocz(mov->nb_streams*sizeof(*mov->tracks));
|
||||
if (!mov->tracks)
|
||||
return AVERROR(ENOMEM);
|
||||
@ -3336,6 +3448,24 @@ static int mov_write_header(AVFormatContext *s)
|
||||
}
|
||||
}
|
||||
|
||||
if (mov->mode == MODE_MOV) {
|
||||
/* Initialize the tmcd tracks */
|
||||
for (i = 0; i < s->nb_streams; i++) {
|
||||
AVStream *st = s->streams[i];
|
||||
t = global_tcr;
|
||||
|
||||
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
|
||||
if (!t)
|
||||
t = av_dict_get(st->metadata, "timecode", NULL, 0);
|
||||
if (!t)
|
||||
continue;
|
||||
if (mov_create_timecode_track(s, tmcd_track, i, t->value) < 0)
|
||||
goto error;
|
||||
tmcd_track++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avio_flush(pb);
|
||||
|
||||
if (mov->flags & FF_MOV_FLAG_ISML)
|
||||
@ -3401,6 +3531,8 @@ static int mov_write_trailer(AVFormatContext *s)
|
||||
for (i=0; i<mov->nb_streams; i++) {
|
||||
if (mov->tracks[i].tag == MKTAG('r','t','p',' '))
|
||||
ff_mov_close_hinting(&mov->tracks[i]);
|
||||
else if (mov->tracks[i].tag == MKTAG('t','m','c','d'))
|
||||
av_freep(&mov->tracks[i].enc);
|
||||
if (mov->flags & FF_MOV_FLAG_FRAGMENT &&
|
||||
mov->tracks[i].vc1_info.struct_offset && s->pb->seekable) {
|
||||
int64_t off = avio_tell(pb);
|
||||
|
@ -87,6 +87,10 @@ typedef struct MOVIndex {
|
||||
#define MOV_TRACK_CTTS 0x0001
|
||||
#define MOV_TRACK_STPS 0x0002
|
||||
uint32_t flags;
|
||||
#define MOV_TIMECODE_FLAG_DROPFRAME 0x0001
|
||||
#define MOV_TIMECODE_FLAG_24HOURSMAX 0x0002
|
||||
#define MOV_TIMECODE_FLAG_ALLOWNEGATIVE 0x0004
|
||||
uint32_t timecode_flags;
|
||||
int language;
|
||||
int track_id;
|
||||
int tag; ///< stsd fourcc
|
||||
@ -102,7 +106,7 @@ typedef struct MOVIndex {
|
||||
int64_t start_dts;
|
||||
|
||||
int hint_track; ///< the track that hints this track, -1 if no hint track is set
|
||||
int src_track; ///< the track that this hint track describes
|
||||
int src_track; ///< the track that this hint (or tmcd) track describes
|
||||
AVFormatContext *rtp_ctx; ///< the format context for the hinting rtp muxer
|
||||
uint32_t prev_rtp_ts;
|
||||
int64_t cur_rtp_ts_unwrapped;
|
||||
|
@ -1,11 +1,11 @@
|
||||
484aeef3be3eb4deef05c83bdc2dd484 *./tests/data/lavf/lavf.mov
|
||||
367346 ./tests/data/lavf/lavf.mov
|
||||
./tests/data/lavf/lavf.mov CRC=0x2f6a9b26
|
||||
305a68397e3cdb505704841fedcdc352 *./tests/data/lavf/lavf.mov
|
||||
357845 ./tests/data/lavf/lavf.mov
|
||||
21b992f6a677f971dfd685cc055a2b0a *./tests/data/lavf/lavf.mov
|
||||
358463 ./tests/data/lavf/lavf.mov
|
||||
./tests/data/lavf/lavf.mov CRC=0x2f6a9b26
|
||||
6e047bce400f2c4a840f783dee1ae030 *./tests/data/lavf/lavf.mov
|
||||
367275 ./tests/data/lavf/lavf.mov
|
||||
f1e80a52983775ea27dda0590b46e17a *./tests/data/lavf/lavf.mov
|
||||
367893 ./tests/data/lavf/lavf.mov
|
||||
./tests/data/lavf/lavf.mov CRC=0xab307eb9
|
||||
305a68397e3cdb505704841fedcdc352 *./tests/data/lavf/lavf.mov
|
||||
357845 ./tests/data/lavf/lavf.mov
|
||||
|
Loading…
Reference in New Issue
Block a user