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);
15 struct hb_mux_object_s
24 /* Cumulated durations so far, in timescale units (see MP4Mux) */
34 /**********************************************************************
36 **********************************************************************
37 * Allocates hb_mux_data_t structures, create file and write headers
38 *********************************************************************/
39 static int MP4Init( hb_mux_object_t * m )
41 hb_job_t * job = m->job;
42 hb_title_t * title = job->title;
45 hb_mux_data_t * mux_data;
48 /* Create an empty mp4 file */
49 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
52 mux_data = malloc( sizeof( hb_mux_data_t ) );
53 job->mux_data = mux_data;
55 /* When using the standard 90000 timescale, QuickTime tends to have
56 synchronization issues (audio not playing at the correct speed).
57 To workaround this, we use the audio samplerate as the
59 MP4SetTimeScale( m->file, job->arate );
61 if( job->vcodec == HB_VCODEC_X264 )
63 /* Stolen from mp4creator */
64 MP4SetVideoProfileLevel( m->file, 0x7F );
66 if (job->areBframes == 1)
68 hb_log("muxmp4: Adjusting duration for B-frames");
69 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
70 MP4_INVALID_DURATION+1, job->width, job->height,
71 job->config.h264.sps[1], /* AVCProfileIndication */
72 job->config.h264.sps[2], /* profile_compat */
73 job->config.h264.sps[3], /* AVCLevelIndication */
74 3 ); /* 4 bytes length before each NAL unit */
78 hb_log("muxmp4: Using default duration as there are no B-frames");
79 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
80 MP4_INVALID_DURATION, job->width, job->height,
81 job->config.h264.sps[1], /* AVCProfileIndication */
82 job->config.h264.sps[2], /* profile_compat */
83 job->config.h264.sps[3], /* AVCLevelIndication */
84 3 ); /* 4 bytes length before each NAL unit */
87 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
88 job->config.h264.sps, job->config.h264.sps_length );
89 MP4AddH264PictureParameterSet( m->file, mux_data->track,
90 job->config.h264.pps, job->config.h264.pps_length );
92 if( job->h264_level == 30)
94 hb_log("About to add iPod atom");
95 AddIPodUUID(m->file, mux_data->track);
99 else /* FFmpeg or XviD */
101 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
102 mux_data->track = MP4AddVideoTrack( m->file, job->arate,
103 MP4_INVALID_DURATION, job->width, job->height,
104 MP4_MPEG4_VIDEO_TYPE );
106 /* VOL from FFmpeg or XviD */
107 MP4SetTrackESConfiguration( m->file, mux_data->track,
108 job->config.mpeg4.bytes, job->config.mpeg4.length );
111 /* apply the anamorphic transformation matrix if needed */
113 if( job->pixel_ratio ) {
117 uint32_t *ptr32 = (uint32_t*) (nval + 2);
120 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
124 memcpy(nval, val, size);
128 width = job->pixel_aspect_width;
129 height = job->pixel_aspect_height;
130 widthRatio = (width / height) * 0x10000;
132 uint32_t widthRatioInt;
133 widthRatioInt = (uint32_t)widthRatio;
135 #ifdef WORDS_BIGENDIAN
136 ptr32[0] = widthRatioInt;
138 /* we need to switch the endianness, as the file format expects big endian */
139 ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
142 if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
143 hb_log("Problem setting transform matrix");
150 /* end of transformation matrix */
152 /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
153 MP4TrackId firstAudioTrack;
155 /* add the audio tracks */
156 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
158 audio = hb_list_item( title->list_audio, i );
159 mux_data = malloc( sizeof( hb_mux_data_t ) );
160 audio->mux_data = mux_data;
162 mux_data->track = MP4AddAudioTrack( m->file,
163 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
164 MP4SetAudioProfileLevel( m->file, 0x0F );
165 MP4SetTrackESConfiguration( m->file, mux_data->track,
166 audio->config.aac.bytes, audio->config.aac.length );
168 /* store a reference to the first audio track,
169 so we can use it to feed the chapter text track's sample rate */
171 firstAudioTrack = mux_data->track;
176 if (job->chapter_markers) {
178 /* add a text track for the chapters */
179 MP4TrackId textTrack;
181 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
183 /* write the chapter markers for each selected chapter */
185 hb_chapter_t * chapter;
186 MP4Duration chapterDuration;
187 float fOrigDuration, fTimescale;
190 for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
192 chapter = hb_list_item( title->list_chapter, i );
194 fOrigDuration = chapter->duration;
195 fTimescale = job->arate;
196 fTSDuration = (fOrigDuration / 90000) * fTimescale;
197 chapterDuration = (MP4Duration)fTSDuration;
199 sprintf(markerBuf, " Chapter %03i", i + 1);
201 markerBuf[1] = 11; // "Chapter xxx"
202 MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
211 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
214 hb_job_t * job = m->job;
218 if( mux_data == job->mux_data )
221 /* Because we use the audio samplerate as the timescale,
222 we have to use potentially variable durations so the video
223 doesn't go out of sync */
224 duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
225 m->sum_dur += duration;
230 duration = MP4_INVALID_DURATION;
233 MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
234 duration, 0, buf->key );
238 static int MP4End( hb_mux_object_t * m )
241 hb_job_t * job = m->job;
242 char filename[1024]; memset( filename, 0, 1024 );
248 hb_log( "muxmp4: optimizing file" );
249 snprintf( filename, 1024, "%s.tmp", job->file );
250 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
252 rename( filename, job->file );
258 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
260 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );