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);
256 width = job->pixel_aspect_width;
257 height = job->pixel_aspect_height;
258 widthRatio = (width / height) * 0x10000;
260 uint32_t widthRatioInt;
261 widthRatioInt = (uint32_t)widthRatio;
263 #ifdef WORDS_BIGENDIAN
264 ptr32[0] = widthRatioInt;
266 /* we need to switch the endianness, as the file format expects big endian */
267 ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
270 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
271 hb_log("Problem setting transform matrix");
278 /* end of transformation matrix */
280 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
281 MP4TrackId firstAudioTrack = 0;
283 /* add the audio tracks */
284 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
286 static u_int8_t reserved2[16] = {
287 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x00, 0x00, 0x00,
289 0x00, 0x02, 0x00, 0x10,
290 0x00, 0x00, 0x00, 0x00,
293 audio = hb_list_item( title->list_audio, i );
294 mux_data = malloc( sizeof( hb_mux_data_t ) );
295 audio->mux_data = mux_data;
297 mux_data->track = MP4AddAudioTrack( m->file,
298 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
299 MP4SetAudioProfileLevel( m->file, 0x0F );
300 MP4SetTrackESConfiguration( m->file, mux_data->track,
301 audio->config.aac.bytes, audio->config.aac.length );
303 /* Set the language for this track */
304 /* The language is stored as 5-bit text - 0x60 */
305 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
306 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
307 language_code |= audio->iso639_2[2] - 0x60;
308 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
310 /* Set the correct number of channels for this track */
311 reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
312 MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
314 /* store a reference to the first audio track,
315 so we can use it to feed the chapter text track's sample rate */
317 firstAudioTrack = mux_data->track;
319 /* Enable the first audio track */
320 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
324 /* Disable the other audio tracks so QuickTime doesn't play
327 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
328 hb_log("Disabled extra audio track %i", mux_data->track-1);
333 if (job->chapter_markers)
335 /* add a text track for the chapters */
336 MP4TrackId textTrack;
337 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
339 m->chapter_track = textTrack;
340 m->chapter_duration = 0;
341 m->current_chapter = job->chapter_start;
347 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
350 hb_job_t * job = m->job;
354 if( mux_data == job->mux_data )
356 /* Add the sample before the new frame.
357 It is important that this be calculated prior to the duration
358 of the new video sample, as we want to sync to right after it.
359 (This is because of how durations for text tracks work in QT) */
360 if( job->chapter_markers && buf->new_chap )
362 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
364 if( !MP4WriteSample(m->file,
371 hb_error("Failed to write to output file, disk full?");
375 m->current_chapter++;
376 m->chapter_duration = m->sum_dur;
380 /* Because we use the audio samplerate as the timescale,
381 we have to use potentially variable durations so the video
382 doesn't go out of sync */
383 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
384 m->sum_dur += duration;
389 duration = MP4_INVALID_DURATION;
392 /* When we do get the first keyframe, use its duration as the
393 initial delay added to the frame order offset for b-frames.
394 Because of b-pyramid, double this duration when there are
395 b-pyramids, as denoted by job->areBframes equalling 2. */
396 if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
398 initDelay = buf->renderOffset;
402 /* Here's where the sample actually gets muxed.
403 If it's an audio sample, don't offset the sample's playback.
404 If it's a video sample and there are no b-frames, ditto.
405 If there are b-frames, offset by the initDelay plus the
406 difference between the presentation time stamp x264 gives
407 and the decoding time stamp from the buffer data. */
408 if( !MP4WriteSample( m->file,
413 ((mux_data->track != 1) ||
414 (job->areBframes==0) ||
415 (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
416 ((buf->frametype & HB_FRAME_KEY) != 0) ) )
418 hb_error("Failed to write to output file, disk full?");
425 static int MP4End( hb_mux_object_t * m )
427 hb_job_t * job = m->job;
429 /* Write our final chapter marker */
430 if( m->job->chapter_markers )
432 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
434 if( !MP4WriteSample(m->file,
441 hb_error("Failed to write to output file, disk full?");
448 hb_job_t * job = m->job;
449 char filename[1024]; memset( filename, 0, 1024 );
453 /* Walk the entire video sample table and find the minumum ctts value. */
455 MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
457 MP4Duration renderingOffset = 2000000000, tmp;
459 // Find the smallest rendering offset
460 for(i = 1; i <= count; i++)
462 tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
463 if(tmp < renderingOffset)
464 renderingOffset = tmp;
467 // Adjust all ctts values down by renderingOffset
468 for(i = 1; i <= count; i++)
470 MP4SetSampleRenderingOffset(m->file,1,i,
471 MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
474 // Insert track edit to get A/V back in sync. The edit amount is
475 // the rendering offset of the first sample.
476 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
477 MP4GetTrackDuration(m->file, 1), 0);
483 hb_log( "muxmp4: optimizing file" );
484 snprintf( filename, 1024, "%s.tmp", job->file );
485 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
487 rename( filename, job->file );
493 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
495 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );