1 /* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.m0k.org/>.
5 It may be used under the terms of the GNU General Public License. */
12 void AddIPodUUID(MP4FileHandle, MP4TrackId);
15 struct hb_mux_object_s
24 /* Cumulated durations so far, in timescale units (see MP4Mux) */
34 /**********************************************************************
36 **********************************************************************
37 * Allocates hb_mux_data_t structures, create file and write headers
38 *********************************************************************/
39 static int MP4Init( hb_mux_object_t * m )
41 hb_job_t * job = m->job;
42 hb_title_t * title = job->title;
45 hb_mux_data_t * mux_data;
47 u_int16_t language_code;
49 /* Create an empty mp4 file */
50 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
53 mux_data = malloc( sizeof( hb_mux_data_t ) );
54 job->mux_data = mux_data;
56 /* When using the standard 90000 timescale, QuickTime tends to have
57 synchronization issues (audio not playing at the correct speed).
58 To workaround this, we use the audio samplerate as the
60 MP4SetTimeScale( m->file, job->arate );
62 if( job->vcodec == HB_VCODEC_X264 )
64 /* Stolen from mp4creator */
65 MP4SetVideoProfileLevel( m->file, 0x7F );
67 if (job->areBframes == 1)
69 hb_log("muxmp4: Adjusting duration for B-frames");
70 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
71 MP4_INVALID_DURATION+1, job->width, job->height,
72 job->config.h264.sps[1], /* AVCProfileIndication */
73 job->config.h264.sps[2], /* profile_compat */
74 job->config.h264.sps[3], /* AVCLevelIndication */
75 3 ); /* 4 bytes length before each NAL unit */
79 hb_log("muxmp4: Using default duration as there are no B-frames");
80 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
81 MP4_INVALID_DURATION, job->width, job->height,
82 job->config.h264.sps[1], /* AVCProfileIndication */
83 job->config.h264.sps[2], /* profile_compat */
84 job->config.h264.sps[3], /* AVCLevelIndication */
85 3 ); /* 4 bytes length before each NAL unit */
88 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
89 job->config.h264.sps, job->config.h264.sps_length );
90 MP4AddH264PictureParameterSet( m->file, mux_data->track,
91 job->config.h264.pps, job->config.h264.pps_length );
93 if( job->h264_level == 30)
95 hb_log("About to add iPod atom");
96 AddIPodUUID(m->file, mux_data->track);
100 else /* FFmpeg or XviD */
102 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
103 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
104 MP4_INVALID_DURATION, job->width, job->height,
105 MP4_MPEG4_VIDEO_TYPE );
107 /* VOL from FFmpeg or XviD */
108 MP4SetTrackESConfiguration( m->file, mux_data->track,
109 job->config.mpeg4.bytes, job->config.mpeg4.length );
112 /* apply the anamorphic transformation matrix if needed */
114 if( job->pixel_ratio ) {
118 uint32_t *ptr32 = (uint32_t*) (nval + 2);
121 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
125 memcpy(nval, val, size);
129 width = job->pixel_aspect_width;
130 height = job->pixel_aspect_height;
131 widthRatio = (width / height) * 0x10000;
133 uint32_t widthRatioInt;
134 widthRatioInt = (uint32_t)widthRatio;
136 #ifdef WORDS_BIGENDIAN
137 ptr32[0] = widthRatioInt;
139 /* we need to switch the endianness, as the file format expects big endian */
140 ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
143 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
144 hb_log("Problem setting transform matrix");
151 /* end of transformation matrix */
153 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
154 MP4TrackId firstAudioTrack;
156 /* add the audio tracks */
157 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
159 audio = hb_list_item( title->list_audio, i );
160 mux_data = malloc( sizeof( hb_mux_data_t ) );
161 audio->mux_data = mux_data;
163 mux_data->track = MP4AddAudioTrack( m->file,
164 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
165 MP4SetAudioProfileLevel( m->file, 0x0F );
166 MP4SetTrackESConfiguration( m->file, mux_data->track,
167 audio->config.aac.bytes, audio->config.aac.length );
169 /* Set the language for this track */
170 /* The language is stored as 5-bit text - 0x60 */
171 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
172 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
173 language_code |= audio->iso639_2[2] - 0x60;
174 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
176 /* store a reference to the first audio track,
177 so we can use it to feed the chapter text track's sample rate */
179 firstAudioTrack = mux_data->track;
184 if (job->chapter_markers) {
186 /* add a text track for the chapters */
187 MP4TrackId textTrack;
189 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
191 /* write the chapter markers for each selected chapter */
193 hb_chapter_t * chapter;
194 MP4Duration chapterDuration;
195 float fOrigDuration, fTimescale;
198 for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
200 chapter = hb_list_item( title->list_chapter, i );
202 fOrigDuration = chapter->duration;
203 fTimescale = job->arate;
204 fTSDuration = (fOrigDuration / 90000) * fTimescale;
205 chapterDuration = (MP4Duration)fTSDuration;
207 sprintf(markerBuf, " Chapter %03i", i + 1);
209 markerBuf[1] = 11; // "Chapter xxx"
210 MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
219 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
222 hb_job_t * job = m->job;
226 if( mux_data == job->mux_data )
229 /* Because we use the audio samplerate as the timescale,
230 we have to use potentially variable durations so the video
231 doesn't go out of sync */
232 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
233 m->sum_dur += duration;
238 duration = MP4_INVALID_DURATION;
241 MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
242 duration, 0, buf->key );
246 static int MP4End( hb_mux_object_t * m )
249 hb_job_t * job = m->job;
250 char filename[1024]; memset( filename, 0, 1024 );
256 hb_log( "muxmp4: optimizing file" );
257 snprintf( filename, 1024, "%s.tmp", job->file );
258 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
260 rename( filename, job->file );
266 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
268 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );