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);
14 /* B-frame muxing variables */
15 MP4SampleId thisSample = 0;
18 struct hb_mux_object_s
27 /* Cumulated durations so far, in timescale units (see MP4Mux) */
37 /**********************************************************************
39 **********************************************************************
40 * Allocates hb_mux_data_t structures, create file and write headers
41 *********************************************************************/
42 static int MP4Init( hb_mux_object_t * m )
44 hb_job_t * job = m->job;
45 hb_title_t * title = job->title;
48 hb_mux_data_t * mux_data;
50 u_int16_t language_code;
52 /* Create an empty mp4 file */
53 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
56 mux_data = malloc( sizeof( hb_mux_data_t ) );
57 job->mux_data = mux_data;
59 /* When using the standard 90000 timescale, QuickTime tends to have
60 synchronization issues (audio not playing at the correct speed).
61 To workaround this, we use the audio samplerate as the
63 MP4SetTimeScale( m->file, job->arate );
65 if( job->vcodec == HB_VCODEC_X264 )
67 /* Stolen from mp4creator */
68 MP4SetVideoProfileLevel( m->file, 0x7F );
70 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
71 MP4_INVALID_DURATION, 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 */
78 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
79 job->config.h264.sps, job->config.h264.sps_length );
80 MP4AddH264PictureParameterSet( m->file, mux_data->track,
81 job->config.h264.pps, job->config.h264.pps_length );
83 if( job->h264_level == 30)
85 hb_log("About to add iPod atom");
86 AddIPodUUID(m->file, mux_data->track);
90 else /* FFmpeg or XviD */
92 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
93 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
94 MP4_INVALID_DURATION, job->width, job->height,
95 MP4_MPEG4_VIDEO_TYPE );
97 /* VOL from FFmpeg or XviD */
98 MP4SetTrackESConfiguration( m->file, mux_data->track,
99 job->config.mpeg4.bytes, job->config.mpeg4.length );
102 /* apply the anamorphic transformation matrix if needed */
104 if( job->pixel_ratio ) {
108 uint32_t *ptr32 = (uint32_t*) (nval + 2);
111 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
115 memcpy(nval, val, size);
119 width = job->pixel_aspect_width;
120 height = job->pixel_aspect_height;
121 widthRatio = (width / height) * 0x10000;
123 uint32_t widthRatioInt;
124 widthRatioInt = (uint32_t)widthRatio;
126 #ifdef WORDS_BIGENDIAN
127 ptr32[0] = widthRatioInt;
129 /* we need to switch the endianness, as the file format expects big endian */
130 ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
133 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
134 hb_log("Problem setting transform matrix");
141 /* end of transformation matrix */
143 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
144 MP4TrackId firstAudioTrack = 0;
146 /* add the audio tracks */
147 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
149 audio = hb_list_item( title->list_audio, i );
150 mux_data = malloc( sizeof( hb_mux_data_t ) );
151 audio->mux_data = mux_data;
153 mux_data->track = MP4AddAudioTrack( m->file,
154 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
155 MP4SetAudioProfileLevel( m->file, 0x0F );
156 MP4SetTrackESConfiguration( m->file, mux_data->track,
157 audio->config.aac.bytes, audio->config.aac.length );
159 /* Set the language for this track */
160 /* The language is stored as 5-bit text - 0x60 */
161 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
162 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
163 language_code |= audio->iso639_2[2] - 0x60;
164 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
166 /* store a reference to the first audio track,
167 so we can use it to feed the chapter text track's sample rate */
169 firstAudioTrack = mux_data->track;
174 if (job->chapter_markers) {
176 /* add a text track for the chapters */
177 MP4TrackId textTrack;
179 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
181 /* write the chapter markers for each selected chapter */
183 hb_chapter_t * chapter;
184 MP4Duration chapterDuration;
185 float fOrigDuration, fTimescale;
188 for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
190 chapter = hb_list_item( title->list_chapter, i );
192 fOrigDuration = chapter->duration;
193 fTimescale = job->arate;
194 fTSDuration = (fOrigDuration / 90000) * fTimescale;
195 chapterDuration = (MP4Duration)fTSDuration;
197 sprintf(markerBuf, " Chapter %03i", i + 1);
199 markerBuf[1] = 11; // "Chapter xxx"
200 MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
209 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
212 hb_job_t * job = m->job;
216 if( mux_data == job->mux_data )
219 /* Because we use the audio samplerate as the timescale,
220 we have to use potentially variable durations so the video
221 doesn't go out of sync */
222 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
223 m->sum_dur += duration;
228 duration = MP4_INVALID_DURATION;
231 /* When we do get the first keyframe, use its duration as the
232 initial delay added to the frame order offset for b-frames.
233 Because of b-pyramid, double this duration when there are
234 b-pyramids, as denoted by job->areBframes equalling 2. */
235 if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1) && (job->vcodec == HB_VCODEC_X264))
237 initDelay = buf->renderOffset;
241 /* Here's where the sample actually gets muxed.
242 If it's an audio sample, don't offset the sample's playback.
243 If it's a video sample and there are no b-frames, ditto.
244 If there are b-frames, offset by the initDelay plus the
245 difference between the presentation time stamp x264 gives
246 and the decoding time stamp from the buffer data. */
247 MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
248 duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
254 static int MP4End( hb_mux_object_t * m )
257 hb_job_t * job = m->job;
258 char filename[1024]; memset( filename, 0, 1024 );
261 hb_job_t * job = m->job;
264 /* Walk the entire video sample table and find the minumum ctts value. */
266 MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
268 MP4Duration renderingOffset = 2000000000, tmp;
270 // Find the smallest rendering offset
271 for(i = 1; i <= count; i++)
273 tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
274 if(tmp < renderingOffset)
275 renderingOffset = tmp;
278 // Adjust all ctts values down by renderingOffset
279 for(i = 1; i <= count; i++)
281 MP4SetSampleRenderingOffset(m->file,1,i,
282 MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
285 // Insert track edit to get A/V back in sync. The edit amount is
286 // the rendering offset of the first sample.
287 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
288 MP4GetTrackDuration(m->file, 1), 0);
294 hb_log( "muxmp4: optimizing file" );
295 snprintf( filename, 1024, "%s.tmp", job->file );
296 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
298 rename( filename, job->file );
304 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
306 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );