Refer to transitions in the presence-or-lack-thereof of progressive flags on MPEG...
[HandBrake.git] / libhb / muxmp4.c
blob0959c6e48aec217d1999f723c93b5589cd4a027a
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. */
7 /* libmp4v2 header */
8 #include "mp4.h"
10 #include "hb.h"
12 void AddIPodUUID(MP4FileHandle, MP4TrackId);
14 struct hb_mux_object_s
16 HB_MUX_COMMON;
18 hb_job_t * job;
20 /* libmp4v2 handle */
21 MP4FileHandle file;
23 /* Cumulated durations so far, in timescale units (see MP4Mux) */
24 uint64_t sum_dur;
26 /* Chapter state information for muxing */
27 MP4TrackId chapter_track;
28 int current_chapter;
29 uint64_t chapter_duration;
32 struct hb_mux_data_s
34 MP4TrackId track;
37 struct hb_text_sample_s
39 uint8_t sample[1280];
40 uint32_t length;
41 MP4Duration duration;
44 /**********************************************************************
45 * MP4CreateTextSample
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);
53 int x;
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 );
69 x = 2 + 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;
77 // Modifier Type Code
78 sample->sample[x+4] = 'e';
79 sample->sample[x+5] = 'n';
80 sample->sample[x+6] = 'c';
81 sample->sample[x+7] = 'd';
83 // Modifier Value
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;
90 return sample;
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 );
115 string = tmp_buffer;
118 return MP4CreateTextSample( string, duration );
122 /**********************************************************************
123 * MP4Init
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;
132 hb_audio_t * audio;
133 hb_mux_data_t * mux_data;
134 int i;
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.");
148 else
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!");
157 *job->die = 1;
158 return 0;
161 /* Video track */
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
168 timescale */
169 if (!(MP4SetTimeScale( m->file, job->arate )))
171 hb_error("muxmp4.c: MP4SetTimeScale failed!");
172 *job->die = 1;
173 return 0;
176 if( job->vcodec == HB_VCODEC_X264 )
178 /* Stolen from mp4creator */
179 if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
181 hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
182 *job->die = 1;
183 return 0;
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!");
211 *job->die = 1;
212 return 0;
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!");
220 *job->die = 1;
221 return 0;
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!");
230 *job->die = 1;
231 return 0;
235 /* apply the anamorphic transformation matrix if needed */
237 if( job->pixel_ratio ) {
239 uint8_t* val;
240 uint8_t nval[38];
241 uint32_t *ptr32 = (uint32_t*) (nval + 2);
242 uint32_t size;
244 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
246 if (size == 38) {
248 memcpy(nval, val, size);
250 float width, height;
251 width = job->pixel_aspect_width;
252 height = job->pixel_aspect_height;
254 uint32_t pixelRatioInt;
255 if (width >= height)
257 pixelRatioInt = (uint32_t)((width / height) * 0x10000);
259 #ifdef WORDS_BIGENDIAN
260 ptr32[0] = pixelRatioInt;
261 #else
262 /* we need to switch the endianness, as the file format expects big endian */
263 ptr32[0] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
264 #endif
267 else
269 pixelRatioInt = (uint32_t)((height / width) * 0x10000);
270 #ifdef WORDS_BIGENDIAN
271 ptr32[4] = pixelRatioInt;
272 #else
273 /* we need to switch the endianness, as the file format expects big endian */
274 ptr32[4] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
275 #endif
279 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
280 hb_log("Problem setting transform matrix");
287 /* end of transformation matrix */
289 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
290 MP4TrackId firstAudioTrack = 0;
292 /* add the audio tracks */
293 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
295 static u_int8_t reserved2[16] = {
296 0x00, 0x00, 0x00, 0x00,
297 0x00, 0x00, 0x00, 0x00,
298 0x00, 0x02, 0x00, 0x10,
299 0x00, 0x00, 0x00, 0x00,
302 audio = hb_list_item( title->list_audio, i );
303 mux_data = malloc( sizeof( hb_mux_data_t ) );
304 audio->mux_data = mux_data;
306 mux_data->track = MP4AddAudioTrack( m->file,
307 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
308 MP4SetAudioProfileLevel( m->file, 0x0F );
309 MP4SetTrackESConfiguration( m->file, mux_data->track,
310 audio->config.aac.bytes, audio->config.aac.length );
312 /* Set the language for this track */
313 /* The language is stored as 5-bit text - 0x60 */
314 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
315 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
316 language_code |= audio->iso639_2[2] - 0x60;
317 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
319 /* Set the correct number of channels for this track */
320 reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
321 MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
323 /* Set the audio track alternate group */
324 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
326 /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
327 // MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
329 /* store a reference to the first audio track,
330 so we can use it to feed the chapter text track's sample rate */
331 if (i == 0) {
332 firstAudioTrack = mux_data->track;
334 /* Enable the first audio track */
335 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
338 else
339 /* Disable the other audio tracks so QuickTime doesn't play
340 them all at once. */
342 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
343 hb_log("Disabled extra audio track %i", mux_data->track-1);
348 if (job->chapter_markers)
350 /* add a text track for the chapters */
351 MP4TrackId textTrack;
352 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
354 m->chapter_track = textTrack;
355 m->chapter_duration = 0;
356 m->current_chapter = job->chapter_start;
359 /* Add encoded-by metadata listing version and build date */
360 char *tool_string;
361 tool_string = (char *)malloc(80);
362 snprintf( tool_string, 80, "HandBrake %s %i", HB_VERSION, HB_BUILD);
363 MP4SetMetadataTool(m->file, tool_string);
364 free(tool_string);
366 return 0;
369 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
370 hb_buffer_t * buf )
372 hb_job_t * job = m->job;
374 uint64_t duration;
376 if( mux_data == job->mux_data )
378 /* Add the sample before the new frame.
379 It is important that this be calculated prior to the duration
380 of the new video sample, as we want to sync to right after it.
381 (This is because of how durations for text tracks work in QT) */
382 if( job->chapter_markers && buf->new_chap )
384 struct hb_text_sample_s *sample;
386 /* If this is an x264 encode with bframes the IDR frame we're
387 trying to mark will be displayed offset by its renderOffset
388 so we need to offset the chapter by the same amount.
389 MP4 render offsets don't seem to work for text tracks so
390 we have to fudge the duration instead. */
391 duration = m->sum_dur - m->chapter_duration;
393 if ( job->areBframes )
395 duration += buf->renderOffset * job->arate / 90000;
398 sample = MP4GenerateChapterSample( m, duration );
400 if( !MP4WriteSample(m->file,
401 m->chapter_track,
402 sample->sample,
403 sample->length,
404 sample->duration,
405 0, true) )
407 hb_error("Failed to write to output file, disk full?");
408 *job->die = 1;
410 free(sample);
411 m->current_chapter++;
412 m->chapter_duration += duration;
415 /* Video */
416 /* Because we use the audio samplerate as the timescale,
417 we have to use potentially variable durations so the video
418 doesn't go out of sync */
419 int64_t bias = ( buf->start * job->arate / 90000 ) - m->sum_dur;
420 duration = ( buf->stop - buf->start ) * job->arate / 90000 + bias;
421 m->sum_dur += duration;
423 else
425 /* Audio */
426 duration = MP4_INVALID_DURATION;
429 /* Here's where the sample actually gets muxed.
430 If it's an audio sample, don't offset the sample's playback.
431 If it's a video sample and there are no b-frames, ditto.
432 If there are b-frames, offset by the initDelay plus the
433 difference between the presentation time stamp x264 gives
434 and the decoding time stamp from the buffer data. */
435 if( !MP4WriteSample( m->file,
436 mux_data->track,
437 buf->data,
438 buf->size,
439 duration,
440 ((mux_data->track != 1) ||
441 (job->areBframes==0) ||
442 (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
443 ((buf->frametype & HB_FRAME_KEY) != 0) ) )
445 hb_error("Failed to write to output file, disk full?");
446 *job->die = 1;
449 return 0;
452 static int MP4End( hb_mux_object_t * m )
454 hb_job_t * job = m->job;
456 /* Write our final chapter marker */
457 if( m->job->chapter_markers )
459 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
461 if( !MP4WriteSample(m->file,
462 m->chapter_track,
463 sample->sample,
464 sample->length,
465 sample->duration,
466 0, true) )
468 hb_error("Failed to write to output file, disk full?");
469 *job->die = 1;
471 free(sample);
474 if (job->areBframes)
476 // Insert track edit to get A/V back in sync. The edit amount is
477 // the rendering offset of the first sample.
478 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
479 MP4GetTrackDuration(m->file, 1), 0);
480 if ( m->job->chapter_markers )
482 // apply same edit to chapter track to keep it in sync with video
483 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
484 MP4GetSampleRenderingOffset(m->file,1,1),
485 MP4GetTrackDuration(m->file, m->chapter_track), 0);
489 MP4Close( m->file );
491 if ( job->mp4_optimize )
493 hb_log( "muxmp4: optimizing file" );
494 char filename[1024]; memset( filename, 0, 1024 );
495 snprintf( filename, 1024, "%s.tmp", job->file );
496 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
497 remove( job->file );
498 rename( filename, job->file );
501 return 0;
504 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
506 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
507 m->init = MP4Init;
508 m->mux = MP4Mux;
509 m->end = MP4End;
510 m->job = job;
511 return m;