From 69dde1ad36b7d95b8b9268f414aa6c076212ed41 Mon Sep 17 00:00:00 2001 From: Gildas Bazin Date: Sat, 14 Feb 2004 19:08:09 +0000 Subject: [PATCH] AAC support in mov, correct aspect ratio support and user data support patch by (Gildas Bazin ) and regression test checksum update for it by me Originally committed as revision 2787 to svn://svn.ffmpeg.org/ffmpeg/trunk --- libavformat/movenc.c | 182 ++++++++++++++++++++++++++++++---- tests/ffmpeg.regression.ref | 2 +- tests/rotozoom.regression.ref | 2 +- 3 files changed, 162 insertions(+), 24 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index 104f176716..c5129ea08d 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -1,7 +1,7 @@ /* * MOV, 3GP, MP4 encoder. * Copyright (c) 2003 Thomas Raivio. - * Enhancements by Gildas Bazin + * Copyright (c) 2004 Gildas Bazin . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,6 +27,10 @@ #define MOV_INDEX_CLUSTER_SIZE 16384 #define globalTimescale 1000 +#define MODE_MP4 0 +#define MODE_MOV 1 +#define MODE_3GP 2 + typedef struct MOVIentry { unsigned int flags, pos, size; unsigned int samplesInChunk; @@ -35,6 +39,7 @@ typedef struct MOVIentry { } MOVIentry; typedef struct MOVIndex { + int mode; int entry; int mdat_size; int ents_allocated; @@ -53,11 +58,11 @@ typedef struct MOVIndex { } MOVTrack; typedef struct { + int mode; long time; int nb_streams; int mdat_written; offset_t mdat_pos; - offset_t movi_list; long timescale; MOVTrack tracks[MAX_STREAMS]; } MOVContext; @@ -204,6 +209,33 @@ static int mov_write_damr_tag(ByteIOContext *pb) return 0x11; } +static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track) +{ + int pos = url_ftell(pb); + + put_be32(pb, 0); /* size */ + put_tag(pb, "wave"); + + 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); + + mov_write_esds_tag(pb, track); + + put_be32(pb, 12); /* size */ + put_tag(pb, "srcq"); + put_be32(pb, 0x40); + + put_be32(pb, 8); /* size */ + put_be32(pb, 0); /* null tag */ + + return updateSize (pb, pos); +} + static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) { int pos = url_ftell(pb); @@ -229,8 +261,12 @@ static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) put_be32(pb, 0); /* Reserved */ put_be16(pb, 0); /* Reserved */ put_be16(pb, 1); /* Data-reference index, XXX == 1 */ + /* SoundDescription */ - put_be16(pb, 0); /* Version */ + 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, 0); /* Revision level */ put_be32(pb, 0); /* Reserved */ @@ -238,13 +274,24 @@ static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track) /* 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 */ - put_be16(pb, 0); /* compression ID (= 0) */ - put_be16(pb, 0); /* packet size (= 0) */ + put_be16(pb, 0xfffe); /* compression ID (= 0) */ + put_be16(pb, 0xac); /* packet size (= 0) */ put_be16(pb, track->timescale); /* Time scale */ put_be16(pb, 0); /* Reserved */ - if(track->enc->codec_id == CODEC_ID_AAC) - mov_write_esds_tag(pb, track); + 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 */ + 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); return updateSize (pb, pos); @@ -608,7 +655,9 @@ static int mov_write_tkhd_tag(ByteIOContext *pb, MOVTrack* track) /* Track width and height, for visual only */ if(track->enc->codec_type == CODEC_TYPE_VIDEO) { - put_be32(pb, track->enc->width*0x10000); + double sample_aspect_ratio = av_q2d(track->enc->sample_aspect_ratio); + if( !sample_aspect_ratio ) sample_aspect_ratio = 1; + put_be32(pb, sample_aspect_ratio * track->enc->width*0x10000); put_be32(pb, track->enc->height*0x10000); } else { @@ -691,7 +740,81 @@ static int mov_write_mvhd_tag(ByteIOContext *pb, MOVContext *mov) return 0x6c; } -static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov) +static int mov_write_udta_tag(ByteIOContext *pb, MOVContext* mov, + AVFormatContext *s) +{ + int pos = url_ftell(pb); + int i; + + put_be32(pb, 0); /* size */ + put_tag(pb, "udta"); + + /* Requirements */ + for (i=0; itracks[i].entry <= 0) continue; + if (mov->tracks[i].enc->codec_id == CODEC_ID_AAC || + mov->tracks[i].enc->codec_id == CODEC_ID_MPEG4) { + int 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); + break; + } + } + + /* Encoder */ + { + int 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] ) + { + int 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] ) + { + int 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] ) + { + int 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); + } + + return updateSize(pb, pos); +} + +static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov, + AVFormatContext *s) { int pos, i; pos = url_ftell(pb); @@ -732,6 +855,8 @@ static int mov_write_moov_tag(ByteIOContext *pb, MOVContext *mov) } } + mov_write_udta_tag(pb, mov, s); + return updateSize(pb, pos); } @@ -746,17 +871,19 @@ int mov_write_mdat_tag(ByteIOContext *pb, MOVContext* mov) /* TODO: This needs to be more general */ int mov_write_ftyp_tag(ByteIOContext *pb, AVFormatContext *s) { + MOVContext *mov = s->priv_data; + put_be32(pb, 0x14 ); /* size */ put_tag(pb, "ftyp"); - if (!strcmp("3gp", s->oformat->name)) + if ( mov->mode == MODE_3GP ) put_tag(pb, "3gp4"); else put_tag(pb, "isom"); put_be32(pb, 0x200 ); - if (!strcmp("3gp", s->oformat->name)) + if ( mov->mode == MODE_3GP ) put_tag(pb, "3gp4"); else put_tag(pb, "mp41"); @@ -767,10 +894,22 @@ int mov_write_ftyp_tag(ByteIOContext *pb, AVFormatContext *s) static int mov_write_header(AVFormatContext *s) { ByteIOContext *pb = &s->pb; + MOVContext *mov = s->priv_data; + int i; - if(s->oformat != NULL) { - if(!strcmp("3gp", s->oformat->name) || !strcmp("mp4", s->oformat->name)) - mov_write_ftyp_tag(pb,s); + /* Default mode == MP4 */ + mov->mode = MODE_MP4; + + if (s->oformat != NULL) { + if (!strcmp("3gp", s->oformat->name)) mov->mode = MODE_3GP; + else if (!strcmp("mov", s->oformat->name)) mov->mode = MODE_MOV; + + if ( mov->mode == MODE_3GP || mov->mode == MODE_MP4 ) + mov_write_ftyp_tag(pb,s); + } + + for (i=0; itracks[i].mode = mov->mode; } put_flush_packet(pb); @@ -845,7 +984,7 @@ static int mov_write_packet(AVFormatContext *s, int stream_index, mov->time = Timestamp(); } - trk->cluster[cl][id].pos = url_ftell(pb) - mov->movi_list; + 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; @@ -871,22 +1010,20 @@ static int mov_write_trailer(AVFormatContext *s) ByteIOContext *pb = &s->pb; int res = 0; int i, j; - offset_t file_size; - file_size = url_ftell(pb); - j = 0; + offset_t moov_pos = url_ftell(pb); /* Write size of mdat tag */ - for (i=0; itracks[i].ents_allocated > 0) { j += mov->tracks[i].mdat_size; } } url_fseek(pb, mov->mdat_pos, SEEK_SET); put_be32(pb, j+8); - url_fseek(pb, file_size, SEEK_SET); + url_fseek(pb, moov_pos, SEEK_SET); - mov_write_moov_tag(pb, mov); + mov_write_moov_tag(pb, mov, s); for (i=0; itracks[i].ents_allocated/MOV_INDEX_CLUSTER_SIZE; j++) { @@ -898,6 +1035,7 @@ static int mov_write_trailer(AVFormatContext *s) mov->tracks[i].cluster = NULL; mov->tracks[i].ents_allocated = mov->tracks[i].entry = 0; } + put_flush_packet(pb); return res; @@ -909,7 +1047,7 @@ static AVOutputFormat mov_oformat = { NULL, "mov", sizeof(MOVContext), - CODEC_ID_PCM_ALAW, + CODEC_ID_AAC, CODEC_ID_MPEG4, mov_write_header, mov_write_packet, diff --git a/tests/ffmpeg.regression.ref b/tests/ffmpeg.regression.ref index 8c8e4e6f2e..0114cb9240 100644 --- a/tests/ffmpeg.regression.ref +++ b/tests/ffmpeg.regression.ref @@ -29,7 +29,7 @@ stddev: 8.18 PSNR:29.86 bytes:7602176 920a0a8a0063655d1f34dcaad7857f98 *./data/a-h263p.avi 0eb167c9dfcbeeecbf3debed8af8f811 *./data/out.yuv stddev: 2.08 PSNR:41.74 bytes:7602176 -3ddc7fca3c66a27da6544bbb0fd9aca1 *./data/a-odivx.mp4 +1499f43271be5e95880954670620eb1f *./data/a-odivx.mp4 d0d19e18c9d7b6fecaa7f9fc68691d89 *./data/out.yuv stddev: 7.94 PSNR:30.12 bytes:7602176 5704a082cc5c5970620123ae20566286 *./data/a-huffyuv.avi diff --git a/tests/rotozoom.regression.ref b/tests/rotozoom.regression.ref index 9b763f001a..5b32ac0ce6 100644 --- a/tests/rotozoom.regression.ref +++ b/tests/rotozoom.regression.ref @@ -29,7 +29,7 @@ stddev: 5.41 PSNR:33.45 bytes:7602176 f7828488c31ccb6787367ef4e4a2ad42 *./data/a-h263p.avi 7d39d1f272205a6a231d0e0baf32ff9d *./data/out.yuv stddev: 1.91 PSNR:42.49 bytes:7602176 -7e4693c0e68c0ee0cdfac145d3563536 *./data/a-odivx.mp4 +406dd7eaf5d09702d2b7164197a640a9 *./data/a-odivx.mp4 19fad75bd384b38f08830bfa19c06183 *./data/out.yuv stddev: 5.27 PSNR:33.67 bytes:7602176 242a7a18c2793e115007bc163861ef4e *./data/a-huffyuv.avi