mirror of
https://git.ffmpeg.org/ffmpeg.git
synced 2024-12-25 16:52:31 +00:00
movenc: Write QuickTime chapters
Originally committed as revision 23022 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
parent
1b206f62ea
commit
ddb630177a
@ -77,12 +77,15 @@ typedef struct MOVIndex {
|
|||||||
MOVIentry *cluster;
|
MOVIentry *cluster;
|
||||||
int audio_vbr;
|
int audio_vbr;
|
||||||
int height; ///< active picture (w/o VBI) height for D-10/IMX
|
int height; ///< active picture (w/o VBI) height for D-10/IMX
|
||||||
|
uint32_t tref_tag;
|
||||||
|
int tref_id; ///< trackID of the referenced track
|
||||||
} MOVTrack;
|
} MOVTrack;
|
||||||
|
|
||||||
typedef struct MOVMuxContext {
|
typedef struct MOVMuxContext {
|
||||||
int mode;
|
int mode;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
int nb_streams;
|
int nb_streams;
|
||||||
|
int chapter_track; ///< qt chapter track number
|
||||||
int64_t mdat_pos;
|
int64_t mdat_pos;
|
||||||
uint64_t mdat_size;
|
uint64_t mdat_size;
|
||||||
MOVTrack *tracks;
|
MOVTrack *tracks;
|
||||||
@ -1183,8 +1186,8 @@ static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack *track, AVStream *st)
|
|||||||
put_be32(pb, 0x40000000); /* reserved */
|
put_be32(pb, 0x40000000); /* reserved */
|
||||||
|
|
||||||
/* Track width and height, for visual only */
|
/* Track width and height, for visual only */
|
||||||
if(track->enc->codec_type == AVMEDIA_TYPE_VIDEO ||
|
if(st && (track->enc->codec_type == AVMEDIA_TYPE_VIDEO ||
|
||||||
track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) {
|
track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
|
||||||
double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio);
|
double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio);
|
||||||
if(!sample_aspect_ratio || track->height != track->enc->height)
|
if(!sample_aspect_ratio || track->height != track->enc->height)
|
||||||
sample_aspect_ratio = 1;
|
sample_aspect_ratio = 1;
|
||||||
@ -1217,6 +1220,16 @@ static int mov_write_edts_tag(ByteIOContext *pb, MOVTrack *track)
|
|||||||
return 0x24;
|
return 0x24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mov_write_tref_tag(ByteIOContext *pb, MOVTrack *track)
|
||||||
|
{
|
||||||
|
put_be32(pb, 20); // size
|
||||||
|
put_tag(pb, "tref");
|
||||||
|
put_be32(pb, 12); // size (subatom)
|
||||||
|
put_le32(pb, track->tref_tag);
|
||||||
|
put_be32(pb, track->tref_id);
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
// goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it)
|
// goes at the end of each track! ... Critical for PSP playback ("Incompatible data" without it)
|
||||||
static int mov_write_uuid_tag_psp(ByteIOContext *pb, MOVTrack *mov)
|
static int mov_write_uuid_tag_psp(ByteIOContext *pb, MOVTrack *mov)
|
||||||
{
|
{
|
||||||
@ -1244,6 +1257,8 @@ static int mov_write_trak_tag(ByteIOContext *pb, MOVTrack *track, AVStream *st)
|
|||||||
mov_write_tkhd_tag(pb, track, st);
|
mov_write_tkhd_tag(pb, track, st);
|
||||||
if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS)
|
if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS)
|
||||||
mov_write_edts_tag(pb, track); // PSP Movies require edts box
|
mov_write_edts_tag(pb, track); // PSP Movies require edts box
|
||||||
|
if (track->tref_tag)
|
||||||
|
mov_write_tref_tag(pb, track);
|
||||||
mov_write_mdia_tag(pb, track);
|
mov_write_mdia_tag(pb, track);
|
||||||
if (track->mode == MODE_PSP)
|
if (track->mode == MODE_PSP)
|
||||||
mov_write_uuid_tag_psp(pb,track); // PSP Movies require this uuid box
|
mov_write_uuid_tag_psp(pb,track); // PSP Movies require this uuid box
|
||||||
@ -1656,11 +1671,17 @@ static int mov_write_moov_tag(ByteIOContext *pb, MOVMuxContext *mov,
|
|||||||
mov->tracks[i].trackID = i+1;
|
mov->tracks[i].trackID = i+1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mov->chapter_track)
|
||||||
|
for (i=0; i<s->nb_streams; i++) {
|
||||||
|
mov->tracks[i].tref_tag = MKTAG('c','h','a','p');
|
||||||
|
mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].trackID;
|
||||||
|
}
|
||||||
|
|
||||||
mov_write_mvhd_tag(pb, mov);
|
mov_write_mvhd_tag(pb, mov);
|
||||||
//mov_write_iods_tag(pb, mov);
|
//mov_write_iods_tag(pb, mov);
|
||||||
for (i=0; i<mov->nb_streams; i++) {
|
for (i=0; i<mov->nb_streams; i++) {
|
||||||
if(mov->tracks[i].entry > 0) {
|
if(mov->tracks[i].entry > 0) {
|
||||||
mov_write_trak_tag(pb, &(mov->tracks[i]), s->streams[i]);
|
mov_write_trak_tag(pb, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1918,6 +1939,41 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QuickTime chapters involve an additional text track with the chapter names
|
||||||
|
// as samples, and a tref pointing from the other tracks to the chapter one.
|
||||||
|
static void mov_create_chapter_track(AVFormatContext *s, int tracknum)
|
||||||
|
{
|
||||||
|
MOVMuxContext *mov = s->priv_data;
|
||||||
|
MOVTrack *track = &mov->tracks[tracknum];
|
||||||
|
AVPacket pkt = { .stream_index = tracknum, .flags = AV_PKT_FLAG_KEY };
|
||||||
|
int i, len;
|
||||||
|
|
||||||
|
track->mode = mov->mode;
|
||||||
|
track->tag = MKTAG('t','e','x','t');
|
||||||
|
track->timescale = MOV_TIMESCALE;
|
||||||
|
track->enc = avcodec_alloc_context();
|
||||||
|
track->enc->codec_type = AVMEDIA_TYPE_SUBTITLE;
|
||||||
|
|
||||||
|
for (i = 0; i < s->nb_chapters; i++) {
|
||||||
|
AVChapter *c = s->chapters[i];
|
||||||
|
AVMetadataTag *t;
|
||||||
|
|
||||||
|
int64_t end = av_rescale_q(c->end, c->time_base, (AVRational){1,MOV_TIMESCALE});
|
||||||
|
pkt.pts = pkt.dts = av_rescale_q(c->start, c->time_base, (AVRational){1,MOV_TIMESCALE});
|
||||||
|
pkt.duration = end - pkt.dts;
|
||||||
|
|
||||||
|
if ((t = av_metadata_get(c->metadata, "title", NULL, 0))) {
|
||||||
|
len = strlen(t->value);
|
||||||
|
pkt.size = len+2;
|
||||||
|
pkt.data = av_malloc(pkt.size);
|
||||||
|
AV_WB16(pkt.data, len);
|
||||||
|
memcpy(pkt.data+2, t->value, len);
|
||||||
|
mov_write_packet(s, &pkt);
|
||||||
|
av_freep(&pkt.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int mov_write_header(AVFormatContext *s)
|
static int mov_write_header(AVFormatContext *s)
|
||||||
{
|
{
|
||||||
ByteIOContext *pb = s->pb;
|
ByteIOContext *pb = s->pb;
|
||||||
@ -1949,7 +2005,11 @@ static int mov_write_header(AVFormatContext *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mov->tracks = av_mallocz(s->nb_streams*sizeof(*mov->tracks));
|
mov->nb_streams = s->nb_streams;
|
||||||
|
if (mov->mode & (MODE_MOV|MODE_IPOD) && s->nb_chapters)
|
||||||
|
mov->chapter_track = mov->nb_streams++;
|
||||||
|
|
||||||
|
mov->tracks = av_mallocz(mov->nb_streams*sizeof(*mov->tracks));
|
||||||
if (!mov->tracks)
|
if (!mov->tracks)
|
||||||
return AVERROR(ENOMEM);
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
@ -2019,7 +2079,9 @@ static int mov_write_header(AVFormatContext *s)
|
|||||||
|
|
||||||
mov_write_mdat_tag(pb, mov);
|
mov_write_mdat_tag(pb, mov);
|
||||||
mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based
|
mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based
|
||||||
mov->nb_streams = s->nb_streams;
|
|
||||||
|
if (mov->chapter_track)
|
||||||
|
mov_create_chapter_track(s, mov->chapter_track);
|
||||||
|
|
||||||
put_flush_packet(pb);
|
put_flush_packet(pb);
|
||||||
|
|
||||||
@ -2053,6 +2115,9 @@ static int mov_write_trailer(AVFormatContext *s)
|
|||||||
|
|
||||||
mov_write_moov_tag(pb, mov, s);
|
mov_write_moov_tag(pb, mov, s);
|
||||||
|
|
||||||
|
if (mov->chapter_track)
|
||||||
|
av_freep(&mov->tracks[mov->chapter_track].enc);
|
||||||
|
|
||||||
for (i=0; i<mov->nb_streams; i++) {
|
for (i=0; i<mov->nb_streams; i++) {
|
||||||
av_freep(&mov->tracks[i].cluster);
|
av_freep(&mov->tracks[i].cluster);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user