[FFmpeg-cvslog] r23173 - in branches/0.6: . libavformat/movenc.c

siretart subversion
Wed May 19 10:02:33 CEST 2010


Author: siretart
Date: Wed May 19 10:02:33 2010
New Revision: 23173

Log:
movenc: Write QuickTime chapters

backport r23022 by conrad

Modified:
   branches/0.6/   (props changed)
   branches/0.6/libavformat/movenc.c

Modified: branches/0.6/libavformat/movenc.c
==============================================================================
--- branches/0.6/libavformat/movenc.c	Wed May 19 10:02:05 2010	(r23172)
+++ branches/0.6/libavformat/movenc.c	Wed May 19 10:02:33 2010	(r23173)
@@ -77,12 +77,15 @@ typedef struct MOVIndex {
     MOVIentry   *cluster;
     int         audio_vbr;
     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;
 
 typedef struct MOVMuxContext {
     int     mode;
     int64_t time;
     int     nb_streams;
+    int     chapter_track; ///< qt chapter track number
     int64_t mdat_pos;
     uint64_t mdat_size;
     MOVTrack *tracks;
@@ -1183,8 +1186,8 @@ static int mov_write_tkhd_tag(ByteIOCont
     put_be32(pb, 0x40000000); /* reserved */
 
     /* Track width and height, for visual only */
-    if(track->enc->codec_type == AVMEDIA_TYPE_VIDEO ||
-       track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE) {
+    if(st && (track->enc->codec_type == AVMEDIA_TYPE_VIDEO ||
+              track->enc->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
         double sample_aspect_ratio = av_q2d(st->sample_aspect_ratio);
         if(!sample_aspect_ratio || track->height != track->enc->height)
             sample_aspect_ratio = 1;
@@ -1217,6 +1220,16 @@ static int mov_write_edts_tag(ByteIOCont
     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)
 static int mov_write_uuid_tag_psp(ByteIOContext *pb, MOVTrack *mov)
 {
@@ -1244,6 +1257,8 @@ static int mov_write_trak_tag(ByteIOCont
     mov_write_tkhd_tag(pb, track, st);
     if (track->mode == MODE_PSP || track->flags & MOV_TRACK_CTTS)
         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);
     if (track->mode == MODE_PSP)
         mov_write_uuid_tag_psp(pb,track);  // PSP Movies require this uuid box
@@ -1656,11 +1671,17 @@ static int mov_write_moov_tag(ByteIOCont
         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_iods_tag(pb, mov);
     for (i=0; i<mov->nb_streams; i++) {
         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(AVFormatCont
     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)
 {
     ByteIOContext *pb = s->pb;
@@ -1949,7 +2005,11 @@ static int mov_write_header(AVFormatCont
         }
     }
 
-    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)
         return AVERROR(ENOMEM);
 
@@ -2019,7 +2079,9 @@ static int mov_write_header(AVFormatCont
 
     mov_write_mdat_tag(pb, mov);
     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);
 
@@ -2053,6 +2115,9 @@ static int mov_write_trailer(AVFormatCon
 
     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++) {
         av_freep(&mov->tracks[i].cluster);
 



More information about the ffmpeg-cvslog mailing list