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)
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 /* store a reference to the first audio track,
328 so we can use it to feed the chapter text track's sample rate */
330 firstAudioTrack = mux_data->track;
332 /* Enable the first audio track */
333 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
337 /* Disable the other audio tracks so QuickTime doesn't play
340 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
341 hb_log("Disabled extra audio track %i", mux_data->track-1);
346 if (job->chapter_markers)
348 /* add a text track for the chapters */
349 MP4TrackId textTrack;
350 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
352 m->chapter_track = textTrack;
353 m->chapter_duration = 0;
354 m->current_chapter = job->chapter_start;
360 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
363 hb_job_t * job = m->job;
367 if( mux_data == job->mux_data )
369 /* Add the sample before the new frame.
370 It is important that this be calculated prior to the duration
371 of the new video sample, as we want to sync to right after it.
372 (This is because of how durations for text tracks work in QT) */
373 if( job->chapter_markers && buf->new_chap )
375 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
377 if( !MP4WriteSample(m->file,
384 hb_error("Failed to write to output file, disk full?");
388 m->current_chapter++;
389 m->chapter_duration = m->sum_dur;
393 /* Because we use the audio samplerate as the timescale,
394 we have to use potentially variable durations so the video
395 doesn't go out of sync */
398 duration = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
402 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
404 m->sum_dur += duration;
409 duration = MP4_INVALID_DURATION;
412 /* When we do get the first keyframe, use its duration as the
413 initial delay added to the frame order offset for b-frames.
414 Because of b-pyramid, double this duration when there are
415 b-pyramids, as denoted by job->areBframes equalling 2. */
416 if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
418 initDelay = buf->renderOffset;
422 /* Here's where the sample actually gets muxed.
423 If it's an audio sample, don't offset the sample's playback.
424 If it's a video sample and there are no b-frames, ditto.
425 If there are b-frames, offset by the initDelay plus the
426 difference between the presentation time stamp x264 gives
427 and the decoding time stamp from the buffer data. */
428 if( !MP4WriteSample( m->file,
433 ((mux_data->track != 1) ||
434 (job->areBframes==0) ||
435 (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
436 ((buf->frametype & HB_FRAME_KEY) != 0) ) )
438 hb_error("Failed to write to output file, disk full?");
445 static int MP4End( hb_mux_object_t * m )
447 hb_job_t * job = m->job;
449 /* Write our final chapter marker */
450 if( m->job->chapter_markers )
452 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
454 if( !MP4WriteSample(m->file,
461 hb_error("Failed to write to output file, disk full?");
468 hb_job_t * job = m->job;
469 char filename[1024]; memset( filename, 0, 1024 );
473 /* Walk the entire video sample table and find the minumum ctts value. */
475 MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
477 MP4Duration renderingOffset = 2000000000, tmp;
479 // Find the smallest rendering offset
480 for(i = 1; i <= count; i++)
482 tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
483 if(tmp < renderingOffset)
484 renderingOffset = tmp;
487 // Adjust all ctts values down by renderingOffset
488 for(i = 1; i <= count; i++)
490 MP4SetSampleRenderingOffset(m->file,1,i,
491 MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
494 // Insert track edit to get A/V back in sync. The edit amount is
495 // the rendering offset of the first sample.
496 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
497 MP4GetTrackDuration(m->file, 1), 0);
503 hb_log( "muxmp4: optimizing file" );
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 );