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 /* Create an empty mp4 file */
142 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
143 if (m->file == MP4_INVALID_FILE_HANDLE)
145 hb_log("muxmp4.c: MP4Create failed!");
151 mux_data = malloc( sizeof( hb_mux_data_t ) );
152 job->mux_data = mux_data;
154 /* When using the standard 90000 timescale, QuickTime tends to have
155 synchronization issues (audio not playing at the correct speed).
156 To workaround this, we use the audio samplerate as the
158 if (!(MP4SetTimeScale( m->file, job->arate )))
160 hb_log("muxmp4.c: MP4SetTimeScale failed!");
165 if( job->vcodec == HB_VCODEC_X264 )
167 /* Stolen from mp4creator */
168 if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
170 hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
175 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
176 MP4_INVALID_DURATION, job->width, job->height,
177 job->config.h264.sps[1], /* AVCProfileIndication */
178 job->config.h264.sps[2], /* profile_compat */
179 job->config.h264.sps[3], /* AVCLevelIndication */
180 3 ); /* 4 bytes length before each NAL unit */
183 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
184 job->config.h264.sps, job->config.h264.sps_length );
185 MP4AddH264PictureParameterSet( m->file, mux_data->track,
186 job->config.h264.pps, job->config.h264.pps_length );
188 if( job->h264_level == 30)
190 hb_log("About to add iPod atom");
191 AddIPodUUID(m->file, mux_data->track);
195 else /* FFmpeg or XviD */
197 if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
199 hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
203 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
204 MP4_INVALID_DURATION, job->width, job->height,
205 MP4_MPEG4_VIDEO_TYPE );
206 if (mux_data->track == MP4_INVALID_TRACK_ID)
208 hb_log("muxmp4.c: MP4AddVideoTrack failed!");
214 /* VOL from FFmpeg or XviD */
215 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
216 job->config.mpeg4.bytes, job->config.mpeg4.length )))
218 hb_log("muxmp4.c: MP4SetTrackESConfiguration failed!");
224 /* apply the anamorphic transformation matrix if needed */
226 if( job->pixel_ratio ) {
230 uint32_t *ptr32 = (uint32_t*) (nval + 2);
233 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
237 memcpy(nval, val, size);
241 width = job->pixel_aspect_width;
242 height = job->pixel_aspect_height;
243 widthRatio = (width / height) * 0x10000;
245 uint32_t widthRatioInt;
246 widthRatioInt = (uint32_t)widthRatio;
248 #ifdef WORDS_BIGENDIAN
249 ptr32[0] = widthRatioInt;
251 /* we need to switch the endianness, as the file format expects big endian */
252 ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
255 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
256 hb_log("Problem setting transform matrix");
263 /* end of transformation matrix */
265 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
266 MP4TrackId firstAudioTrack = 0;
268 /* add the audio tracks */
269 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
271 static u_int8_t reserved2[16] = {
272 0x00, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x02, 0x00, 0x10,
275 0x00, 0x00, 0x00, 0x00,
278 audio = hb_list_item( title->list_audio, i );
279 mux_data = malloc( sizeof( hb_mux_data_t ) );
280 audio->mux_data = mux_data;
282 mux_data->track = MP4AddAudioTrack( m->file,
283 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
284 MP4SetAudioProfileLevel( m->file, 0x0F );
285 MP4SetTrackESConfiguration( m->file, mux_data->track,
286 audio->config.aac.bytes, audio->config.aac.length );
288 /* Set the language for this track */
289 /* The language is stored as 5-bit text - 0x60 */
290 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
291 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
292 language_code |= audio->iso639_2[2] - 0x60;
293 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
295 /* Set the correct number of channels for this track */
296 reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
297 MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
299 /* store a reference to the first audio track,
300 so we can use it to feed the chapter text track's sample rate */
302 firstAudioTrack = mux_data->track;
307 if (job->chapter_markers)
309 /* add a text track for the chapters */
310 MP4TrackId textTrack;
311 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
313 m->chapter_track = textTrack;
314 m->chapter_duration = 0;
315 m->current_chapter = job->chapter_start;
321 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
324 hb_job_t * job = m->job;
328 if( mux_data == job->mux_data )
330 /* Add the sample before the new frame.
331 It is important that this be calculated prior to the duration
332 of the new video sample, as we want to sync to right after it.
333 (This is because of how durations for text tracks work in QT) */
334 if( job->chapter_markers && buf->new_chap )
336 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
338 MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
340 m->current_chapter++;
341 m->chapter_duration = m->sum_dur;
345 /* Because we use the audio samplerate as the timescale,
346 we have to use potentially variable durations so the video
347 doesn't go out of sync */
348 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
349 m->sum_dur += duration;
354 duration = MP4_INVALID_DURATION;
357 /* When we do get the first keyframe, use its duration as the
358 initial delay added to the frame order offset for b-frames.
359 Because of b-pyramid, double this duration when there are
360 b-pyramids, as denoted by job->areBframes equalling 2. */
361 if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1) && (job->vcodec == HB_VCODEC_X264))
363 initDelay = buf->renderOffset;
367 /* Here's where the sample actually gets muxed.
368 If it's an audio sample, don't offset the sample's playback.
369 If it's a video sample and there are no b-frames, ditto.
370 If there are b-frames, offset by the initDelay plus the
371 difference between the presentation time stamp x264 gives
372 and the decoding time stamp from the buffer data. */
373 MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
374 duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
380 static int MP4End( hb_mux_object_t * m )
382 /* Write our final chapter marker */
383 if( m->job->chapter_markers )
385 struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
387 MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
392 hb_job_t * job = m->job;
393 char filename[1024]; memset( filename, 0, 1024 );
396 hb_job_t * job = m->job;
399 /* Walk the entire video sample table and find the minumum ctts value. */
401 MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
403 MP4Duration renderingOffset = 2000000000, tmp;
405 // Find the smallest rendering offset
406 for(i = 1; i <= count; i++)
408 tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
409 if(tmp < renderingOffset)
410 renderingOffset = tmp;
413 // Adjust all ctts values down by renderingOffset
414 for(i = 1; i <= count; i++)
416 MP4SetSampleRenderingOffset(m->file,1,i,
417 MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
420 // Insert track edit to get A/V back in sync. The edit amount is
421 // the rendering offset of the first sample.
422 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
423 MP4GetTrackDuration(m->file, 1), 0);
429 hb_log( "muxmp4: optimizing file" );
430 snprintf( filename, 1024, "%s.tmp", job->file );
431 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
433 rename( filename, job->file );
439 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
441 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );