OSDN Git Service

Successfully create MP4 files bigger than 4GB
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
1 /* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 titer Exp $
2
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. */
6
7 /* libmp4v2 header */
8 #include "mp4.h"
9
10 #include "hb.h"
11
12 void AddIPodUUID(MP4FileHandle, MP4TrackId);
13
14 /* B-frame muxing variables */
15 MP4SampleId thisSample = 0;
16 uint64_t initDelay;
17
18 struct hb_mux_object_s
19 {
20     HB_MUX_COMMON;
21
22     hb_job_t * job;
23
24     /* libmp4v2 handle */
25     MP4FileHandle file;
26
27     /* Cumulated durations so far, in timescale units (see MP4Mux) */
28     uint64_t sum_dur;
29         
30 };
31
32 struct hb_mux_data_s
33 {
34     MP4TrackId track;
35 };
36
37 /**********************************************************************
38  * MP4Init
39  **********************************************************************
40  * Allocates hb_mux_data_t structures, create file and write headers
41  *********************************************************************/
42 static int MP4Init( hb_mux_object_t * m )
43 {
44     hb_job_t   * job   = m->job;
45     hb_title_t * title = job->title;
46     
47     hb_audio_t    * audio;
48     hb_mux_data_t * mux_data;
49     int i;
50     u_int16_t language_code;
51
52     /* Create an empty mp4 file */
53     m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
54
55     /* Video track */
56     mux_data      = malloc( sizeof( hb_mux_data_t ) );
57     job->mux_data = mux_data;
58
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
62        timescale */
63     MP4SetTimeScale( m->file, job->arate );
64
65     if( job->vcodec == HB_VCODEC_X264 )
66     {
67         /* Stolen from mp4creator */
68         MP4SetVideoProfileLevel( m->file, 0x7F );
69
70                 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
71                         MP4_INVALID_DURATION, job->width, job->height,
72                         job->config.h264.sps[1], /* AVCProfileIndication */
73                         job->config.h264.sps[2], /* profile_compat */
74                         job->config.h264.sps[3], /* AVCLevelIndication */
75                         3 );      /* 4 bytes length before each NAL unit */
76                 
77
78         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
79                 job->config.h264.sps, job->config.h264.sps_length );
80         MP4AddH264PictureParameterSet( m->file, mux_data->track,
81                 job->config.h264.pps, job->config.h264.pps_length );
82
83                 if( job->h264_level == 30)
84                 {
85                         hb_log("About to add iPod atom");
86                         AddIPodUUID(m->file, mux_data->track);
87                 }
88
89     }
90     else /* FFmpeg or XviD */
91     {
92         MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
93         mux_data->track = MP4AddVideoTrack( m->file, job->arate,
94                 MP4_INVALID_DURATION, job->width, job->height,
95                 MP4_MPEG4_VIDEO_TYPE );
96
97         /* VOL from FFmpeg or XviD */
98         MP4SetTrackESConfiguration( m->file, mux_data->track,
99                 job->config.mpeg4.bytes, job->config.mpeg4.length );
100     }
101
102         /* apply the anamorphic transformation matrix if needed */
103
104         if( job->pixel_ratio ) {
105
106                 uint8_t* val;
107                 uint8_t nval[38];
108                 uint32_t *ptr32 = (uint32_t*) (nval + 2);
109                 uint32_t size;
110
111                 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
112
113                 if (size == 38) {
114
115                         memcpy(nval, val, size);
116
117                         float width, height;
118                         float widthRatio;
119                         width = job->pixel_aspect_width;
120                         height = job->pixel_aspect_height;
121                         widthRatio = (width / height) * 0x10000;
122
123                         uint32_t widthRatioInt;
124                         widthRatioInt = (uint32_t)widthRatio;
125
126 #ifdef WORDS_BIGENDIAN
127                         ptr32[0] = widthRatioInt;
128 #else
129                         /* we need to switch the endianness, as the file format expects big endian */
130                         ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
131 #endif
132
133                         if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
134                                 hb_log("Problem setting transform matrix");
135                         }
136                         
137                 }
138
139         }
140
141         /* end of transformation matrix */
142
143         /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
144         MP4TrackId firstAudioTrack = 0;
145
146         /* add the audio tracks */
147     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
148     {
149         audio = hb_list_item( title->list_audio, i );
150         mux_data = malloc( sizeof( hb_mux_data_t ) );
151         audio->mux_data = mux_data;
152
153         mux_data->track = MP4AddAudioTrack( m->file,
154                 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
155         MP4SetAudioProfileLevel( m->file, 0x0F );
156         MP4SetTrackESConfiguration( m->file, mux_data->track,
157                 audio->config.aac.bytes, audio->config.aac.length );
158                 
159         /* Set the language for this track */
160         /* The language is stored as 5-bit text - 0x60 */
161         language_code = audio->iso639_2[0] - 0x60;   language_code <<= 5;
162         language_code |= audio->iso639_2[1] - 0x60;  language_code <<= 5;
163         language_code |= audio->iso639_2[2] - 0x60;
164         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
165                                 
166                 /* store a reference to the first audio track,
167                 so we can use it to feed the chapter text track's sample rate */
168                 if (i == 0) {
169                         firstAudioTrack = mux_data->track;
170                 }
171                 
172     }
173
174         if (job->chapter_markers) {
175
176                 /* add a text track for the chapters */
177                 MP4TrackId textTrack;
178
179                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
180
181                 /* write the chapter markers for each selected chapter */
182                 char markerBuf[13];
183                 hb_chapter_t  * chapter;
184                 MP4Duration chapterDuration;
185                 float fOrigDuration, fTimescale;
186                 float fTSDuration;
187
188                 for( i = job->chapter_start - 1; i <= job->chapter_end - 1; i++ )
189                 {
190                         chapter = hb_list_item( title->list_chapter, i );
191
192                         fOrigDuration = chapter->duration;
193                         fTimescale = job->arate;
194                         fTSDuration = (fOrigDuration / 90000) * fTimescale;
195                         chapterDuration = (MP4Duration)fTSDuration;
196                         
197                         sprintf(markerBuf, "  Chapter %03i", i + 1);
198                         markerBuf[0] = 0;
199                         markerBuf[1] = 11; // "Chapter xxx"
200                         MP4WriteSample(m->file, textTrack, (u_int8_t*)markerBuf, 13, chapterDuration, 0, true);
201
202                 }
203                 
204         }
205         
206     return 0;
207 }
208
209 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
210                    hb_buffer_t * buf )
211 {
212     hb_job_t * job = m->job;
213
214     uint64_t duration;
215
216     if( mux_data == job->mux_data )
217     {
218         /* Video */
219         /* Because we use the audio samplerate as the timescale,
220            we have to use potentially variable durations so the video
221            doesn't go out of sync */
222         duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
223         m->sum_dur += duration;
224     }
225     else
226     {
227         /* Audio */
228         duration = MP4_INVALID_DURATION;
229     }
230
231     /* When we do get the first keyframe, use its duration as the
232        initial delay added to the frame order offset for b-frames.
233        Because of b-pyramid, double this duration when there are
234        b-pyramids, as denoted by job->areBframes equalling 2. */
235     if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1) && (job->vcodec == HB_VCODEC_X264))
236     {
237         initDelay = buf->renderOffset;
238         thisSample++;
239     }
240
241     /* Here's where the sample actually gets muxed. 
242        If it's an audio sample, don't offset the sample's playback.
243        If it's a video sample and there are no b-frames, ditto.
244        If there are b-frames, offset by the initDelay plus the
245        difference between the presentation time stamp x264 gives
246        and the decoding time stamp from the buffer data. */
247        MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
248             duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
249             (buf->key == 1) );
250                                 
251     return 0;
252 }
253
254 static int MP4End( hb_mux_object_t * m )
255 {
256 #if 0
257     hb_job_t * job = m->job;
258     char filename[1024]; memset( filename, 0, 1024 );
259 #endif
260
261     hb_job_t * job = m->job;
262     
263     if (job->areBframes)
264     /* Walk the entire video sample table and find the minumum ctts value. */
265     {
266            MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
267            MP4SampleId i;
268            MP4Duration renderingOffset = 2000000000, tmp;
269            
270            // Find the smallest rendering offset
271            for(i = 1; i <= count; i++)
272            {
273                tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
274                if(tmp < renderingOffset)
275                    renderingOffset = tmp;
276            }
277            
278            // Adjust all ctts values down by renderingOffset
279            for(i = 1; i <= count; i++)
280            {
281                MP4SetSampleRenderingOffset(m->file,1,i,
282                    MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
283            }
284            
285            // Insert track edit to get A/V back in sync.  The edit amount is
286            // the rendering offset of the first sample.
287            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
288                MP4GetTrackDuration(m->file, 1), 0);
289      }
290
291     MP4Close( m->file );
292
293 #if 0
294     hb_log( "muxmp4: optimizing file" );
295     snprintf( filename, 1024, "%s.tmp", job->file );
296     MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
297     remove( job->file );
298     rename( filename, job->file );
299 #endif
300
301     return 0;
302 }
303
304 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
305 {
306     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
307     m->init      = MP4Init;
308     m->mux       = MP4Mux;
309     m->end       = MP4End;
310     m->job       = job;
311     return m;
312 }
313