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 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;
37 struct hb_text_sample_s
44 /**********************************************************************
46 **********************************************************************
47 * Creates a buffer for a text track sample
48 *********************************************************************/
49 static struct hb_text_sample_s *MP4CreateTextSample( char *textString, uint64_t duration )
51 struct hb_text_sample_s *sample = NULL;
52 int stringLength = strlen(textString);
55 if( stringLength < 1024 )
57 sample = malloc( sizeof( struct hb_text_sample_s ) );
59 //textLength = (stringLength; // Account for BOM
60 sample->length = stringLength + 2 + 12; // Account for text length code and other marker
61 sample->duration = (MP4Duration)duration;
63 // 2-byte length marker
64 sample->sample[0] = (stringLength >> 8) & 0xff;
65 sample->sample[1] = stringLength & 0xff;
67 strncpy( (char *)&(sample->sample[2]), textString, stringLength );
71 // Modifier Length Marker
72 sample->sample[x] = 0x00;
73 sample->sample[x+1] = 0x00;
74 sample->sample[x+2] = 0x00;
75 sample->sample[x+3] = 0x0C;
78 sample->sample[x+4] = 'e';
79 sample->sample[x+5] = 'n';
80 sample->sample[x+6] = 'c';
81 sample->sample[x+7] = 'd';
84 sample->sample[x+8] = 0x00;
85 sample->sample[x+9] = 0x00;
86 sample->sample[x+10] = (256 >> 8) & 0xff;
87 sample->sample[x+11] = 256 & 0xff;
93 /**********************************************************************
94 * MP4GenerateChapterSample
95 **********************************************************************
96 * Creates a buffer for a text track sample
97 *********************************************************************/
98 static struct hb_text_sample_s *MP4GenerateChapterSample( hb_mux_object_t * m, uint64_t duration )
100 int chapter = m->current_chapter;
101 hb_chapter_t *chapter_data = hb_list_item( m->job->title->list_chapter, chapter - 1 );
102 char tmp_buffer[1024];
103 char *string = tmp_buffer;
105 tmp_buffer[0] = '\0';
107 if( chapter_data != NULL )
109 string = chapter_data->title;
112 if( strlen(string) == 0 || strlen(string) >= 1024 )
114 snprintf( tmp_buffer, 1023, "Chapter %03i", chapter );
118 return MP4CreateTextSample( string, duration );
122 /**********************************************************************
124 **********************************************************************
125 * Allocates hb_mux_data_t structures, create file and write headers
126 *********************************************************************/
127 static int MP4Init( hb_mux_object_t * m )
129 hb_job_t * job = m->job;
130 hb_title_t * title = job->title;
133 hb_mux_data_t * mux_data;
135 u_int16_t language_code;
137 /* Flags for enabling/disabling tracks in an MP4. */
138 typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
141 /* Create an empty mp4 file */
142 if (job->largeFileSize)
143 /* Use 64-bit MP4 file */
145 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
146 hb_log("Using 64-bit MP4 formatting.");
149 /* Limit MP4s to less than 4 GB */
151 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
154 if (m->file == MP4_INVALID_FILE_HANDLE)
156 hb_error("muxmp4.c: MP4Create failed!");
162 mux_data = malloc( sizeof( hb_mux_data_t ) );
163 job->mux_data = mux_data;
165 /* When using the standard 90000 timescale, QuickTime tends to have
166 synchronization issues (audio not playing at the correct speed).
167 To workaround this, we use the audio samplerate as the
169 if (!(MP4SetTimeScale( m->file, job->arate )))
171 hb_error("muxmp4.c: MP4SetTimeScale failed!");
176 if( job->vcodec == HB_VCODEC_X264 )
178 /* Stolen from mp4creator */
179 if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
181 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
186 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
187 MP4_INVALID_DURATION, job->width, job->height,
188 job->config.h264.sps[1], /* AVCProfileIndication */
189 job->config.h264.sps[2], /* profile_compat */
190 job->config.h264.sps[3], /* AVCLevelIndication */
191 3 ); /* 4 bytes length before each NAL unit */
194 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
195 job->config.h264.sps, job->config.h264.sps_length );
196 MP4AddH264PictureParameterSet( m->file, mux_data->track,
197 job->config.h264.pps, job->config.h264.pps_length );
199 if( job->h264_level == 30 || job->ipod_atom)
201 hb_log("About to add iPod atom");
202 AddIPodUUID(m->file, mux_data->track);
206 else /* FFmpeg or XviD */
208 if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
210 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
214 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
215 MP4_INVALID_DURATION, job->width, job->height,
216 MP4_MPEG4_VIDEO_TYPE );
217 if (mux_data->track == MP4_INVALID_TRACK_ID)
219 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
225 /* VOL from FFmpeg or XviD */
226 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
227 job->config.mpeg4.bytes, job->config.mpeg4.length )))
229 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
235 if( job->pixel_ratio )
237 /* PASP atom for anamorphic video */
240 width = job->pixel_aspect_width;
242 height = job->pixel_aspect_height;
244 MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
246 MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
249 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
250 MP4TrackId firstAudioTrack = 0;
252 /* add the audio tracks */
253 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
255 static u_int8_t reserved2[16] = {
256 0x00, 0x00, 0x00, 0x00,
257 0x00, 0x00, 0x00, 0x00,
258 0x00, 0x02, 0x00, 0x10,
259 0x00, 0x00, 0x00, 0x00,
262 audio = hb_list_item( title->list_audio, i );
263 mux_data = malloc( sizeof( hb_mux_data_t ) );
264 audio->mux_data = mux_data;
266 if( job->acodec & HB_ACODEC_AC3 ||
267 job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
269 mux_data->track = MP4AddAC3AudioTrack(
271 job->arate, 1536, MP4_MPEG4_AUDIO_TYPE );
272 MP4SetTrackBytesProperty(
273 m->file, mux_data->track,
275 (const u_int8_t*)"Surround", strlen("Surround"));
277 mux_data->track = MP4AddAudioTrack(
279 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
280 MP4SetTrackBytesProperty(
281 m->file, mux_data->track,
283 (const u_int8_t*)"Stereo", strlen("Stereo"));
285 MP4SetAudioProfileLevel( m->file, 0x0F );
286 MP4SetTrackESConfiguration(
287 m->file, mux_data->track,
288 audio->config.aac.bytes, audio->config.aac.length );
290 /* Set the correct number of channels for this track */
291 reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
292 MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
295 /* Set the language for this track */
296 /* The language is stored as 5-bit text - 0x60 */
297 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
298 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
299 language_code |= audio->iso639_2[2] - 0x60;
300 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
303 /* Set the audio track alternate group */
304 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
306 /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
307 // MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
309 /* store a reference to the first audio track,
310 so we can use it to feed the chapter text track's sample rate */
312 firstAudioTrack = mux_data->track;
314 /* Enable the first audio track */
315 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
319 /* Disable the other audio tracks so QuickTime doesn't play
322 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
323 hb_log("Disabled extra audio track %i", mux_data->track-1);
328 if (job->chapter_markers)
330 /* add a text track for the chapters */
331 MP4TrackId textTrack;
332 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
334 m->chapter_track = textTrack;
335 m->chapter_duration = 0;
336 m->current_chapter = job->chapter_start;
339 /* Add encoded-by metadata listing version and build date */
341 tool_string = (char *)malloc(80);
342 snprintf( tool_string, 80, "HandBrake %s %i", HB_VERSION, HB_BUILD);
343 MP4SetMetadataTool(m->file, tool_string);
349 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
352 hb_job_t * job = m->job;
356 if( mux_data == job->mux_data )
358 /* Add the sample before the new frame.
359 It is important that this be calculated prior to the duration
360 of the new video sample, as we want to sync to right after it.
361 (This is because of how durations for text tracks work in QT) */
362 if( job->chapter_markers && buf->new_chap )
364 struct hb_text_sample_s *sample;
366 /* If this is an x264 encode with bframes the IDR frame we're
367 trying to mark will be displayed offset by its renderOffset
368 so we need to offset the chapter by the same amount.
369 MP4 render offsets don't seem to work for text tracks so
370 we have to fudge the duration instead. */
371 duration = m->sum_dur - m->chapter_duration;
373 if ( job->areBframes )
375 duration += buf->renderOffset * job->arate / 90000;
378 sample = MP4GenerateChapterSample( m, 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 += duration;
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 */
399 int64_t bias = ( buf->start * job->arate / 90000 ) - m->sum_dur;
400 duration = ( buf->stop - buf->start ) * job->arate / 90000 + bias;
403 /* We got an illegal mp4/h264 duration. This shouldn't
404 be possible and usually indicates a bug in the upstream code.
405 Complain in the hope that someone will go find the bug but
406 try to fix the error so that the file will still be playable. */
407 hb_log("MP4Mux: illegal duration %lld, bias %lld, start %lld (%lld),"
408 "stop %lld (%lld), sum_dur %lld",
409 duration, bias, buf->start * job->arate / 90000, buf->start,
410 buf->stop * job->arate / 90000, buf->stop, m->sum_dur );
411 /* we don't know when the next frame starts so we can't pick a
412 valid duration for this one so we pick something "short"
413 (roughly 1/3 of an NTSC frame time) and rely on the bias calc
414 for the next frame to correct things (a duration underestimate
415 just results in a large bias on the next frame). */
416 duration = 1000 * job->arate / 90000;
418 m->sum_dur += duration;
423 duration = MP4_INVALID_DURATION;
426 /* Here's where the sample actually gets muxed.
427 If it's an audio sample, don't offset the sample's playback.
428 If it's a video sample and there are no b-frames, ditto.
429 If there are b-frames, offset by the initDelay plus the
430 difference between the presentation time stamp x264 gives
431 and the decoding time stamp from the buffer data. */
432 if( !MP4WriteSample( m->file,
437 ((mux_data->track != 1) ||
438 (job->areBframes==0) ||
439 (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
440 ((buf->frametype & HB_FRAME_KEY) != 0) ) )
442 hb_error("Failed to write to output file, disk full?");
449 static int MP4End( hb_mux_object_t * m )
451 hb_job_t * job = m->job;
453 /* Write our final chapter marker */
454 if( m->job->chapter_markers )
456 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
458 if( !MP4WriteSample(m->file,
465 hb_error("Failed to write to output file, disk full?");
473 // Insert track edit to get A/V back in sync. The edit amount is
474 // the rendering offset of the first sample.
475 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
476 MP4GetTrackDuration(m->file, 1), 0);
477 if ( m->job->chapter_markers )
479 // apply same edit to chapter track to keep it in sync with video
480 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
481 MP4GetSampleRenderingOffset(m->file,1,1),
482 MP4GetTrackDuration(m->file, m->chapter_track), 0);
488 if ( job->mp4_optimize )
490 hb_log( "muxmp4: optimizing file" );
491 char filename[1024]; memset( filename, 0, 1024 );
492 snprintf( filename, 1024, "%s.tmp", job->file );
493 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
495 rename( filename, job->file );
501 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
503 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );