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) */
37 /**********************************************************************
39 **********************************************************************
40 * Allocates hb_mux_data_t structures, create file and write headers
41 *********************************************************************/
42 static int MP4Init( hb_mux_object_t * m )
44 hb_job_t * job = m->job;
45 hb_title_t * title = job->title;
48 hb_mux_data_t * mux_data;
50 u_int16_t language_code;
52 /* Create an empty mp4 file */
53 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
56 mux_data = malloc( sizeof( hb_mux_data_t ) );
57 job->mux_data = mux_data;
59 /* When using the standard 90000 timescale, QuickTime tends to have
60 synchronization issues (audio not playing at the correct speed).
61 To workaround this, we use the audio samplerate as the
63 MP4SetTimeScale( m->file, job->arate );
65 if( job->vcodec == HB_VCODEC_X264 )
67 /* Stolen from mp4creator */
68 MP4SetVideoProfileLevel( m->file, 0x7F );
70 if (job->areBframes >= 1)
72 hb_log("muxmp4: Adjusting duration for B-frames");
73 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
74 MP4_INVALID_DURATION+1, job->width, job->height,
75 job->config.h264.sps[1], /* AVCProfileIndication */
76 job->config.h264.sps[2], /* profile_compat */
77 job->config.h264.sps[3], /* AVCLevelIndication */
78 3 ); /* 4 bytes length before each NAL unit */
82 hb_log("muxmp4: Using default duration as there are no B-frames");
83 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
84 MP4_INVALID_DURATION, job->width, job->height,
85 job->config.h264.sps[1], /* AVCProfileIndication */
86 job->config.h264.sps[2], /* profile_compat */
87 job->config.h264.sps[3], /* AVCLevelIndication */
88 3 ); /* 4 bytes length before each NAL unit */
91 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
92 job->config.h264.sps, job->config.h264.sps_length );
93 MP4AddH264PictureParameterSet( m->file, mux_data->track,
94 job->config.h264.pps, job->config.h264.pps_length );
96 if( job->h264_level == 30)
98 hb_log("About to add iPod atom");
99 AddIPodUUID(m->file, mux_data->track);
103 else /* FFmpeg or XviD */
105 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
106 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
107 MP4_INVALID_DURATION, job->width, job->height,
108 MP4_MPEG4_VIDEO_TYPE );
110 /* VOL from FFmpeg or XviD */
111 MP4SetTrackESConfiguration( m->file, mux_data->track,
112 job->config.mpeg4.bytes, job->config.mpeg4.length );
115 /* apply the anamorphic transformation matrix if needed */
117 if( job->pixel_ratio ) {
121 uint32_t *ptr32 = (uint32_t*) (nval + 2);
124 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
128 memcpy(nval, val, size);
132 width = job->pixel_aspect_width;
133 height = job->pixel_aspect_height;
134 widthRatio = (width / height) * 0x10000;
136 uint32_t widthRatioInt;
137 widthRatioInt = (uint32_t)widthRatio;
139 #ifdef WORDS_BIGENDIAN
140 ptr32[0] = widthRatioInt;
142 /* we need to switch the endianness, as the file format expects big endian */
143 ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
146 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
147 hb_log("Problem setting transform matrix");
154 /* end of transformation matrix */
156 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
157 MP4TrackId firstAudioTrack;
159 /* add the audio tracks */
160 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
162 audio = hb_list_item( title->list_audio, i );
163 mux_data = malloc( sizeof( hb_mux_data_t ) );
164 audio->mux_data = mux_data;
166 mux_data->track = MP4AddAudioTrack( m->file,
167 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
168 MP4SetAudioProfileLevel( m->file, 0x0F );
169 MP4SetTrackESConfiguration( m->file, mux_data->track,
170 audio->config.aac.bytes, audio->config.aac.length );
172 /* Set the language for this track */
173 /* The language is stored as 5-bit text - 0x60 */
174 language_code = audio->iso639_2[0] - 0x60; language_code <<= 5;
175 language_code |= audio->iso639_2[1] - 0x60; language_code <<= 5;
176 language_code |= audio->iso639_2[2] - 0x60;
177 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
179 /* store a reference to the first audio track,
180 so we can use it to feed the chapter text track's sample rate */
182 firstAudioTrack = mux_data->track;
187 if (job->chapter_markers) {
189 /* add a text track for the chapters */
190 MP4TrackId textTrack;
192 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
194 /* write the chapter markers for each selected chapter */
196 hb_chapter_t * chapter;
197 MP4Duration chapterDuration;
198 float fOrigDuration, fTimescale;
201 for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
203 chapter = hb_list_item( title->list_chapter, i );
205 fOrigDuration = chapter->duration;
206 fTimescale = job->arate;
207 fTSDuration = (fOrigDuration / 90000) * fTimescale;
208 chapterDuration = (MP4Duration)fTSDuration;
210 sprintf(markerBuf, " Chapter %03i", i + 1);
212 markerBuf[1] = 11; // "Chapter xxx"
213 MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
222 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
225 hb_job_t * job = m->job;
229 if( mux_data == job->mux_data )
232 /* Because we use the audio samplerate as the timescale,
233 we have to use potentially variable durations so the video
234 doesn't go out of sync */
235 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
236 m->sum_dur += duration;
241 duration = MP4_INVALID_DURATION;
244 /* If for some reason the first frame muxmp4 gets isn't a key-frame,
245 drop frames until we get one. (Yes, very bad. Quick and dirty.)
246 This is for QuickTime--when it sees a non-IDR frame first, it
247 displays a white box instead of video until the second GOP.
248 Also, you've got to save the skipped duration to keep from
249 throwing off the offset values. */
250 if((mux_data->track == 1) && (thisSample == 0) && (buf->key != 1) && (job->vcodec == HB_VCODEC_X264))
252 initDelay +=duration;
255 /* When we do get the first keyframe, use its duration as the
256 initial delay added to the frame order offset for b-frames.
257 Because of b-pyramid, double this duration when there are
258 b-pyramids, as denoted by job->areBframes equalling 2. */
259 if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1) && (job->vcodec == HB_VCODEC_X264))
261 initDelay += duration * job->areBframes;
265 /* Here's where the sample actually gets muxed.
266 If it's an audio sample, don't offset the sample's playback.
267 If it's a video sample and there are no b-frames, ditto.
268 If there are b-frames, offset by the initDelay plus the
269 difference between the presentation time stamp x264 gives
270 and the decoding time stamp from the buffer data. */
271 MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
272 duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : ( initDelay + ((buf->encodedPTS - buf->start) * job->arate / 90000)),
278 static int MP4End( hb_mux_object_t * m )
281 hb_job_t * job = m->job;
282 char filename[1024]; memset( filename, 0, 1024 );
288 hb_log( "muxmp4: optimizing file" );
289 snprintf( filename, 1024, "%s.tmp", job->file );
290 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
292 rename( filename, job->file );
298 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
300 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );