Lessens the brownpantsification effect from the DVD Read Blocks Failed error message.
[HandBrake.git] / libhb / muxmp4.c
blobc029487b07590b501a8f2d1d7650978e108a93ab
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 /* B-frame muxing variables */
15 MP4SampleId thisSample = 0;
16 uint64_t initDelay;
18 struct hb_mux_object_s
20 HB_MUX_COMMON;
22 hb_job_t * job;
24 /* libmp4v2 handle */
25 MP4FileHandle file;
27 /* Cumulated durations so far, in timescale units (see MP4Mux) */
28 uint64_t sum_dur;
30 /* Chapter state information for muxing */
31 MP4TrackId chapter_track;
32 int current_chapter;
33 uint64_t chapter_duration;
36 struct hb_mux_data_s
38 MP4TrackId track;
41 struct hb_text_sample_s
43 uint8_t sample[1280];
44 uint32_t length;
45 MP4Duration duration;
48 /**********************************************************************
49 * MP4CreateTextSample
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);
57 int x;
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 );
73 x = 2 + 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;
81 // Modifier Type Code
82 sample->sample[x+4] = 'e';
83 sample->sample[x+5] = 'n';
84 sample->sample[x+6] = 'c';
85 sample->sample[x+7] = 'd';
87 // Modifier Value
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;
94 return sample;
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 );
119 string = tmp_buffer;
122 return MP4CreateTextSample( string, duration );
126 /**********************************************************************
127 * MP4Init
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;
136 hb_audio_t * audio;
137 hb_mux_data_t * mux_data;
138 int i;
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.");
152 else
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_log("muxmp4.c: MP4Create failed!");
161 *job->die = 1;
162 return 0;
165 /* Video track */
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
172 timescale */
173 if (!(MP4SetTimeScale( m->file, job->arate )))
175 hb_log("muxmp4.c: MP4SetTimeScale failed!");
176 *job->die = 1;
177 return 0;
180 if( job->vcodec == HB_VCODEC_X264 )
182 /* Stolen from mp4creator */
183 if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
185 hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
186 *job->die = 1;
187 return 0;
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_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
215 *job->die = 1;
216 return 0;
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_log("muxmp4.c: MP4AddVideoTrack failed!");
224 *job->die = 1;
225 return 0;
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_log("muxmp4.c: MP4SetTrackESConfiguration failed!");
234 *job->die = 1;
235 return 0;
239 /* apply the anamorphic transformation matrix if needed */
241 if( job->pixel_ratio ) {
243 uint8_t* val;
244 uint8_t nval[38];
245 uint32_t *ptr32 = (uint32_t*) (nval + 2);
246 uint32_t size;
248 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
250 if (size == 38) {
252 memcpy(nval, val, size);
254 float width, height;
255 float widthRatio;
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;
265 #else
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);
268 #endif
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 */
316 if (i == 0) {
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));
323 else
324 /* Disable the other audio tracks so QuickTime doesn't play
325 them all at once. */
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;
344 return 0;
347 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
348 hb_buffer_t * buf )
350 hb_job_t * job = m->job;
352 uint64_t duration;
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 MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
365 free(sample);
366 m->current_chapter++;
367 m->chapter_duration = m->sum_dur;
370 /* Video */
371 /* Because we use the audio samplerate as the timescale,
372 we have to use potentially variable durations so the video
373 doesn't go out of sync */
374 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
375 m->sum_dur += duration;
377 else
379 /* Audio */
380 duration = MP4_INVALID_DURATION;
383 /* When we do get the first keyframe, use its duration as the
384 initial delay added to the frame order offset for b-frames.
385 Because of b-pyramid, double this duration when there are
386 b-pyramids, as denoted by job->areBframes equalling 2. */
387 if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
389 initDelay = buf->renderOffset;
390 thisSample++;
393 /* Here's where the sample actually gets muxed.
394 If it's an audio sample, don't offset the sample's playback.
395 If it's a video sample and there are no b-frames, ditto.
396 If there are b-frames, offset by the initDelay plus the
397 difference between the presentation time stamp x264 gives
398 and the decoding time stamp from the buffer data. */
399 MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
400 duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
401 ((buf->frametype & HB_FRAME_KEY) != 0) );
403 return 0;
406 static int MP4End( hb_mux_object_t * m )
408 /* Write our final chapter marker */
409 if( m->job->chapter_markers )
411 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
413 MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
414 free(sample);
417 #if 0
418 hb_job_t * job = m->job;
419 char filename[1024]; memset( filename, 0, 1024 );
420 #endif
422 hb_job_t * job = m->job;
424 if (job->areBframes)
425 /* Walk the entire video sample table and find the minumum ctts value. */
427 MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
428 MP4SampleId i;
429 MP4Duration renderingOffset = 2000000000, tmp;
431 // Find the smallest rendering offset
432 for(i = 1; i <= count; i++)
434 tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
435 if(tmp < renderingOffset)
436 renderingOffset = tmp;
439 // Adjust all ctts values down by renderingOffset
440 for(i = 1; i <= count; i++)
442 MP4SetSampleRenderingOffset(m->file,1,i,
443 MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
446 // Insert track edit to get A/V back in sync. The edit amount is
447 // the rendering offset of the first sample.
448 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
449 MP4GetTrackDuration(m->file, 1), 0);
452 MP4Close( m->file );
454 #if 0
455 hb_log( "muxmp4: optimizing file" );
456 snprintf( filename, 1024, "%s.tmp", job->file );
457 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
458 remove( job->file );
459 rename( filename, job->file );
460 #endif
462 return 0;
465 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
467 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
468 m->init = MP4Init;
469 m->mux = MP4Mux;
470 m->end = MP4End;
471 m->job = job;
472 return m;