]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/movenc.c
Add a read_timestamp function to ogg demuxer
[ffmpeg] / libavformat / movenc.c
index dd1266303bbe60452771d9dfb1876e557aa9e0aa..933ab46f3baa8b3c423909838101784c74d740f2 100644 (file)
@@ -54,7 +54,6 @@ typedef struct MOVIndex {
     long        time;
     int64_t     trackDuration;
     long        sampleCount;
-    long        sampleDuration;
     long        sampleSize;
     int         hasKeyframes;
     int         hasBframes;
@@ -79,7 +78,7 @@ typedef struct MOVContext {
     MOVTrack tracks[MAX_STREAMS];
 } MOVContext;
 
-//FIXME supprt 64bit varaint with wide placeholders
+//FIXME support 64 bit variant with wide placeholders
 static offset_t updateSize (ByteIOContext *pb, offset_t pos)
 {
     offset_t curpos = url_ftell(pb);
@@ -261,7 +260,7 @@ static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
     putDescr(pb, 0x04, 13 + decoderSpecificInfoLen);
 
     // Object type indication
-    put_byte(pb, codec_get_tag(ff_mov_obj_type, track->enc->codec_id));
+    put_byte(pb, codec_get_tag(ff_mp4_obj_type, track->enc->codec_id));
 
     // the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio)
     // plus 1 bit to indicate upstream and 1 bit set to 1 (reserved)
@@ -323,25 +322,6 @@ static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track)
     return updateSize (pb, pos);
 }
 
