OSDN Git Service

Finally recommitting clee's patch from r518 to allow 64-bit mp4 files that can hold...
[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     /* Chapter state information for muxing */
31     MP4TrackId chapter_track;
32     int current_chapter;
33     uint64_t chapter_duration;
34 };
35
36 struct hb_mux_data_s
37 {
38     MP4TrackId track;
39 };
40
41 struct hb_text_sample_s
42 {
43     uint8_t     sample[1280];
44     uint32_t    length;
45     MP4Duration duration;
46 };
47
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 )
54 {
55     struct hb_text_sample_s *sample = NULL;
56     int stringLength = strlen(textString);
57     int x;
58     
59     if( stringLength < 1024 )
60     {
61         sample = malloc( sizeof( struct hb_text_sample_s ) );
62
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;
66         
67         // 2-byte length marker
68         sample->sample[0] = (stringLength >> 8) & 0xff;
69         sample->sample[1] = stringLength & 0xff;
70         
71         strncpy( (char *)&(sample->sample[2]), textString, stringLength );
72         
73         x = 2 + stringLength;
74
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;
80         
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';
86         
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;
92     }
93     
94     return sample;
95 }
96  
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 )
103 {
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;
108     
109     tmp_buffer[0] = '\0';
110     
111     if( chapter_data != NULL )
112     {
113         string = chapter_data->title;
114     }
115     
116     if( strlen(string) == 0 || strlen(string) >= 1024 )
117     {
118         snprintf( tmp_buffer, 1023, "Chapter %03i", chapter );
119         string = tmp_buffer;
120     }
121     
122     return MP4CreateTextSample( string, duration );
123 }
124
125  
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 )
132 {
133     hb_job_t   * job   = m->job;
134     hb_title_t * title = job->title;
135     
136     hb_audio_t    * audio;
137     hb_mux_data_t * mux_data;
138     int i;
139     u_int16_t language_code;
140     
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;
143     
144
145     /* Create an empty mp4 file */
146     if (job->largeFileSize)
147     /* Use 64-bit MP4 file */
148     {
149         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA ); 
150         hb_log("Using 64-bit MP4 formatting.");
151     }
152     else
153     /* Limit MP4s to less than 4 GB */
154     {
155         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
156     }
157     
158     if (m->file == MP4_INVALID_FILE_HANDLE)
159     {
160         hb_log("muxmp4.c: MP4Create failed!");
161         *job->die = 1;
162         return 0;
163     }
164
165     /* Video track */
166     mux_data      = malloc( sizeof( hb_mux_data_t ) );
167     job->mux_data = mux_data;
168
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 )))
174     {
175         hb_log("muxmp4.c: MP4SetTimeScale failed!");
176         *job->die = 1;
177         return 0;
178     }
179
180     if( job->vcodec == HB_VCODEC_X264 )
181     {
182         /* Stolen from mp4creator */
183         if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
184         {
185             hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
186             *job->die = 1;
187             return 0;
188         }
189
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 */
196                 
197
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 );
202
203                 if( job->h264_level == 30)
204                 {
205                         hb_log("About to add iPod atom");
206                         AddIPodUUID(m->file, mux_data->track);
207                 }
208
209     }
210     else /* FFmpeg or XviD */
211     {
212         if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
213         {
214             hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
215             *job->die = 1;
216             return 0;
217         }
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)
222         {
223             hb_log("muxmp4.c: MP4AddVideoTrack failed!");
224             *job->die = 1;
225             return 0;
226         }
227         
228
229         /* VOL from FFmpeg or XviD */
230         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
231                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
232         {
233             hb_log("muxmp4.c: MP4SetTrackESConfiguration failed!");
234             *job->die = 1;
235             return 0;
236         }
237     }
238
239         /* apply the anamorphic transformation matrix if needed */
240
241         if( job->pixel_ratio ) {
242
243                 uint8_t* val;
244                 uint8_t nval[38];
245                 uint32_t *ptr32 = (uint32_t*) (nval + 2);
246                 uint32_t size;
247
248                 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
249
250                 if (size == 38) {
251
252                         memcpy(nval, val, size);
253
254                         float width, height;
255                         float widthRatio;
256                         width = job->pixel_aspect_width;
257                         height = job->pixel_aspect_height;
258                         widthRatio = (width / height) * 0x10000;
259
260                         uint32_t widthRatioInt;
261                         widthRatioInt = (uint32_t)widthRatio;
262
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
269
270                         if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
271                                 hb_log("Problem setting transform matrix");
272                         }
273                         
274                 }
275
276         }
277
278         /* end of transformation matrix */
279
280         /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
281         MP4TrackId firstAudioTrack = 0;
282
283         /* add the audio tracks */
284     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
285     {
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, 
291             };
292             
293         audio = hb_list_item( title->list_audio, i );
294         mux_data = malloc( sizeof( hb_mux_data_t ) );
295         audio->mux_data = mux_data;
296
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 );
302                 
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);
309         
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));
313         
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;
318             
319             /* Enable the first audio track */
320             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
321         }
322
323         else
324             /* Disable the other audio tracks so QuickTime doesn't play
325                them all at once. */
326         {
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);
329         }
330                 
331     }
332
333         if (job->chapter_markers) 
334     {
335                 /* add a text track for the chapters */
336                 MP4TrackId textTrack;
337                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
338         
339         m->chapter_track = textTrack;
340         m->chapter_duration = 0;
341         m->current_chapter = job->chapter_start;
342         }
343         
344     return 0;
345 }
346
347 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
348                    hb_buffer_t * buf )
349 {
350     hb_job_t * job = m->job;
351
352     uint64_t duration;
353
354     if( mux_data == job->mux_data )
355     {    
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 )
361         {
362             struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
363         
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;
368         }
369     
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;
376     }
377     else
378     {
379         /* Audio */
380         duration = MP4_INVALID_DURATION;
381     }
382
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->key == 1) && (job->vcodec == HB_VCODEC_X264))
388     {
389         initDelay = buf->renderOffset;
390         thisSample++;
391     }
392
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->key == 1) );
402                                 
403     return 0;
404 }
405
406 static int MP4End( hb_mux_object_t * m )
407 {
408     /* Write our final chapter marker */
409     if( m->job->chapter_markers )
410     {
411         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
412     
413         MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
414         free(sample);
415     }
416     
417 #if 0
418     hb_job_t * job = m->job;
419     char filename[1024]; memset( filename, 0, 1024 );
420 #endif
421
422     hb_job_t * job = m->job;
423     
424     if (job->areBframes)
425     /* Walk the entire video sample table and find the minumum ctts value. */
426     {
427            MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
428            MP4SampleId i;
429            MP4Duration renderingOffset = 2000000000, tmp;
430            
431            // Find the smallest rendering offset
432            for(i = 1; i <= count; i++)
433            {
434                tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
435                if(tmp < renderingOffset)
436                    renderingOffset = tmp;
437            }
438            
439            // Adjust all ctts values down by renderingOffset
440            for(i = 1; i <= count; i++)
441            {
442                MP4SetSampleRenderingOffset(m->file,1,i,
443                    MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
444            }
445            
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);
450      }
451
452     MP4Close( m->file );
453
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
461
462     return 0;
463 }
464
465 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
466 {
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;
473 }
474