X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=818e57b02288e0301ca4f6dab25f2f9135242224;hb=383eda234879aad7e90fe73c52574ef7e735f85c;hp=f1529596f8b6c125e04de82a91482447ee576403;hpb=32ba6fb141c787b52a1017d5e49e144d3313b619;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index f1529596f8b..818e57b0228 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1,26 +1,28 @@ /* - * MOV, 3GP, MP4 encoder. + * MOV, 3GP, MP4 muxer * Copyright (c) 2003 Thomas Raivio. * Copyright (c) 2004 Gildas Bazin . * - * This library is free software; you can redistribute it and/or + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" -#include "avi.h" +#include "riff.h" #include "avio.h" -#include "mov.h" +#include "isom.h" #undef NDEBUG #include @@ -42,44 +44,40 @@ typedef struct MOVIentry { char key_frame; unsigned int entries; int64_t cts; + int64_t dts; } MOVIentry; typedef struct MOVIndex { int mode; int entry; - uint64_t mdat_size; - int ents_allocated; long timescale; long time; int64_t trackDuration; long sampleCount; - long sampleDuration; + long sampleSize; int hasKeyframes; int hasBframes; int language; int trackID; + int tag; AVCodecContext *enc; int vosLen; uint8_t *vosData; - MOVIentry** cluster; + MOVIentry *cluster; + int audio_vbr; } MOVTrack; typedef struct MOVContext { int mode; int64_t time; int nb_streams; - int mdat_written; offset_t mdat_pos; + uint64_t mdat_size; long timescale; MOVTrack tracks[MAX_STREAMS]; } MOVContext; -static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track); - -/* output language code from iso639 language name */ -extern int ff_mov_iso639_to_lang(const char *lang, int mp4); - //FIXME supprt 64bit varaint with wide placeholders static offset_t updateSize (ByteIOContext *pb, offset_t pos) { @@ -106,12 +104,10 @@ static int mov_write_stco_tag(ByteIOContext *pb, MOVTrack* track) put_be32(pb, 0); /* version & flags */ put_be32(pb, track->entry); /* entry count */ for (i=0; ientry; i++) { - int cl = i / MOV_INDEX_CLUSTER_SIZE; - int id = i % MOV_INDEX_CLUSTER_SIZE; if(mode64 == 1) - put_be64(pb, track->cluster[cl][id].pos); + put_be64(pb, track->cluster[i].pos); else - put_be32(pb, track->cluster[cl][id].pos); + put_be32(pb, track->cluster[i].pos); } return updateSize (pb, pos); } @@ -128,17 +124,15 @@ static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track) put_be32(pb, 0); /* version & flags */ for (i=0; ientry; i++) { - int cl = i / MOV_INDEX_CLUSTER_SIZE; - int id = i % MOV_INDEX_CLUSTER_SIZE; - tst = track->cluster[cl][id].size/track->cluster[cl][id].entries; + tst = track->cluster[i].size/track->cluster[i].entries; if(oldtst != -1 && tst != oldtst) { equalChunks = 0; } oldtst = tst; - entries += track->cluster[cl][id].entries; + entries += track->cluster[i].entries; } if (equalChunks) { - int sSize = track->cluster[0][0].size/track->cluster[0][0].entries; + int sSize = track->cluster[0].size/track->cluster[0].entries; put_be32(pb, sSize); // sample size put_be32(pb, entries); // sample count } @@ -146,11 +140,9 @@ static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track) put_be32(pb, 0); // sample size put_be32(pb, entries); // sample count for (i=0; ientry; i++) { - int cl = i / MOV_INDEX_CLUSTER_SIZE; - int id = i % MOV_INDEX_CLUSTER_SIZE; - for ( j=0; jcluster[cl][id].entries; j++) { - put_be32(pb, track->cluster[cl][id].size / - track->cluster[cl][id].entries); + for ( j=0; jcluster[i].entries; j++) { + put_be32(pb, track->cluster[i].size / + track->cluster[i].entries); } } } @@ -170,14 +162,12 @@ static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track) entryPos = url_ftell(pb); put_be32(pb, track->entry); // entry count for (i=0; ientry; i++) { - int cl = i / MOV_INDEX_CLUSTER_SIZE; - int id = i % MOV_INDEX_CLUSTER_SIZE; - if(oldval != track->cluster[cl][id].samplesInChunk) + if(oldval != track->cluster[i].samplesInChunk) { put_be32(pb, i+1); // first chunk - put_be32(pb, track->cluster[cl][id].samplesInChunk); // samples per chunk + put_be32(pb, track->cluster[i].samplesInChunk); // samples per chunk put_be32(pb, 0x1); // sample description index - oldval = track->cluster[cl][id].samplesInChunk; + oldval = track->cluster[i].samplesInChunk; index++; } } @@ -201,9 +191,7 @@ static int mov_write_stss_tag(ByteIOContext *pb, MOVTrack* track) entryPos = url_ftell(pb); put_be32(pb, track->entry); // entry count for (i=0; ientry; i++) { - int cl = i / MOV_INDEX_CLUSTER_SIZE; - int id = i % MOV_INDEX_CLUSTER_SIZE; - if(track->cluster[cl][id].key_frame == 1) { + if(track->cluster[i].key_frame == 1) { put_be32(pb, i+1); index++; } @@ -215,20 +203,95 @@ static int mov_write_stss_tag(ByteIOContext *pb, MOVTrack* track) return updateSize (pb, pos); } -static int mov_write_damr_tag(ByteIOContext *pb) +static int mov_write_amr_tag(ByteIOContext *pb, MOVTrack *track) { put_be32(pb, 0x11); /* size */ - put_tag(pb, "damr"); + if (track->mode == MODE_MOV) put_tag(pb, "samr"); + else put_tag(pb, "damr"); put_tag(pb, "FFMP"); - put_byte(pb, 0); + put_byte(pb, 0); /* decoder version */ - put_be16(pb, 0x80); /* Mode set (all modes for AMR_NB) */ - put_be16(pb, 0xa); /* Mode change period (no restriction) */ - //put_be16(pb, 0x81ff); /* Mode set (all modes for AMR_NB) */ - //put_be16(pb, 1); /* Mode change period (no restriction) */ + put_be16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */ + put_byte(pb, 0x00); /* Mode change period (no restriction) */ + put_byte(pb, 0x01); /* Frames per sample */ return 0x11; } +static int mov_write_enda_tag(ByteIOContext *pb) +{ + put_be32(pb, 10); + put_tag(pb, "enda"); + put_be16(pb, 1); /* little endian */ + return 10; +} + +static unsigned int descrLength(unsigned int len) +{ + int i; + for(i=1; len>>(7*i); i++); + return len + 1 + i; +} + +static void putDescr(ByteIOContext *pb, int tag, unsigned int size) +{ + int i= descrLength(size) - size - 2; + put_byte(pb, tag); + for(; i>0; i--) + put_byte(pb, (size>>(7*i)) | 0x80); + put_byte(pb, size & 0x7F); +} + +static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic +{ + offset_t pos = url_ftell(pb); + int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0; + + put_be32(pb, 0); // size + put_tag(pb, "esds"); + put_be32(pb, 0); // Version + + // ES descriptor + putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) + + descrLength(1)); + put_be16(pb, track->trackID); + put_byte(pb, 0x00); // flags (= no flags) + + // DecoderConfig descriptor + putDescr(pb, 0x04, 13 + decoderSpecificInfoLen); + + // Object type indication + 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) + if(track->enc->codec_type == CODEC_TYPE_AUDIO) + put_byte(pb, 0x15); // flags (= Audiostream) + else + put_byte(pb, 0x11); // flags (= Visualstream) + + put_byte(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits) + put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // Buffersize DB + + put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window) + if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0) + put_be32(pb, 0); // vbr + else + put_be32(pb, track->enc->rc_max_rate); // avg bitrate + + if (track->vosLen) + { + // DecoderSpecific info descriptor + putDescr(pb, 0x05, track->vosLen); + put_buffer(pb, track->vosData, track->vosLen); + } + + + // SL descriptor + putDescr(pb, 0x06, 1); + put_byte(pb, 0x02); + return updateSize (pb, pos); +} + static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track) { offset_t pos = url_ftell(pb); @@ -238,17 +301,20 @@ static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track) put_be32(pb, 12); /* size */ put_tag(pb, "frma"); - put_tag(pb, "mp4a"); - - put_be32(pb, 12); /* size */ - put_tag(pb, "mp4a"); - put_be32(pb, 0); + put_le32(pb, track->tag); - mov_write_esds_tag(pb, track); - - put_be32(pb, 12); /* size */ - put_tag(pb, "srcq"); - put_be32(pb, 0x40); + if (track->enc->codec_id == CODEC_ID_AAC) { + /* useless atom needed by mplayer, ipod, not needed by quicktime */ + put_be32(pb, 12); /* size */ + put_tag(pb, "mp4a"); + put_be32(pb, 0); + mov_write_esds_tag(pb, track); + } else if (track->enc->codec_id == CODEC_ID_PCM_S24LE || + track->enc->codec_id == CODEC_ID_PCM_S32LE) { + mov_write_enda_tag(pb); + } else if (track->enc->codec_id == CODEC_ID_AMR_NB) { + mov_write_amr_tag(pb, track); + } put_be32(pb, 8); /* size */ put_be32(pb, 0); /* null tag */ @@ -256,84 +322,61 @@ 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_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); - int tag; + int version = track->mode == MODE_MOV && + (track->audio_vbr || + track->enc->codec_id == CODEC_ID_PCM_S32LE || + track->enc->codec_id == CODEC_ID_PCM_S24LE); put_be32(pb, 0); /* size */ - - tag = track->enc->codec_tag; - if (!tag) - tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id); - // if no mac fcc found, try with Microsoft tags - if (!tag) - { - int tmp = codec_get_tag(codec_wav_tags, track->enc->codec_id); - tag = MKTAG('m', 's', ((tmp >> 8) & 0xff), (tmp & 0xff)); - } - put_le32(pb, tag); // store it byteswapped - + put_le32(pb, track->tag); // store it byteswapped put_be32(pb, 0); /* Reserved */ put_be16(pb, 0); /* Reserved */ put_be16(pb, 1); /* Data-reference index, XXX == 1 */ /* SoundDescription */ - if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) - put_be16(pb, 1); /* Version 1 */ - else - put_be16(pb, 0); /* Version 0 */ + put_be16(pb, version); /* Version */ put_be16(pb, 0); /* Revision level */ put_be32(pb, 0); /* Reserved */ - put_be16(pb, track->enc->channels); /* Number of channels */ - /* TODO: Currently hard-coded to 16-bit, there doesn't seem - to be a good way to get number of bits of audio */ - put_be16(pb, 0x10); /* Reserved */ - - if(track->enc->codec_id == CODEC_ID_AAC || - track->enc->codec_id == CODEC_ID_MP3) - { - put_be16(pb, 0xfffe); /* compression ID (vbr)*/ - } - else - { - put_be16(pb, 0); /* compression ID (= 0) */ + 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, 0); /* packet size (= 0) */ put_be16(pb, track->timescale); /* Time scale */ put_be16(pb, 0); /* Reserved */ - if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) - { - /* SoundDescription V1 extended info */ - put_be32(pb, track->enc->frame_size); /* Samples per packet */ - put_be32(pb, 1536); /* Bytes per packet */ - put_be32(pb, 2); /* Bytes per frame */ + if(version == 1) { /* SoundDescription V1 extended info */ + put_be32(pb, track->enc->frame_size); /* Samples per packet */ + put_be32(pb, track->sampleSize / track->enc->channels); /* Bytes per packet */ + put_be32(pb, track->sampleSize); /* Bytes per frame */ put_be32(pb, 2); /* Bytes per sample */ } - if(track->enc->codec_id == CODEC_ID_AAC) { - if( track->mode == MODE_MOV ) mov_write_wave_tag(pb, track); - else mov_write_esds_tag(pb, track); - } - if(track->enc->codec_id == CODEC_ID_AMR_NB) - mov_write_damr_tag(pb); + if(track->mode == MODE_MOV && + (track->enc->codec_id == CODEC_ID_AAC || + track->enc->codec_id == CODEC_ID_AMR_NB || + track->enc->codec_id == CODEC_ID_PCM_S24LE || + track->enc->codec_id == CODEC_ID_PCM_S32LE)) + mov_write_wave_tag(pb, track); + else if(track->enc->codec_id == CODEC_ID_AAC) + mov_write_esds_tag(pb, track); + else if(track->enc->codec_id == CODEC_ID_AMR_NB) + mov_write_amr_tag(pb, track); + return updateSize (pb, pos); } @@ -342,8 +385,10 @@ static int mov_write_d263_tag(ByteIOContext *pb) put_be32(pb, 0xf); /* size */ put_tag(pb, "d263"); put_tag(pb, "FFMP"); - put_be16(pb, 0x0a); - put_byte(pb, 0); + put_byte(pb, 0); /* decoder version */ + /* FIXME use AVCodecContext level/profile, when encoder will set values */ + put_byte(pb, 0xa); /* level */ + put_byte(pb, 0); /* profile */ return 0xf; } @@ -376,15 +421,15 @@ static uint8_t *avc_find_startcode( uint8_t *p, uint8_t *end ) if( (x - 0x01010101) & (~x) & 0x80808080 ) { // generic if( p[1] == 0 ) { if( p[0] == 0 && p[2] == 1 ) - return p; + return p-1; if( p[2] == 0 && p[3] == 1 ) - return p+1; + return p; } if( p[3] == 0 ) { if( p[2] == 0 && p[4] == 1 ) - return p+2; + return p+1; if( p[4] == 0 && p[5] == 1 ) - return p+3; + return p+2; } } } @@ -425,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; @@ -434,18 +479,11 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track) buf = track->vosData; end = track->vosData + track->vosLen; - put_byte(pb, 1); /* version */ - put_byte(pb, 77); /* profile */ - put_byte(pb, 64); /* profile compat */ - put_byte(pb, 30); /* level */ - put_byte(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */ - put_byte(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ - /* look for sps and pps */ 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; @@ -458,6 +496,14 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track) } assert(sps); assert(pps); + + put_byte(pb, 1); /* version */ + put_byte(pb, sps[1]); /* profile */ + put_byte(pb, sps[2]); /* profile compat */ + put_byte(pb, sps[3]); /* level */ + put_byte(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */ + put_byte(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ + put_be16(pb, sps_size); put_buffer(pb, sps, sps_size); put_byte(pb, 1); /* number of pps */ @@ -470,131 +516,9 @@ static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track) return updateSize(pb, pos); } -static unsigned int descrLength(unsigned int len) -{ - if (len < 0x00000080) - return 2 + len; - else if (len < 0x00004000) - return 3 + len; - else if(len < 0x00200000) - return 4 + len; - else - return 5 + len; -} - -static void putDescr(ByteIOContext *pb, int tag, int size) +static int mov_find_video_codec_tag(AVFormatContext *s, MOVTrack *track) { - uint32_t len; - uint8_t vals[4]; - - len = size; - vals[3] = (uint8_t)(len & 0x7f); - len >>= 7; - vals[2] = (uint8_t)((len & 0x7f) | 0x80); - len >>= 7; - vals[1] = (uint8_t)((len & 0x7f) | 0x80); - len >>= 7; - vals[0] = (uint8_t)((len & 0x7f) | 0x80); - - put_byte(pb, tag); // DescriptorTag - - if (size < 0x00000080) - { - put_byte(pb, vals[3]); - } - else if (size < 0x00004000) - { - put_byte(pb, vals[2]); - put_byte(pb, vals[3]); - } - else if (size < 0x00200000) - { - put_byte(pb, vals[1]); - put_byte(pb, vals[2]); - put_byte(pb, vals[3]); - } - else if (size < 0x10000000) - { - put_byte(pb, vals[0]); - put_byte(pb, vals[1]); - put_byte(pb, vals[2]); - put_byte(pb, vals[3]); - } -} - -static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic -{ - int decoderSpecificInfoLen; - offset_t pos = url_ftell(pb); - - decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0; - - put_be32(pb, 0); // size - put_tag(pb, "esds"); - put_be32(pb, 0); // Version - - // ES descriptor - putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) + - descrLength(1)); - put_be16(pb, track->trackID); - put_byte(pb, 0x00); // flags (= no flags) - - // DecoderConfig descriptor - putDescr(pb, 0x04, 13 + decoderSpecificInfoLen); - - // Object type indication - put_byte(pb, codec_get_tag(ff_mov_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) - if(track->enc->codec_type == CODEC_TYPE_AUDIO) - put_byte(pb, 0x15); // flags (= Audiostream) - else - put_byte(pb, 0x11); // flags (= Visualstream) - - put_byte(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits) - put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // Buffersize DB - - put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window) - if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0) - put_be32(pb, 0); // vbr - else - put_be32(pb, track->enc->rc_max_rate); // avg bitrate - - if (track->vosLen) - { - // DecoderSpecific info descriptor - putDescr(pb, 0x05, track->vosLen); - put_buffer(pb, track->vosData, track->vosLen); - } - - - // SL descriptor - putDescr(pb, 0x06, 1); - put_byte(pb, 0x02); - 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('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 }, -}; - -static int mov_find_video_codec_tag(MOVTrack* track) -{ - int tag; - - tag = track->enc->codec_tag; + int tag = track->enc->codec_tag; if (!tag) { if (track->enc->codec_id == CODEC_ID_DVVIDEO) { if (track->enc->height == 480) { /* NTSC */ @@ -610,13 +534,40 @@ static int mov_find_video_codec_tag(MOVTrack* track) else tag = MKTAG('d', 'v', 'p', 'p'); } + } else if (track->enc->codec_id == CODEC_ID_H263) { + if (track->mode == MODE_MOV) + tag = MKTAG('h', '2', '6', '3'); + else + tag = MKTAG('s', '2', '6', '3'); } else { tag = codec_get_tag(codec_movvideo_tags, track->enc->codec_id); } } // if no mac fcc found, try with Microsoft tags - if (!tag) + if (!tag) { tag = codec_get_tag(codec_bmp_tags, track->enc->codec_id); + if (tag) { + av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n"); + } + } + assert(tag); + return tag; +} + +static int mov_find_audio_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->enc->codec_tag; + if (!tag) { + tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id); + } + // if no mac fcc found, try with Microsoft tags + if (!tag) { + int ms_tag = codec_get_tag(codec_wav_tags, track->enc->codec_id); + if (ms_tag) { + tag = MKTAG('m', 's', ((ms_tag >> 8) & 0xff), (ms_tag & 0xff)); + av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n"); + } + } assert(tag); return tag; } @@ -625,26 +576,28 @@ static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track) { offset_t pos = url_ftell(pb); char compressor_name[32]; - int tag; put_be32(pb, 0); /* size */ - - tag = mov_find_video_codec_tag(track); - put_le32(pb, tag); // store it byteswapped - + put_le32(pb, track->tag); // store it byteswapped put_be32(pb, 0); /* Reserved */ put_be16(pb, 0); /* Reserved */ put_be16(pb, 1); /* Data-reference index */ put_be16(pb, 0); /* Codec stream version */ put_be16(pb, 0); /* Codec stream revision (=0) */ - put_tag(pb, "FFMP"); /* Vendor */ - if(track->enc->codec_id == CODEC_ID_RAWVIDEO) { - put_be32(pb, 0); /* Temporal Quality */ - put_be32(pb, 0x400); /* Spatial Quality = lossless*/ + if (track->mode == MODE_MOV) { + put_tag(pb, "FFMP"); /* Vendor */ + if(track->enc->codec_id == CODEC_ID_RAWVIDEO) { + put_be32(pb, 0); /* Temporal Quality */ + put_be32(pb, 0x400); /* Spatial Quality = lossless*/ + } else { + put_be32(pb, 0x200); /* Temporal Quality = normal */ + put_be32(pb, 0x200); /* Spatial Quality = normal */ + } } else { - put_be32(pb, 0x200); /* Temporal Quality = normal */ - put_be32(pb, 0x200); /* Spatial Quality = normal */ + put_be32(pb, 0); /* Reserved */ + put_be32(pb, 0); /* Reserved */ + put_be32(pb, 0); /* Reserved */ } put_be16(pb, track->enc->width); /* Video width */ put_be16(pb, track->enc->height); /* Video height */ @@ -654,7 +607,8 @@ static int mov_write_video_tag(ByteIOContext *pb, MOVTrack* track) put_be16(pb, 1); /* Frame count (= 1) */ memset(compressor_name,0,32); - if (track->enc->codec && track->enc->codec->name) + /* FIXME not sure, ISO 14496-1 draft where it shall be set to 0 */ + if (track->mode == MODE_MOV && track->enc->codec && track->enc->codec->name) strncpy(compressor_name,track->enc->codec->name,31); put_byte(pb, strlen(compressor_name)); put_buffer(pb, compressor_name, 31); @@ -689,22 +643,20 @@ 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; ctts_entries = av_malloc((track->entry + 1) * sizeof(*ctts_entries)); /* worst case */ ctts_entries[0].count = 1; - ctts_entries[0].duration = track->cluster[0][0].cts; + ctts_entries[0].duration = track->cluster[0].cts; for (i=1; ientry; i++) { - int cl = i / MOV_INDEX_CLUSTER_SIZE; - int id = i % MOV_INDEX_CLUSTER_SIZE; - if (track->cluster[cl][id].cts == ctts_entries[entries].duration) { + if (track->cluster[i].cts == ctts_entries[entries].duration) { ctts_entries[entries].count++; /* compress */ } else { entries++; - ctts_entries[entries].duration = track->cluster[cl][id].cts; + ctts_entries[entries].duration = track->cluster[i].cts; ctts_entries[entries].count = 1; } } @@ -722,18 +674,46 @@ static int mov_write_ctts_tag(ByteIOContext *pb, MOVTrack* track) return atom_size; } -/* TODO: */ /* Time to sample atom */ static int mov_write_stts_tag(ByteIOContext *pb, MOVTrack* track) { - put_be32(pb, 0x18); /* size */ + MOV_stts_t *stts_entries; + uint32_t entries = -1; + uint32_t atom_size; + int i; + + if (track->enc->codec_type == CODEC_TYPE_AUDIO && !track->audio_vbr) { + stts_entries = av_malloc(sizeof(*stts_entries)); /* one entry */ + stts_entries[0].count = track->sampleCount; + stts_entries[0].duration = 1; + entries = 1; + } else { + stts_entries = av_malloc(track->entry * sizeof(*stts_entries)); /* worst case */ + for (i=0; ientry; i++) { + int64_t duration = i + 1 == track->entry ? + track->trackDuration - track->cluster[i].dts + track->cluster[0].dts : /* readjusting */ + track->cluster[i+1].dts - track->cluster[i].dts; + if (i && duration == stts_entries[entries].duration) { + stts_entries[entries].count++; /* compress */ + } else { + entries++; + stts_entries[entries].duration = duration; + stts_entries[entries].count = 1; + } + } + entries++; /* last one */ + } + atom_size = 16 + (entries * 8); + put_be32(pb, atom_size); /* size */ put_tag(pb, "stts"); put_be32(pb, 0); /* version & flags */ - put_be32(pb, 1); /* entry count */ - - put_be32(pb, track->sampleCount); /* sample count */ - put_be32(pb, track->sampleDuration); /* sample duration */ - return 0x18; + put_be32(pb, entries); /* entry count */ + for (i=0; i data handler */ @@ -946,10 +926,7 @@ static int mov_write_edts_tag(ByteIOContext *pb, MOVTrack *track) put_be32(pb, av_rescale_rnd(track->trackDuration, globalTimescale, track->timescale, AV_ROUND_UP)); /* duration ... doesn't seem to effect psp */ - if (track->hasBframes) - put_be32(pb, track->sampleDuration); /* first pts is 1 */ - else - put_be32(pb, 0); + put_be32(pb, track->cluster[0].cts); /* first pts is cts since dts is 0 */ put_be32(pb, 0x00010000); return 0x24; } @@ -1009,7 +986,7 @@ static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov) int64_t maxTrackLenTemp, maxTrackLen = 0; int version; - for (i=0; inb_streams; i++) { if(mov->tracks[i].entry > 0) { maxTrackLenTemp = av_rescale_rnd(mov->tracks[i].trackDuration, globalTimescale, mov->tracks[i].timescale, AV_ROUND_UP); if(maxTrackLen < maxTrackLenTemp) @@ -1078,135 +1055,45 @@ static int mov_write_itunes_hdlr_tag(ByteIOContext *pb, MOVContext* mov, } /* helper function to write a data tag with the specified string as data */ -static int mov_write_string_data_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s, const char *data) +static int mov_write_string_data_tag(ByteIOContext *pb, const char *data, int long_style) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "data"); - put_be32(pb, 1); - put_be32(pb, 0); - put_buffer(pb, data, strlen(data)); - return updateSize(pb, pos); -} - -/* iTunes name of the song/movie */ -static int mov_write_nam_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ - int size = 0; - if ( s->title[0] ) { + if(long_style){ offset_t pos = url_ftell(pb); put_be32(pb, 0); /* size */ - put_tag(pb, "\251nam"); - mov_write_string_data_tag(pb, mov, s, s->title); - size = updateSize(pb, pos); - } - return size; -} - -/* iTunes name of the artist/performer */ -static int mov_write_ART_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ - int size = 0; - if ( s->author[0] ) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251ART"); - // we use the author here as this is the only thing that we have... - mov_write_string_data_tag(pb, mov, s, s->author); - size = updateSize(pb, pos); - } - return size; -} - -/* iTunes name of the writer */ -static int mov_write_wrt_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ - int size = 0; - if ( s->author[0] ) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251wrt"); - mov_write_string_data_tag(pb, mov, s, s->author); - size = updateSize(pb, pos); + put_tag(pb, "data"); + put_be32(pb, 1); + put_be32(pb, 0); + put_buffer(pb, data, strlen(data)); + return updateSize(pb, pos); + }else{ + put_be16(pb, strlen(data)); /* string length */ + put_be16(pb, 0); + put_buffer(pb, data, strlen(data)); + return strlen(data) + 4; } - return size; } -/* iTunes name of the album */ -static int mov_write_alb_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ +static int mov_write_string_tag(ByteIOContext *pb, const char *name, const char *value, int long_style){ int size = 0; - if ( s->album[0] ) { + if ( value && value[0] ) { offset_t pos = url_ftell(pb); put_be32(pb, 0); /* size */ - put_tag(pb, "\251alb"); - mov_write_string_data_tag(pb, mov, s, s->album); - size = updateSize(pb, pos); + put_tag(pb, name); + mov_write_string_data_tag(pb, value, long_style); + size= updateSize(pb, pos); } return size; } /* iTunes year */ -static int mov_write_day_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ - char year[5]; - int size = 0; - if ( s->year ) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251day"); - snprintf(year, 5, "%04d", s->year); - mov_write_string_data_tag(pb, mov, s, year); - size = updateSize(pb, pos); - } - return size; -} - -/* iTunes tool used to create the file */ -static int mov_write_too_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251too"); - mov_write_string_data_tag(pb, mov, s, LIBAVFORMAT_IDENT); - return updateSize(pb, pos); -} - -/* iTunes comment */ -static int mov_write_cmt_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) -{ - int size = 0; - if ( s->comment[0] ) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251cmt"); - mov_write_string_data_tag(pb, mov, s, s->comment); - size = updateSize(pb, pos); - } - return size; -} - -/* iTunes custom genre */ -static int mov_write_gen_tag(ByteIOContext *pb, MOVContext* mov, - AVFormatContext *s) +static int mov_write_day_tag(ByteIOContext *pb, int year, int long_style) { - int size = 0; - if ( s->genre[0] ) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251gen"); - mov_write_string_data_tag(pb, mov, s, s->genre); - size = updateSize(pb, pos); - } - return size; + if(year){ + char year_str[5]; + snprintf(year_str, sizeof(year_str), "%04d", year); + return mov_write_string_tag(pb, "\251day", year_str, long_style); + }else + return 0; } /* iTunes track number */ @@ -1242,14 +1129,15 @@ static int mov_write_ilst_tag(ByteIOContext *pb, MOVContext* mov, offset_t pos = url_ftell(pb); put_be32(pb, 0); /* size */ put_tag(pb, "ilst"); - mov_write_nam_tag(pb, mov, s); - mov_write_ART_tag(pb, mov, s); - mov_write_wrt_tag(pb, mov, s); - mov_write_alb_tag(pb, mov, s); - mov_write_day_tag(pb, mov, s); - mov_write_too_tag(pb, mov, s); - mov_write_cmt_tag(pb, mov, s); - mov_write_gen_tag(pb, mov, s); + mov_write_string_tag(pb, "\251nam", s->title , 1); + mov_write_string_tag(pb, "\251ART", s->author , 1); + mov_write_string_tag(pb, "\251wrt", s->author , 1); + mov_write_string_tag(pb, "\251alb", s->album , 1); + mov_write_day_tag(pb, s->year ,1); + if(mov->tracks[0].enc && !(mov->tracks[0].enc->flags & CODEC_FLAG_BITEXACT)) + mov_write_string_tag(pb, "\251too", LIBAVFORMAT_IDENT, 1); + mov_write_string_tag(pb, "\251cmt", s->comment , 1); + mov_write_string_tag(pb, "\251gen", s->genre , 1); mov_write_trkn_tag(pb, mov, s); return updateSize(pb, pos); } @@ -1286,89 +1174,54 @@ 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 /* 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) { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251req"); - put_be16(pb, sizeof("QuickTime 6.0 or greater") - 1); - put_be16(pb, 0); - put_buffer(pb, "QuickTime 6.0 or greater", - sizeof("QuickTime 6.0 or greater") - 1); - updateSize(pb, pos); + mov_write_string_tag(pb, "\251req", "QuickTime 6.0 or greater", 0); break; } } - /* Encoder */ + 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)) - { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251enc"); - put_be16(pb, sizeof(LIBAVFORMAT_IDENT) - 1); /* string length */ - put_be16(pb, 0); - put_buffer(pb, LIBAVFORMAT_IDENT, sizeof(LIBAVFORMAT_IDENT) - 1); - updateSize(pb, pos); - } - - if( s->title[0] ) - { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251nam"); - put_be16(pb, strlen(s->title)); /* string length */ - put_be16(pb, 0); - put_buffer(pb, s->title, strlen(s->title)); - updateSize(pb, pos); - } - - if( s->author[0] ) - { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, /*"\251aut"*/ "\251day" ); - put_be16(pb, strlen(s->author)); /* string length */ - put_be16(pb, 0); - put_buffer(pb, s->author, strlen(s->author)); - updateSize(pb, pos); - } - - if( s->comment[0] ) - { - offset_t pos = url_ftell(pb); - put_be32(pb, 0); /* size */ - put_tag(pb, "\251des"); - put_be16(pb, strlen(s->comment)); /* string length */ - put_be16(pb, 0); - put_buffer(pb, s->comment, strlen(s->comment)); - updateSize(pb, pos); - } + 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); } +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 (char *str) +static uint16_t language_code (const char *str) { - return ((((str[0]-'a') & 0x1F)<<10) + (((str[1]-'a') & 0x1F)<<5) + ((str[2]-'a') & 0x1F)); + return ((((str[0]-0x60) & 0x1F)<<10) + (((str[1]-0x60) & 0x1F)<<5) + ((str[2]-0x60) & 0x1F)); } static int mov_write_uuidusmt_tag (ByteIOContext *pb, AVFormatContext *s) @@ -1389,16 +1242,49 @@ static int mov_write_uuidusmt_tag (ByteIOContext *pb, AVFormatContext *s) put_be32(pb, 0); /* size placeholder*/ put_tag(pb, "MTDT"); - put_be16(pb, 1); + put_be16(pb, 4); size += 10; + // ? + put_be16(pb, 0x0C); /* size */ + put_be32(pb, 0x0B); /* type */ + put_be16(pb, language_code("und")); /* language */ + put_be16(pb, 0x0); /* ? */ + put_be16(pb, 0x021C); /* data */ + size += 12; + + // Encoder + 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); + 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("und")); /* language */ + 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 = 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"); size += len*2+10; // size @@ -1411,6 +1297,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, @@ -1422,34 +1311,16 @@ static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov, put_tag(pb, "moov"); mov->timescale = globalTimescale; - for (i=0; inb_streams; i++) { if(mov->tracks[i].entry <= 0) continue; - if(mov->tracks[i].enc->codec_type == CODEC_TYPE_VIDEO) { - mov->tracks[i].timescale = mov->tracks[i].enc->time_base.den; - mov->tracks[i].sampleDuration = mov->tracks[i].enc->time_base.num; - } - else if(mov->tracks[i].enc->codec_type == CODEC_TYPE_AUDIO) { - /* If AMR, track timescale = 8000, AMR_WB = 16000 */ - if(mov->tracks[i].enc->codec_id == CODEC_ID_AMR_NB) { - mov->tracks[i].sampleDuration = 160; // Bytes per chunk - mov->tracks[i].timescale = 8000; - } - else { - mov->tracks[i].timescale = mov->tracks[i].enc->sample_rate; - mov->tracks[i].sampleDuration = mov->tracks[i].enc->frame_size; - } - } - - mov->tracks[i].trackDuration = - (int64_t)mov->tracks[i].sampleCount * mov->tracks[i].sampleDuration; mov->tracks[i].time = mov->time; mov->tracks[i].trackID = i+1; } mov_write_mvhd_tag(pb, mov); //mov_write_iods_tag(pb, mov); - for (i=0; inb_streams; i++) { if(mov->tracks[i].entry > 0) { mov_write_trak_tag(pb, &(mov->tracks[i])); } @@ -1458,15 +1329,15 @@ 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); + mov_write_udta_tag(pb, mov, s); return updateSize(pb, pos); } -int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov) +static int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov) { put_be32(pb, 8); // placeholder for extended size field (64 bit) - put_tag(pb, "wide"); + put_tag(pb, mov->mode == MODE_MOV ? "wide" : "free"); mov->mdat_pos = url_ftell(pb); put_be32(pb, 0); /* size placeholder*/ @@ -1594,31 +1465,34 @@ static int mov_write_header(AVFormatContext *s) } for(i=0; inb_streams; i++){ - AVCodecContext *c= s->streams[i]->codec; - - if(c->codec_type == CODEC_TYPE_VIDEO){ - av_set_pts_info(s->streams[i], 64, 1, c->time_base.den); - if (!codec_get_tag(codec_movvideo_tags, c->codec_id)){ - if(!codec_get_tag(codec_bmp_tags, c->codec_id)) - return -1; - else - av_log(s, AV_LOG_INFO, "Warning, using MS style video codec tag, the file may be unplayable!\n"); - } - }else if(c->codec_type == CODEC_TYPE_AUDIO){ - av_set_pts_info(s->streams[i], 64, 1, c->sample_rate); - if (!codec_get_tag(codec_movaudio_tags, c->codec_id)){ - if(!codec_get_tag(codec_wav_tags, c->codec_id)) - return -1; - else - av_log(s, AV_LOG_INFO, "Warning, using MS style audio codec tag, the file may be unplayable!\n"); + AVStream *st= s->streams[i]; + MOVTrack *track= &mov->tracks[i]; + + track->enc = st->codec; + track->language = ff_mov_iso639_to_lang(st->language, mov->mode != MODE_MOV); + track->mode = mov->mode; + if(st->codec->codec_type == CODEC_TYPE_VIDEO){ + track->tag = mov_find_video_codec_tag(s, track); + track->timescale = st->codec->time_base.den; + av_set_pts_info(st, 64, 1, st->codec->time_base.den); + }else if(st->codec->codec_type == CODEC_TYPE_AUDIO){ + track->tag = mov_find_audio_codec_tag(s, track); + track->timescale = st->codec->sample_rate; + av_set_pts_info(st, 64, 1, st->codec->sample_rate); + 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; + }else{ + track->sampleSize = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels; } } - mov->tracks[i].language = ff_mov_iso639_to_lang(s->streams[i]->language, mov->mode != MODE_MOV); } - for (i=0; itracks[i].mode = mov->mode; - } + mov_write_mdat_tag(pb, mov); + mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based + mov->nb_streams = s->nb_streams; put_flush_packet(pb); @@ -1629,100 +1503,72 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) { MOVContext *mov = s->priv_data; ByteIOContext *pb = &s->pb; - AVCodecContext *enc = s->streams[pkt->stream_index]->codec; - MOVTrack* trk = &mov->tracks[pkt->stream_index]; - int cl, id; + MOVTrack *trk = &mov->tracks[pkt->stream_index]; + AVCodecContext *enc = trk->enc; unsigned int samplesInChunk = 0; int size= pkt->size; if (url_is_streamed(&s->pb)) return 0; /* Can't handle that */ if (!size) return 0; /* Discard 0 sized packets */ - if (enc->codec_type == CODEC_TYPE_VIDEO ) { - samplesInChunk = 1; - } - else if (enc->codec_type == CODEC_TYPE_AUDIO ) { - if( enc->codec_id == CODEC_ID_AMR_NB) { - /* We must find out how many AMR blocks there are in one packet */ - static uint16_t packed_size[16] = - {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0}; - int len = 0; - - while (len < size && samplesInChunk < 100) { - len += packed_size[(pkt->data[len] >> 3) & 0x0F]; - samplesInChunk++; - } - } - else if(enc->codec_id == CODEC_ID_PCM_ALAW) { - samplesInChunk = size/enc->channels; - } - else if(enc->codec_id == CODEC_ID_PCM_S16BE || enc->codec_id == CODEC_ID_PCM_S16LE) { - samplesInChunk = size/(2*enc->channels); + if (enc->codec_id == CODEC_ID_AMR_NB) { + /* We must find out how many AMR blocks there are in one packet */ + static uint16_t packed_size[16] = + {13, 14, 16, 18, 20, 21, 27, 32, 6, 0, 0, 0, 0, 0, 0, 0}; + int len = 0; + + while (len < size && samplesInChunk < 100) { + len += packed_size[(pkt->data[len] >> 3) & 0x0F]; + samplesInChunk++; } - else { - samplesInChunk = 1; + if(samplesInChunk > 1){ + av_log(s, AV_LOG_ERROR, "fatal error, input is not a single packet, inplement a AVParser for it\n"); + return -1; } - } - - if ((enc->codec_id == CODEC_ID_MPEG4 || enc->codec_id == CODEC_ID_AAC) - && trk->vosLen == 0) { -// assert(enc->extradata_size); + } else if (trk->sampleSize) + samplesInChunk = size/trk->sampleSize; + else + samplesInChunk = 1; + /* copy extradata if it exists */ + if (trk->vosLen == 0 && enc->extradata_size > 0) { trk->vosLen = enc->extradata_size; trk->vosData = av_malloc(trk->vosLen); memcpy(trk->vosData, enc->extradata, trk->vosLen); } - if (enc->codec_id == CODEC_ID_H264) { - if (trk->vosLen == 0) { - /* copy extradata */ - trk->vosLen = enc->extradata_size; - trk->vosData = av_malloc(trk->vosLen); - memcpy(trk->vosData, enc->extradata, trk->vosLen); - } - if (*(uint8_t *)trk->vosData != 1) { - /* from x264 or from bytestream h264 */ - /* nal reformating needed */ - avc_parse_nal_units(&pkt->data, &pkt->size); - assert(pkt->size); - size = pkt->size; - } + if (enc->codec_id == CODEC_ID_H264 && trk->vosLen > 0 && *(uint8_t *)trk->vosData != 1) { + /* from x264 or from bytestream h264 */ + /* nal reformating needed */ + avc_parse_nal_units(&pkt->data, &pkt->size); + assert(pkt->size); + size = pkt->size; } - cl = trk->entry / MOV_INDEX_CLUSTER_SIZE; - id = trk->entry % MOV_INDEX_CLUSTER_SIZE; - - if (trk->ents_allocated <= trk->entry) { - trk->cluster = av_realloc(trk->cluster, (cl+1)*sizeof(void*)); + if (!(trk->entry % MOV_INDEX_CLUSTER_SIZE)) { + trk->cluster = av_realloc(trk->cluster, (trk->entry + MOV_INDEX_CLUSTER_SIZE) * sizeof(*trk->cluster)); if (!trk->cluster) return -1; - trk->cluster[cl] = av_malloc(MOV_INDEX_CLUSTER_SIZE*sizeof(MOVIentry)); - if (!trk->cluster[cl]) - return -1; - trk->ents_allocated += MOV_INDEX_CLUSTER_SIZE; - } - if (mov->mdat_written == 0) { - mov_write_mdat_tag(pb, mov); - mov->mdat_written = 1; - mov->time = s->timestamp + 0x7C25B080; //1970 based -> 1904 based } - trk->cluster[cl][id].pos = url_ftell(pb); - trk->cluster[cl][id].samplesInChunk = samplesInChunk; - trk->cluster[cl][id].size = size; - trk->cluster[cl][id].entries = samplesInChunk; + trk->cluster[trk->entry].pos = url_ftell(pb); + trk->cluster[trk->entry].samplesInChunk = samplesInChunk; + trk->cluster[trk->entry].size = size; + trk->cluster[trk->entry].entries = samplesInChunk; + trk->cluster[trk->entry].dts = pkt->dts; + trk->trackDuration = pkt->dts - trk->cluster[0].dts + pkt->duration; + if(enc->codec_type == CODEC_TYPE_VIDEO) { if (pkt->dts != pkt->pts) trk->hasBframes = 1; - trk->cluster[cl][id].cts = pkt->pts - pkt->dts; - trk->cluster[cl][id].key_frame = !!(pkt->flags & PKT_FLAG_KEY); - if(trk->cluster[cl][id].key_frame) + trk->cluster[trk->entry].cts = pkt->pts - pkt->dts; + trk->cluster[trk->entry].key_frame = !!(pkt->flags & PKT_FLAG_KEY); + if(trk->cluster[trk->entry].key_frame) trk->hasKeyframes++; } - trk->enc = enc; trk->entry++; trk->sampleCount += samplesInChunk; - trk->mdat_size += size; + mov->mdat_size += size; put_buffer(pb, pkt->data, size); @@ -1736,39 +1582,29 @@ static int mov_write_trailer(AVFormatContext *s) ByteIOContext *pb = &s->pb; int res = 0; int i; - uint64_t j; offset_t moov_pos = url_ftell(pb); /* Write size of mdat tag */ - for (i=0, j=0; itracks[i].ents_allocated > 0) { - j += mov->tracks[i].mdat_size; - } - } - if (j+8 <= UINT32_MAX) { + if (mov->mdat_size+8 <= UINT32_MAX) { url_fseek(pb, mov->mdat_pos, SEEK_SET); - put_be32(pb, j+8); + put_be32(pb, mov->mdat_size+8); } else { /* overwrite 'wide' placeholder atom */ url_fseek(pb, mov->mdat_pos - 8, SEEK_SET); put_be32(pb, 1); /* special value: real atom size will be 64 bit value after tag field */ put_tag(pb, "mdat"); - put_be64(pb, j+16); + put_be64(pb, mov->mdat_size+16); } url_fseek(pb, moov_pos, SEEK_SET); mov_write_moov_tag(pb, mov, s); - for (i=0; itracks[i].ents_allocated/MOV_INDEX_CLUSTER_SIZE; j++) { - av_free(mov->tracks[i].cluster[j]); - } - av_free(mov->tracks[i].cluster); + for (i=0; inb_streams; i++) { + av_freep(&mov->tracks[i].cluster); + if( mov->tracks[i].vosLen ) av_free( mov->tracks[i].vosData ); - mov->tracks[i].cluster = NULL; - mov->tracks[i].ents_allocated = mov->tracks[i].entry = 0; } put_flush_packet(pb); @@ -1776,7 +1612,8 @@ static int mov_write_trailer(AVFormatContext *s) return res; } -static AVOutputFormat mov_oformat = { +#ifdef CONFIG_MOV_MUXER +AVOutputFormat mov_muxer = { "mov", "mov format", NULL, @@ -1789,8 +1626,9 @@ static AVOutputFormat mov_oformat = { mov_write_trailer, .flags = AVFMT_GLOBALHEADER, }; - -static AVOutputFormat _3gp_oformat = { +#endif +#ifdef CONFIG_TGP_MUXER +AVOutputFormat tgp_muxer = { "3gp", "3gp format", NULL, @@ -1803,8 +1641,9 @@ static AVOutputFormat _3gp_oformat = { mov_write_trailer, .flags = AVFMT_GLOBALHEADER, }; - -static AVOutputFormat mp4_oformat = { +#endif +#ifdef CONFIG_MP4_MUXER +AVOutputFormat mp4_muxer = { "mp4", "mp4 format", "application/mp4", @@ -1817,8 +1656,9 @@ static AVOutputFormat mp4_oformat = { mov_write_trailer, .flags = AVFMT_GLOBALHEADER, }; - -static AVOutputFormat psp_oformat = { +#endif +#ifdef CONFIG_PSP_MUXER +AVOutputFormat psp_muxer = { "psp", "psp mp4 format", NULL, @@ -1831,8 +1671,9 @@ static AVOutputFormat psp_oformat = { mov_write_trailer, .flags = AVFMT_GLOBALHEADER, }; - -static AVOutputFormat _3g2_oformat = { +#endif +#ifdef CONFIG_TG2_MUXER +AVOutputFormat tg2_muxer = { "3g2", "3gp2 format", NULL, @@ -1845,13 +1686,4 @@ static AVOutputFormat _3g2_oformat = { mov_write_trailer, .flags = AVFMT_GLOBALHEADER, }; - -int movenc_init(void) -{ - av_register_output_format(&mov_oformat); - av_register_output_format(&_3gp_oformat); - av_register_output_format(&mp4_oformat); - av_register_output_format(&psp_oformat); - av_register_output_format(&_3g2_oformat); - return 0; -} +#endif