-static const CodecTag codec_movaudio_tags[] = {
-    { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') },
-    { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') },
-    { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') },
-    { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') },
-    { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') },
-    { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') },
-    { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') },
-    { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') },
-    { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') },
-    { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') },
-    { CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4') },
-    { CODEC_ID_PCM_S24LE, MKTAG('i', 'n', '2', '4') },
-    { CODEC_ID_PCM_S32BE, MKTAG('i', 'n', '3', '2') },
-    { CODEC_ID_PCM_S32LE, MKTAG('i', 'n', '3', '2') },
-    { CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') },
-    { CODEC_ID_NONE, 0 },
-};
-
 static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
 {
     offset_t pos = url_ftell(pb);
@@ -361,11 +341,20 @@ static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
     put_be16(pb, 0); /* Revision level */
     put_be32(pb, 0); /* Reserved */
 
-    put_be16(pb, track->mode == MODE_MOV ? track->enc->channels : 2); /* Number of channels */
-    /* FIXME 8 bit for 'raw ' in mov */
-    put_be16(pb, 16); /* Reserved */
+    if (track->mode == MODE_MOV) {
+        put_be16(pb, track->enc->channels);
+        if (track->enc->codec_id == CODEC_ID_PCM_U8 ||
+            track->enc->codec_id == CODEC_ID_PCM_S8)
+            put_be16(pb, 8); /* bits per sample */
+        else
+            put_be16(pb, 16);
+        put_be16(pb, track->audio_vbr ? -2 : 0); /* compression ID */
+    } else { /* reserved for mp4/3gp */
+        put_be16(pb, 2);
+        put_be16(pb, 16);
+        put_be16(pb, 0);
+    }
 
-    put_be16(pb, track->mode == MODE_MOV && track->audio_vbr ? -2 : 0); /* compression ID */
     put_be16(pb, 0); /* packet size (= 0) */
     put_be16(pb, track->timescale); /* Time scale */
     put_be16(pb, 0); /* Reserved */
@@ -418,7 +407,7 @@ static int mov_write_svq3_tag(ByteIOContext *pb)
 
 static uint8_t *avc_find_startcode( uint8_t *p, uint8_t *end )
 {
-    uint8_t *a = p + 4 - ((int)p & 3);
+    uint8_t *a = p + 4 - ((long)p & 3);
 
     for( end -= 3; p < a && p < end; p++ ) {
         if( p[0] == 0 && p[1] == 0 && p[2] == 1 )
@@ -481,7 +470,7 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track)
     put_tag(pb, "avcC");
     if (track->vosLen > 6) {
         /* check for h264 start code */
-        if (BE_32(track->vosData) == 0x00000001) {
+        if (AV_RB32(track->vosData) == 0x00000001) {
             uint8_t *buf, *end;
             uint32_t sps_size=0, pps_size=0;
             uint8_t *sps=0, *pps=0;
@@ -494,7 +483,7 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track)
             while (buf < end) {
                 unsigned int size;
                 uint8_t nal_type;
-                size = BE_32(buf);
+                size = AV_RB32(buf);
                 nal_type = buf[4] & 0x1f;
                 if (nal_type == 7) { /* SPS */
                     sps = buf + 4;
@@ -527,21 +516,50 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track)
     return updateSize(pb, pos);
 }
 
-static const CodecTag codec_movvideo_tags[] = {
-    { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') },
-    { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') },
-    { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
-    { CODEC_ID_H263, MKTAG('h', '2', '6', '3') },
-    { CODEC_ID_H263, MKTAG('s', '2', '6', '3') },
-    { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') },
-    /* special handling in mov_find_video_codec_tag */
-    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */
-    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL */
-    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'p', 'p') }, /* DVCPRO PAL */
-    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'n') }, /* DVCPRO50 NTSC */
-    { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '5', 'p') }, /* DVCPRO50 PAL */
-    { CODEC_ID_NONE, 0 },
-};
+/* also used by all avid codecs (dv, imx, meridien) and their variants */
+static int mov_write_avid_tag(ByteIOContext *pb, MOVTrack *track)
+{
+    int i;
+    put_be32(pb, 24); /* size */
+    put_tag(pb, "ACLR");
+    put_tag(pb, "ACLR");
+    put_tag(pb, "0001");
+    put_be32(pb, 1); /* yuv 1 / rgb 2 ? */
+    put_be32(pb, 0); /* unknown */
+
+    put_be32(pb, 24); /* size */
+    put_tag(pb, "APRG");
+    put_tag(pb, "APRG");
+    put_tag(pb, "0001");
+    put_be32(pb, 1); /* unknown */
+    put_be32(pb, 0); /* unknown */
+
+    put_be32(pb, 120); /* size */
+    put_tag(pb, "ARES");
+    put_tag(pb, "ARES");
+    put_tag(pb, "0001");
+    put_be32(pb, AV_RB32(track->vosData + 0x28)); /* dnxhd cid, some id ? */
+    put_be32(pb, track->enc->width);
+    /* values below are based on samples created with quicktime and avid codecs */
+    if (track->vosData[5] & 2) { // interlaced
+        put_be32(pb, track->enc->height/2);
+        put_be32(pb, 2); /* unknown */
+        put_be32(pb, 0); /* unknown */
+        put_be32(pb, 4); /* unknown */
+    } else {
+        put_be32(pb, track->enc->height);
+        put_be32(pb, 1); /* unknown */
+        put_be32(pb, 0); /* unknown */
+        put_be32(pb, 5); /* unknown */
+    }
+    /* padding */
+    for (i = 0; i < 10; i++)
+        put_be64(pb, 0);
+
+    /* extra padding for stsd needed */
+    put_be32(pb, 0);
+    return 0;
+}
 
 static int mov_find_video_codec_tag(AVFormatContext *s, MOVTrack *track)
 {
@@ -650,6 +668,8 @@ static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track)
         mov_write_svq3_tag(pb);
     else if(track->enc->codec_id == CODEC_ID_H264)
         mov_write_avcc_tag(pb, track);
+    else if(track->enc->codec_id == CODEC_ID_DNXHD)
+        mov_write_avid_tag(pb, track);
 
     return updateSize (pb, pos);
 }
@@ -670,7 +690,7 @@ static int mov_write_stsd_tag(ByteIOContext *pb, MOVTrack* track)
 
 static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack* track)
 {
-    Time2Sample *ctts_entries;
+    MOV_stts_t *ctts_entries;
     uint32_t entries = 0;
     uint32_t atom_size;
     int i;
@@ -704,7 +724,7 @@ static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack* track)
 /* Time to sample atom */
 static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track)
 {
-    Time2Sample *stts_entries;
+    MOV_stts_t *stts_entries;
     uint32_t entries = -1;
     uint32_t atom_size;
     int i;
@@ -872,6 +892,14 @@ static int mov_write_mdhd_tag(ByteIOContext *pb, MOVTrack* track)
     (version == 1) ? put_be64(pb, track->trackDuration) : put_be32(pb, track->trackDuration); /* duration */
     put_be16(pb, track->language); /* language */
     put_be16(pb, 0); /* reserved (quality) */
+
+    if(version!=0 && track->mode == MODE_MOV){
+        av_log(NULL, AV_LOG_ERROR,
+            "FATAL error, file duration too long for timebase, this file will not be\n"
+            "playable with quicktime. Choose a different timebase or a different\n"
+            "container format\n");
+    }
+
     return 32;
 }
 
@@ -1201,7 +1229,7 @@ static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov,
     /* iTunes meta data */
     mov_write_meta_tag(pb, mov, s);
 
-  if(mov->mode == MODE_MOV){ // the title field breaks gtkpod with mp4 and my suspicion is that stuff isnt valid in mp4
+  if(mov->mode == MODE_MOV){ // the title field breaks gtkpod with mp4 and my suspicion is that stuff is not valid in mp4
     /* Requirements */
     for (i=0; i<mov->nb_streams; i++) {
         if(mov->tracks[i].entry <= 0) continue;
@@ -1225,19 +1253,25 @@ static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov,
     return updateSize(pb, pos);
 }
 
+static int utf8len(uint8_t *b){
+    int len=0;
+    int val;
+    while(*b){
+        GET_UTF8(val, *b++, return -1;)
+        len++;
+    }
+    return len;
+}
 
-static size_t ascii_to_wc (ByteIOContext *pb, char *b, size_t n)
+static int ascii_to_wc (ByteIOContext *pb, uint8_t *b)
 {
-    size_t i;
-    unsigned char c;
-    for (i = 0; i < n - 1; i++) {
-        c = b[i];
-        if (! (0x20 <= c && c <= 0x7f ))
-            c = 0x3f;  /* '?' */
-        put_be16(pb, c);
+    int val;
+    while(*b){
+        GET_UTF8(val, *b++, return -1;)
+        put_be16(pb, val);
     }
     put_be16(pb, 0x00);
-    return 2*n;
+    return 0;
 }
 
 static uint16_t language_code (const char *str)
@@ -1275,31 +1309,37 @@ static int mov_write_uuidusmt_tag (ByteIOContext *pb, AVFormatContext *s)
         size += 12;
 
         // Encoder
-        len = strlen(LIBAVCODEC_IDENT)+1;
+        len = utf8len(LIBAVCODEC_IDENT)+1;
+        if(len<=0)
+            goto not_utf8;
         put_be16(pb, len*2+10);             /* size */
         put_be32(pb, 0x04);                 /* type */
         put_be16(pb, language_code("eng")); /* language */
         put_be16(pb, 0x01);                 /* ? */
-        ascii_to_wc(pb, LIBAVCODEC_IDENT, len);
+        ascii_to_wc(pb, LIBAVCODEC_IDENT);
         size += len*2+10;
 
         // Title
-        len = strlen(s->title)+1;
+        len = utf8len(s->title)+1;
+        if(len<=0)
+            goto not_utf8;
         put_be16(pb, len*2+10);             /* size */
         put_be32(pb, 0x01);                 /* type */
         put_be16(pb, language_code("eng")); /* language */
         put_be16(pb, 0x01);                 /* ? */
-        ascii_to_wc (pb, s->title, len);
+        ascii_to_wc (pb, s->title);
         size += len*2+10;
 
         // Date
 //        snprintf(dt,32,"%04d/%02d/%02d %02d:%02d:%02d",t_st->tm_year+1900,t_st->tm_mon+1,t_st->tm_mday,t_st->tm_hour,t_st->tm_min,t_st->tm_sec);
-        len = strlen("2006/04/01 11:11:11")+1;
+        len = utf8len("2006/04/01 11:11:11")+1;
+        if(len<=0)
+            goto not_utf8;
         put_be16(pb, len*2+10);    /* size */
         put_be32(pb, 0x03);        /* type */
         put_be16(pb, language_code("und")); /* language */
         put_be16(pb, 0x01);        /* ? */
-        ascii_to_wc (pb, "2006/04/01 11:11:11", len);
+        ascii_to_wc (pb, "2006/04/01 11:11:11");
         size += len*2+10;
 
         // size
@@ -1312,6 +1352,9 @@ static int mov_write_uuidusmt_tag (ByteIOContext *pb, AVFormatContext *s)
     }
 
     return size;
+not_utf8:
+    av_log(s, AV_LOG_ERROR, "not utf8\n");
+    return -1;
 }
 
 static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov,
@@ -1457,6 +1500,11 @@ static int mov_write_header(AVFormatContext *s)
     MOVContext *mov = s->priv_data;
     int i;
 
+    if (url_is_streamed(&s->pb)) {
+        av_log(s, AV_LOG_ERROR, "muxer does not support non seekable output\n");
+        return -1;
+    }
+
     /* Default mode == MP4 */
     mov->mode = MODE_MP4;
 
@@ -1486,28 +1534,25 @@ static int mov_write_header(AVFormatContext *s)
         if(st->codec->codec_type == CODEC_TYPE_VIDEO){
             track->tag = mov_find_video_codec_tag(s, track);
             track->timescale = st->codec->time_base.den;
-            track->sampleDuration = st->codec->time_base.num;
             av_set_pts_info(st, 64, 1, st->codec->time_base.den);
+            if (track->timescale > 100000)
+                av_log(NULL, AV_LOG_WARNING,
+                       "WARNING codec timebase is very high. If duration is too long,\n"
+                       "file may not be playable by quicktime. Specify a shorter timebase\n"
+                       "or choose different container.\n");
         }else if(st->codec->codec_type == CODEC_TYPE_AUDIO){
             track->tag = mov_find_audio_codec_tag(s, track);
             track->timescale = st->codec->sample_rate;
-            track->sampleDuration = st->codec->frame_size;
             av_set_pts_info(st, 64, 1, st->codec->sample_rate);
-            switch(track->enc->codec_id){
-            case CODEC_ID_MP3:
-            case CODEC_ID_AAC:
-            case CODEC_ID_AMR_NB:
-            case CODEC_ID_AMR_WB:
+            if(!st->codec->frame_size){
+                av_log(s, AV_LOG_ERROR, "track %d: codec frame size is not set\n", i);
+                return -1;
+            }else if(st->codec->frame_size > 1){ /* assume compressed audio */
                 track->audio_vbr = 1;
-                break;
-            default:
+            }else{
                 track->sampleSize = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels;
             }
         }
-        if (!track->sampleDuration) {
-            av_log(s, AV_LOG_ERROR, "track %d: sample duration is not set\n", i);
-            return -1;
-        }
     }
 
     mov_write_mdat_tag(pb, mov);
@@ -1542,7 +1587,7 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
             samplesInChunk++;
         }
         if(samplesInChunk > 1){
-            av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, inplement a AVParser for it\n");
+            av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, implement a AVParser for it\n");
             return -1;
         }
     } else if (trk->sampleSize)
@@ -1563,6 +1608,13 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
         avc_parse_nal_units(&pkt->data, &pkt->size);
         assert(pkt->size);
         size = pkt->size;
+    } else if (enc->codec_id == CODEC_ID_DNXHD && !trk->vosLen) {
+        /* copy frame header to create needed atoms */
+        if (size < 640)
+            return -1;
+        trk->vosLen = 640;
+        trk->vosData = av_malloc(trk->vosLen);
+        memcpy(trk->vosData, pkt->data, 640);
     }
 
     if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) {