X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=c8d32f3c679abf297a6965df6c7cc3f913287c81;hb=446263879b96f03cffe486e97b14b4e28a61f6ab;hp=013005b0f0d5a0ed3c174e241ce524884cfe9154;hpb=f45b744cb7b2a32188fccac902da597454a04ed9;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 013005b0f0d..c8d32f3c679 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -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; } @@ -1192,52 +1220,70 @@ static int mov_write_meta_tag(ByteIOContext *pb, MOVContext* mov, static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov, AVFormatContext *s) { - offset_t pos = url_ftell(pb); - int i; - - put_be32(pb, 0); /* size */ - put_tag(pb, "udta"); + int i, req = 0; - /* 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 /* Requirements */ for (i=0; inb_streams; i++) { if(mov->tracks[i].entry <= 0) continue; if (mov->tracks[i].enc->codec_id == CODEC_ID_AAC || mov->tracks[i].enc->codec_id == CODEC_ID_MPEG4) { - mov_write_string_tag(pb, "\251req", "QuickTime 6.0 or greater", 0); + req = 1; break; } } - mov_write_string_tag(pb, "\251nam", s->title , 0); - mov_write_string_tag(pb, "\251aut", s->author , 0); - mov_write_string_tag(pb, "\251alb", s->album , 0); - mov_write_day_tag(pb, s->year, 0); - if(mov->tracks[0].enc && !(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT)) - mov_write_string_tag(pb, "\251enc", LIBAVFORMAT_IDENT, 0); - mov_write_string_tag(pb, "\251des", s->comment , 0); - mov_write_string_tag(pb, "\251gen", s->genre , 0); - } + if (s->title[0] || s->author[0] || s->album[0] || s->year || + s->comment[0] || s->genre[0] || s->track || + (mov->mode == MODE_MOV && + ((mov->tracks[0].enc && !mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT) || req))) { + offset_t pos = url_ftell(pb); - return updateSize(pb, pos); + put_be32(pb, 0); /* size */ + put_tag(pb, "udta"); + + /* 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 is not valid in mp4 + /* Requirements */ + if (req) + mov_write_string_tag(pb, "\251req", "QuickTime 6.0 or greater", 0); + + mov_write_string_tag(pb, "\251nam", s->title , 0); + mov_write_string_tag(pb, "\251aut", s->author , 0); + mov_write_string_tag(pb, "\251alb", s->album , 0); + mov_write_day_tag(pb, s->year, 0); + if(mov->tracks[0].enc && !(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT)) + mov_write_string_tag(pb, "\251enc", LIBAVFORMAT_IDENT, 0); + mov_write_string_tag(pb, "\251des", s->comment , 0); + mov_write_string_tag(pb, "\251gen", s->genre , 0); + } + + return updateSize(pb, pos); + } + + return 0; } +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 +1321,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 +1364,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, @@ -1340,8 +1395,8 @@ static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov, if (mov->mode == MODE_PSP) mov_write_uuidusmt_tag(pb, s); - else - mov_write_udta_tag(pb, mov, s); + else if (mov->mode != MODE_3GP && mov->mode != MODE_3G2) + mov_write_udta_tag(pb, mov, s); return updateSize(pb, pos); } @@ -1457,6 +1512,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 +1546,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 +1599,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 +1620,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)) {