X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmovenc.c;h=0d1b8f8ae3fc457c0c20ff5de0a6bd6a7551d4d1;hb=ac66834c759b7130fb5be51f63cb6dff9b294cba;hp=f14171bf6370505b90f2445fb2b215ebb2bfa336;hpb=915282e5b81e5f9ac92d91da8bcdf73e6f7fe819;p=ffmpeg diff --git a/libavformat/movenc.c b/libavformat/movenc.c index f14171bf637..0d1b8f8ae3f 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1,20 +1,22 @@ /* - * 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" @@ -42,6 +44,7 @@ typedef struct MOVIentry { char key_frame; unsigned int entries; int64_t cts; + int64_t dts; } MOVIentry; typedef struct MOVIndex { @@ -51,7 +54,6 @@ typedef struct MOVIndex { long time; int64_t trackDuration; long sampleCount; - long sampleDuration; long sampleSize; int hasKeyframes; int hasBframes; @@ -63,6 +65,7 @@ typedef struct MOVIndex { int vosLen; uint8_t *vosData; MOVIentry *cluster; + int audio_vbr; } MOVTrack; typedef struct MOVContext { @@ -341,13 +344,10 @@ static const CodecTag codec_movaudio_tags[] = { static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) { offset_t pos = url_ftell(pb); - int vbr= track->mode == MODE_MOV && - (track->enc->codec_id == CODEC_ID_AAC || - track->enc->codec_id == CODEC_ID_MP3 || - track->enc->codec_id == CODEC_ID_AMR_NB); - int version = vbr || - track->enc->codec_id == CODEC_ID_PCM_S32LE || - track->enc->codec_id == CODEC_ID_PCM_S24LE; + 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 */ put_le32(pb, track->tag); // store it byteswapped @@ -364,14 +364,12 @@ static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) /* FIXME 8 bit for 'raw ' in mov */ put_be16(pb, 16); /* Reserved */ - put_be16(pb, vbr ? 0xfffe : 0); /* compression ID */ + 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 */ if(version == 1) { /* SoundDescription V1 extended info */ - if (vbr) - track->sampleSize = 0; 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 */ @@ -397,8 +395,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; } @@ -530,6 +530,7 @@ 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 */ @@ -559,6 +560,11 @@ static int mov_find_video_codec_tag(AVFormatContext *s, 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); } @@ -694,18 +700,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 */ + Time2Sample *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; itrackDuration, 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; } @@ -1193,19 +1224,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) @@ -1243,31 +1280,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 @@ -1280,6 +1323,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, @@ -1294,16 +1340,6 @@ static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov, 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) { - 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; } @@ -1319,7 +1355,7 @@ 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); } @@ -1327,7 +1363,7 @@ static int mov_write_moov_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*/ @@ -1459,16 +1495,31 @@ static int mov_write_header(AVFormatContext *s) 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); - track->sampleSize = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels; + switch(track->enc->codec_id){ + case CODEC_ID_MP3: + case CODEC_ID_AAC: + case CODEC_ID_AMR_NB: + case CODEC_ID_AMR_WB: + track->audio_vbr = 1; + break; + default: + track->sampleSize = (av_get_bits_per_sample(st->codec->codec_id) >> 3) * st->codec->channels; + } + if (!st->codec->frame_size) { + av_log(s, AV_LOG_ERROR, "track %d: codec frame size is not set\n", i); + return -1; + } } - track->language = ff_mov_iso639_to_lang(st->language, mov->mode != MODE_MOV); - track->mode = mov->mode; } mov_write_mdat_tag(pb, mov); @@ -1536,6 +1587,9 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) 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;