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.fr/>.
5 It may be used under the terms of the GNU General Public License. */
12 void AddIPodUUID(MP4FileHandle, MP4TrackId);
14 struct hb_mux_object_s
23 /* Cumulated durations so far, in timescale units (see MP4Mux) */
26 /* Chapter state information for muxing */
27 MP4TrackId chapter_track;
29 uint64_t chapter_duration;
31 /* Sample rate of the first audio track.
32 * Used for the timescale
42 struct hb_text_sample_s
49 /**********************************************************************
51 **********************************************************************
52 * Creates a buffer for a text track sample
53 *********************************************************************/
54 static struct hb_text_sample_s *MP4CreateTextSample( char *textString, uint64_t duration )
56 struct hb_text_sample_s *sample = NULL;
57 int stringLength = strlen(textString);
60 if( stringLength < 1024 )
62 sample = malloc( sizeof( struct hb_text_sample_s ) );
64 //textLength = (stringLength; // Account for BOM
65 sample->length = stringLength + 2 + 12; // Account for text length code and other marker
66 sample->duration = (MP4Duration)duration;
68 // 2-byte length marker
69 sample->sample[0] = (stringLength >> 8) & 0xff;
70 sample->sample[1] = stringLength & 0xff;
72 strncpy( (char *)&(sample->sample[2]), textString, stringLength );
76 // Modifier Length Marker
77 sample->sample[x] = 0x00;
78 sample->sample[x+1] = 0x00;
79 sample->sample[x+2] = 0x00;
80 sample->sample[x+3] = 0x0C;
83 sample->sample[x+4] = 'e';
84 sample->sample[x+5] = 'n';
85 sample->sample[x+6] = 'c';
86 sample->sample[x+7] = 'd';
89 sample->sample[x+8] = 0x00;
90 sample->sample[x+9] = 0x00;
91 sample->sample[x+10] = (256 >> 8) & 0xff;
92 sample->sample[x+11] = 256 & 0xff;
98 /**********************************************************************
99 * MP4GenerateChapterSample
100 **********************************************************************
101 * Creates a buffer for a text track sample
102 *********************************************************************/
103 static struct hb_text_sample_s *MP4GenerateChapterSample( hb_mux_object_t * m,
107 // We substract 1 from the chapter number because the chapters start at
108 // 1 but our name array starts at 0. We substract another 1 because we're
109 // writing the text of the previous chapter mark (when we get the start
110 // of chapter 2 we know the duration of chapter 1 & can write its mark).
111 hb_chapter_t *chapter_data = hb_list_item( m->job->title->list_chapter,
113 char tmp_buffer[1024];
114 char *string = tmp_buffer;
116 tmp_buffer[0] = '\0';
118 if( chapter_data != NULL )
120 string = chapter_data->title;
123 if( strlen(string) == 0 || strlen(string) >= 1024 )
125 snprintf( tmp_buffer, 1023, "Chapter %03i", chapter - 2 );
129 return MP4CreateTextSample( string, duration );
133 /**********************************************************************
135 **********************************************************************
136 * Allocates hb_mux_data_t structures, create file and write headers
137 *********************************************************************/
138 static int MP4Init( hb_mux_object_t * m )
140 hb_job_t * job = m->job;
141 hb_title_t * title = job->title;
144 hb_mux_data_t * mux_data;
146 u_int16_t language_code;
148 /* Flags for enabling/disabling tracks in an MP4. */
149 typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
151 /* Need the sample rate of the first audio track to use as the timescale. */
152 audio = hb_list_item(title->list_audio, 0);
153 m->samplerate = audio->config.out.samplerate;
156 /* Create an empty mp4 file */
157 if (job->largeFileSize)
158 /* Use 64-bit MP4 file */
160 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
161 hb_log("Using 64-bit MP4 formatting.");
164 /* Limit MP4s to less than 4 GB */
166 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
169 if (m->file == MP4_INVALID_FILE_HANDLE)
171 hb_error("muxmp4.c: MP4Create failed!");
177 mux_data = malloc( sizeof( hb_mux_data_t ) );
178 job->mux_data = mux_data;
180 /* When using the standard 90000 timescale, QuickTime tends to have
181 synchronization issues (audio not playing at the correct speed).
182 To workaround this, we use the audio samplerate as the
184 if (!(MP4SetTimeScale( m->file, m->samplerate )))
186 hb_error("muxmp4.c: MP4SetTimeScale failed!");
191 if( job->vcodec == HB_VCODEC_X264 )
193 /* Stolen from mp4creator */
194 if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
196 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
201 mux_data->track = MP4AddH264VideoTrack( m->file, m->samplerate,
202 MP4_INVALID_DURATION, job->width, job->height,
203 job->config.h264.sps[1], /* AVCProfileIndication */
204 job->config.h264.sps[2], /* profile_compat */
205 job->config.h264.sps[3], /* AVCLevelIndication */
206 3 ); /* 4 bytes length before each NAL unit */
209 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
210 job->config.h264.sps, job->config.h264.sps_length );
211 MP4AddH264PictureParameterSet( m->file, mux_data->track,
212 job->config.h264.pps, job->config.h264.pps_length );
214 if( job->h264_level == 30 || job->ipod_atom)
216 hb_log("About to add iPod atom");
217 AddIPodUUID(m->file, mux_data->track);
221 else /* FFmpeg or XviD */
223 if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
225 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
229 mux_data->track = MP4AddVideoTrack( m->file, m->samplerate,
230 MP4_INVALID_DURATION, job->width, job->height,
231 MP4_MPEG4_VIDEO_TYPE );
232 if (mux_data->track == MP4_INVALID_TRACK_ID)
234 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
240 /* VOL from FFmpeg or XviD */
241 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
242 job->config.mpeg4.bytes, job->config.mpeg4.length )))
244 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
250 // COLR atom for color and gamma correction.
252 // http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
253 // http://forum.doom9.org/showthread.php?t=133982#post1090068
254 // we say anything that's likely to be HD content is ITU BT.709 and
255 // DVD, SD TV & other content is ITU BT.601. We look at the title height
256 // rather than the job height here to get uncropped input dimensions.
257 if ( job->title->height >= 720 )
259 // we guess that 720p or above is ITU BT.709 HD content
260 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
264 // ITU BT.601 DVD or SD TV content
265 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
268 if( job->pixel_ratio )
270 /* PASP atom for anamorphic video */
273 width = job->pixel_aspect_width;
275 height = job->pixel_aspect_height;
277 MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
279 MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
282 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
283 MP4TrackId firstAudioTrack = 0;
285 /* add the audio tracks */
286 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
288 static u_int8_t reserved2[16] = {
289 0x00, 0x00, 0x00, 0x00,
290 0x00, 0x00, 0x00, 0x00,
291 0x00, 0x02, 0x00, 0x10,
292 0x00, 0x00, 0x00, 0x00,
295 audio = hb_list_item( title->list_audio, i );
296 mux_data = malloc( sizeof( hb_mux_data_t ) );
297 audio->priv.mux_data = mux_data;
299 if( audio->config.out.codec == HB_ACODEC_AC3 )
301 mux_data->track = MP4AddAC3AudioTrack(
303 m->samplerate, 1536, MP4_MPEG4_AUDIO_TYPE );
304 MP4SetTrackBytesProperty(
305 m->file, mux_data->track,
307 (const u_int8_t*)"Surround", strlen("Surround"));
309 mux_data->track = MP4AddAudioTrack(
311 m->samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
312 MP4SetTrackBytesProperty(
313 m->file, mux_data->track,
315 (const u_int8_t*)"Stereo", strlen("Stereo"));
317 MP4SetAudioProfileLevel( m->file, 0x0F );
318 MP4SetTrackESConfiguration(
319 m->file, mux_data->track,
320 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
322 /* Set the correct number of channels for this track */
323 reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown);
324 MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
327 /* Set the language for this track */
328 /* The language is stored as 5-bit text - 0x60 */
329 language_code = audio->config.lang.iso639_2[0] - 0x60; language_code <<= 5;
330 language_code |= audio->config.lang.iso639_2[1] - 0x60; language_code <<= 5;
331 language_code |= audio->config.lang.iso639_2[2] - 0x60;
332 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
335 /* Set the audio track alternate group */
336 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
338 /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
339 // MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
341 /* store a reference to the first audio track,
342 so we can use it to feed the chapter text track's sample rate */
344 firstAudioTrack = mux_data->track;
346 /* Enable the first audio track */
347 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
351 /* Disable the other audio tracks so QuickTime doesn't play
354 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
355 hb_log("Disabled extra audio track %i", mux_data->track-1);
360 if (job->chapter_markers)
362 /* add a text track for the chapters */
363 MP4TrackId textTrack;
364 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
366 m->chapter_track = textTrack;
367 m->chapter_duration = 0;
368 m->current_chapter = job->chapter_start;
371 /* Add encoded-by metadata listing version and build date */
373 tool_string = (char *)malloc(80);
374 snprintf( tool_string, 80, "HandBrake %s %i", HB_VERSION, HB_BUILD);
375 MP4SetMetadataTool(m->file, tool_string);
381 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
384 hb_job_t * job = m->job;
388 if( mux_data == job->mux_data )
390 /* Add the sample before the new frame.
391 It is important that this be calculated prior to the duration
392 of the new video sample, as we want to sync to right after it.
393 (This is because of how durations for text tracks work in QT) */
394 if( job->chapter_markers && buf->new_chap )
396 struct hb_text_sample_s *sample;
398 /* If this is an x264 encode with bframes the IDR frame we're
399 trying to mark will be displayed offset by its renderOffset
400 so we need to offset the chapter by the same amount.
401 MP4 render offsets don't seem to work for text tracks so
402 we have to fudge the duration instead. */
403 duration = m->sum_dur - m->chapter_duration;
405 if ( job->areBframes )
407 duration += buf->renderOffset * m->samplerate / 90000;
411 /* The initial & final chapters can have very short durations
412 * (less than the error in our total duration estimate) so
413 * the duration calc above can result in a negative number.
414 * when this happens give the chapter a short duration (1/3
415 * of an ntsc frame time). */
416 duration = 1000 * m->samplerate / 90000;
419 sample = MP4GenerateChapterSample( m, duration, buf->new_chap );
421 if( !MP4WriteSample(m->file,
428 hb_error("Failed to write to output file, disk full?");
432 m->current_chapter = buf->new_chap;
433 m->chapter_duration += duration;
437 /* Because we use the audio samplerate as the timescale,
438 we have to use potentially variable durations so the video
439 doesn't go out of sync */
440 int64_t bias = ( buf->start * m->samplerate / 90000 ) - m->sum_dur;
441 duration = ( buf->stop - buf->start ) * m->samplerate / 90000 + bias;
444 /* We got an illegal mp4/h264 duration. This shouldn't
445 be possible and usually indicates a bug in the upstream code.
446 Complain in the hope that someone will go find the bug but
447 try to fix the error so that the file will still be playable. */
448 hb_log("MP4Mux: illegal duration %lld, bias %lld, start %lld (%lld),"
449 "stop %lld (%lld), sum_dur %lld",
450 duration, bias, buf->start * m->samplerate / 90000, buf->start,
451 buf->stop * m->samplerate / 90000, buf->stop, m->sum_dur );
452 /* we don't know when the next frame starts so we can't pick a
453 valid duration for this one so we pick something "short"
454 (roughly 1/3 of an NTSC frame time) and rely on the bias calc
455 for the next frame to correct things (a duration underestimate
456 just results in a large bias on the next frame). */
457 duration = 1000 * m->samplerate / 90000;
459 m->sum_dur += duration;
464 duration = MP4_INVALID_DURATION;
467 /* Here's where the sample actually gets muxed.
468 If it's an audio sample, don't offset the sample's playback.
469 If it's a video sample and there are no b-frames, ditto.
470 If there are b-frames, offset by the initDelay plus the
471 difference between the presentation time stamp x264 gives
472 and the decoding time stamp from the buffer data. */
473 if( !MP4WriteSample( m->file,
478 ((mux_data->track != 1) ||
479 (job->areBframes==0) ||
480 (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * m->samplerate / 90000),
481 ((buf->frametype & HB_FRAME_KEY) != 0) ) )
483 hb_error("Failed to write to output file, disk full?");
490 static int MP4End( hb_mux_object_t * m )
492 hb_job_t * job = m->job;
494 /* Write our final chapter marker */
495 if( m->job->chapter_markers )
497 int64_t duration = m->sum_dur - m->chapter_duration;
500 /* The initial & final chapters can have very short durations
501 * (less than the error in our total duration estimate) so
502 * the duration calc above can result in a negative number.
503 * when this happens give the chapter a short duration (1/3
504 * of an ntsc frame time). */
505 duration = 1000 * m->samplerate / 90000;
508 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, duration,
509 m->current_chapter + 1 );
511 if( !MP4WriteSample(m->file,
518 hb_error("Failed to write to output file, disk full?");
526 // Insert track edit to get A/V back in sync. The edit amount is
527 // the rendering offset of the first sample.
528 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
529 MP4GetTrackDuration(m->file, 1), 0);
530 if ( m->job->chapter_markers )
532 // apply same edit to chapter track to keep it in sync with video
533 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
534 MP4GetSampleRenderingOffset(m->file,1,1),
535 MP4GetTrackDuration(m->file, m->chapter_track), 0);
541 if ( job->mp4_optimize )
543 hb_log( "muxmp4: optimizing file" );
544 char filename[1024]; memset( filename, 0, 1024 );
545 snprintf( filename, 1024, "%s.tmp", job->file );
546 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
548 rename( filename, job->file );
554 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
556 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );