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) */
30 /* Chapter state information for muxing */
31 MP4TrackId chapter_track;
33 uint64_t chapter_duration;
41 struct hb_text_sample_s
48 /**********************************************************************
50 **********************************************************************
51 * Creates a buffer for a text track sample
52 *********************************************************************/
53 static struct hb_text_sample_s *MP4CreateTextSample( char *textString, uint64_t duration )
55 struct hb_text_sample_s *sample = NULL;
56 int stringLength = strlen(textString);
59 if( stringLength < 1024 )
61 sample = malloc( sizeof( struct hb_text_sample_s ) );
63 //textLength = (stringLength; // Account for BOM
64 sample->length = stringLength + 2 + 12; // Account for text length code and other marker
65 sample->duration = (MP4Duration)duration;
67 // 2-byte length marker
68 sample->sample[0] = (stringLength >> 8) & 0xff;
69 sample->sample[1] = stringLength & 0xff;
71 strncpy( (char *)&(sample->sample[2]), textString, stringLength );
75 // Modifier Length Marker
76 sample->sample[x] = 0x00;
77 sample->sample[x+1] = 0x00;
78 sample->sample[x+2] = 0x00;
79 sample->sample[x+3] = 0x0C;
82 sample->sample[x+4] = 'e';
83 sample->sample[x+5] = 'n';
84 sample->sample[x+6] = 'c';
85 sample->sample[x+7] = 'd';
88 sample->sample[x+8] = 0x00;
89 sample->sample[x+9] = 0x00;
90 sample->sample[x+10] = (256 >> 8) & 0xff;
91 sample->sample[x+11] = 256 & 0xff;
97 /**********************************************************************
98 * MP4GenerateChapterSample
99 **********************************************************************
100 * Creates a buffer for a text track sample
101 *********************************************************************/
102 static struct hb_text_sample_s *MP4GenerateChapterSample( hb_mux_object_t * m, uint64_t duration )
104 int chapter = m->current_chapter;
105 hb_chapter_t *chapter_data = hb_list_item( m->job->title->list_chapter, chapter - 1 );
106 char tmp_buffer[1024];
107 char *string = tmp_buffer;
109 tmp_buffer[0] = '\0';
111 if( chapter_data != NULL )
113 string = chapter_data->title;
116 if( strlen(string) == 0 || strlen(string) >= 1024 )
118 snprintf( tmp_buffer, 1023, "Chapter %03i", chapter );
122 return MP4CreateTextSample( string, duration );
126 /**********************************************************************
128 **********************************************************************
129 * Allocates hb_mux_data_t structures, create file and write headers
130 *********************************************************************/
131 static int MP4Init( hb_mux_object_t * m )
133 hb_job_t * job = m->job;
134 hb_title_t * title = job->title;
137 hb_mux_data_t * mux_data;
139 u_int16_t language_code;
141 /* Flags for enabling/disabling tracks in an MP4. */
142 typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
145 /* Create an empty mp4 file */
146 if (job->largeFileSize)
147 /* Use 64-bit MP4 file */
149 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
150 hb_log("Using 64-bit MP4 formatting.");
153 /* Limit MP4s to less than 4 GB */
155 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
158 if (m->file == MP4_INVALID_FILE_HANDLE)
160 hb_error("muxmp4.c: MP4Create failed!");
166 mux_data = malloc( sizeof( hb_mux_data_t ) );
167 job->mux_data = mux_data;
169 /* When using the standard 90000 timescale, QuickTime tends to have
170 synchronization issues (audio not playing at the correct speed).
171 To workaround this, we use the audio samplerate as the
173 if (!(MP4SetTimeScale( m->file, job->arate )))
175 hb_error("muxmp4.c: MP4SetTimeScale failed!");
180 if( job->vcodec == HB_VCODEC_X264 )
182 /* Stolen from mp4creator */
183 if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
185 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
190 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
191 MP4_INVALID_DURATION, job->width, job->height,
192 job->config.h264.sps[1], /* AVCProfileIndication */
193 job->config.h264.sps[2], /* profile_compat */
194 job->config.h264.sps[3], /* AVCLevelIndication */
195 3 ); /* 4 bytes length before each NAL unit */
198 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
199 job->config.h264.sps, job->config.h264.sps_length );
200 MP4AddH264PictureParameterSet( m->file, mux_data->track,
201 job->config.h264.pps, job->config.h264.pps_length );
203 if( job->h264_level == 30 || job->ipod_atom)
205 hb_log("About to add iPod atom");
206 AddIPodUUID(m->file, mux_data->track);
210 else /* FFmpeg or XviD */
212 if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
214 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
218 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
219 MP4_INVALID_DURATION, job->width, job->height,
220 MP4_MPEG4_VIDEO_TYPE );
221 if (mux_data->track == MP4_INVALID_TRACK_ID)
223 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
229 /* VOL from FFmpeg or XviD */
230 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
231 job->config.mpeg4.bytes, job->config.mpeg4.length )))
233 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
239 /* apply the anamorphic transformation matrix if needed */
241 if( job->pixel_ratio ) {
245 uint32_t *ptr32 = (uint32_t*) (nval + 2);
248 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
252 memcpy(nval, val, size);
255 width = job->pixel_aspect_width;
256 height = job->pixel_aspect_height;
258 uint32_t pixelRatioInt;
261 pixelRatioInt = (uint32_t)((width / height) * 0x10000);
263 #ifdef WORDS_BIGENDIAN
264 ptr32[0] = pixelRatioInt;
266 /* we need to switch the endianness, as the file format expects big endian */
267 ptr32[0] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
273 pixelRatioInt = (uint32_t)((height / width) * 0x10000);
274 #ifdef WORDS_BIGENDIAN
275 ptr32[4] = pixelRatioInt;
277 /* we need to switch the endianness, as the file format expects big endian */
278 ptr32[4] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
283 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
284 hb_log("Problem setting transform matrix");
291 /* end of transformation matrix */
293 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
294 MP4TrackId firstAudioTrack = 0;
296 /* add the audio tracks */
297 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
299 static u_int8_t reserved2[16] = {
300 0x00, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x02, 0x00, 0x10,
303 0x00, 0x00, 0x00, 0x00,
306 audio = hb_list_item( title->list_audio, i );
307 mux_data = malloc( sizeof( hb_mux_data_t ) );
308 audio->mux_data = mux_data;
310 mux_data->track = MP4AddAudioTrack( m->file,
311 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
312 MP4SetAudioProfileLevel( m->file, 0x0F );
313 MP4SetTrackESConfiguration( m->file, mux_data->track,
314 audio->config.aac.bytes, audio->config.aac.length );
316 /* Set the language for this track */
317 /* The language is stored as 5-bit text - 0x60 */
318 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
319 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
320 language_code |= audio->iso639_2[2] - 0x60;
321 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
323 /* Set the correct number of channels for this track */
324 reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
325 MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
327 /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
328 // MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
330 /* store a reference to the first audio track,
331 so we can use it to feed the chapter text track's sample rate */
333 firstAudioTrack = mux_data->track;
335 /* Enable the first audio track */
336 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
340 /* Disable the other audio tracks so QuickTime doesn't play
343 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
344 hb_log("Disabled extra audio track %i", mux_data->track-1);
349 if (job->chapter_markers)
351 /* add a text track for the chapters */
352 MP4TrackId textTrack;
353 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
355 m->chapter_track = textTrack;
356 m->chapter_duration = 0;
357 m->current_chapter = job->chapter_start;
363 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
366 hb_job_t * job = m->job;
370 if( mux_data == job->mux_data )
372 /* Add the sample before the new frame.
373 It is important that this be calculated prior to the duration
374 of the new video sample, as we want to sync to right after it.
375 (This is because of how durations for text tracks work in QT) */
376 if( job->chapter_markers && buf->new_chap )
378 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
380 if( !MP4WriteSample(m->file,
387 hb_error("Failed to write to output file, disk full?");
391 m->current_chapter++;
392 m->chapter_duration = m->sum_dur;
396 /* Because we use the audio samplerate as the timescale,
397 we have to use potentially variable durations so the video
398 doesn't go out of sync */
401 duration = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
405 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
407 m->sum_dur += duration;
412 duration = MP4_INVALID_DURATION;
415 /* When we do get the first keyframe, use its duration as the
416 initial delay added to the frame order offset for b-frames.
417 Because of b-pyramid, double this duration when there are
418 b-pyramids, as denoted by job->areBframes equalling 2. */
419 if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
421 initDelay = buf->renderOffset;
425 /* Here's where the sample actually gets muxed.
426 If it's an audio sample, don't offset the sample's playback.
427 If it's a video sample and there are no b-frames, ditto.
428 If there are b-frames, offset by the initDelay plus the
429 difference between the presentation time stamp x264 gives
430 and the decoding time stamp from the buffer data. */
431 if( !MP4WriteSample( m->file,
436 ((mux_data->track != 1) ||
437 (job->areBframes==0) ||
438 (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
439 ((buf->frametype & HB_FRAME_KEY) != 0) ) )
441 hb_error("Failed to write to output file, disk full?");
448 static int MP4End( hb_mux_object_t * m )
450 hb_job_t * job = m->job;
452 /* Write our final chapter marker */
453 if( m->job->chapter_markers )
455 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
457 if( !MP4WriteSample(m->file,
464 hb_error("Failed to write to output file, disk full?");
471 /* Walk the entire video sample table and find the minumum ctts value. */
473 MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
475 MP4Duration renderingOffset = 2000000000, tmp;
477 // Find the smallest rendering offset
478 for(i = 1; i <= count; i++)
480 tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
481 if(tmp < renderingOffset)
482 renderingOffset = tmp;
485 // Adjust all ctts values down by renderingOffset
486 for(i = 1; i <= count; i++)
488 MP4SetSampleRenderingOffset(m->file,1,i,
489 MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
492 // Insert track edit to get A/V back in sync. The edit amount is
493 // the rendering offset of the first sample.
494 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
495 MP4GetTrackDuration(m->file, 1), 0);
500 if ( job->mp4_optimize )
502 hb_log( "muxmp4: optimizing file" );
503 char filename[1024]; memset( filename, 0, 1024 );
504 snprintf( filename, 1024, "%s.tmp", job->file );
505 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
507 rename( filename, job->file );
513 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
515 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );