OSDN Git Service

Ooops -- when copying and pasting code snippets, it helps to have the right types.
[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_error("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_error("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_error("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 || job->ipod_atom)
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_error("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_error("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_error("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                         width = job->pixel_aspect_width;
256                         height = job->pixel_aspect_height;
257
258            uint32_t pixelRatioInt;
259            if (width >= height)
260            {
261                pixelRatioInt = (uint32_t)((width / height) * 0x10000);
262
263 #ifdef WORDS_BIGENDIAN
264                ptr32[0] = pixelRatioInt;
265 #else
266                /* we need to switch the endianness, as the file format expects big endian */
267                ptr32[0] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
268 #endif
269
270            }
271            else
272            {
273                pixelRatioInt = (uint32_t)((height / width) * 0x10000);
274 #ifdef WORDS_BIGENDIAN
275                ptr32[4] = pixelRatioInt;
276 #else
277                 /* we need to switch the endianness, as the file format expects big endian */
278                 ptr32[4] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
279 #endif
280             }
281
282
283                         if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
284                                 hb_log("Problem setting transform matrix");
285                         }
286                         
287                 }
288
289         }
290
291         /* end of transformation matrix */
292
293         /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
294         MP4TrackId firstAudioTrack = 0;
295
296         /* add the audio tracks */
297     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
298     {
299         static u_int8_t reserved2[16] = {
300                 0x00, 0x00, 0x00, 0x00, 
301                 0x00, 0x00, 0x00, 0x00, 
302                 0x00, 0x02, 0x00, 0x10,
303                 0x00, 0x00, 0x00, 0x00, 
304             };
305             
306         audio = hb_list_item( title->list_audio, i );
307         mux_data = malloc( sizeof( hb_mux_data_t ) );
308         audio->mux_data = mux_data;
309
310         mux_data->track = MP4AddAudioTrack( m->file,
311                 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
312         MP4SetAudioProfileLevel( m->file, 0x0F );
313         MP4SetTrackESConfiguration( m->file, mux_data->track,
314                 audio->config.aac.bytes, audio->config.aac.length );
315                 
316         /* Set the language for this track */
317         /* The language is stored as 5-bit text - 0x60 */
318         language_code = audio->iso639_2[0] - 0x60;   language_code <<= 5;
319         language_code |= audio->iso639_2[1] - 0x60;  language_code <<= 5;
320         language_code |= audio->iso639_2[2] - 0x60;
321         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
322         
323         /* Set the correct number of channels for this track */
324         reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
325         MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
326
327         /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
328 //        MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels",  (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
329         
330         /* store a reference to the first audio track,
331         so we can use it to feed the chapter text track's sample rate */
332         if (i == 0) {
333             firstAudioTrack = mux_data->track;
334             
335             /* Enable the first audio track */
336             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
337         }
338
339         else
340             /* Disable the other audio tracks so QuickTime doesn't play
341                them all at once. */
342         {
343             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
344             hb_log("Disabled extra audio track %i", mux_data->track-1);
345         }
346                 
347     }
348
349         if (job->chapter_markers) 
350     {
351                 /* add a text track for the chapters */
352                 MP4TrackId textTrack;
353                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
354         
355         m->chapter_track = textTrack;
356         m->chapter_duration = 0;
357         m->current_chapter = job->chapter_start;
358         }
359         
360     return 0;
361 }
362
363 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
364                    hb_buffer_t * buf )
365 {
366     hb_job_t * job = m->job;
367
368     uint64_t duration;
369
370     if( mux_data == job->mux_data )
371     {    
372         /* Add the sample before the new frame.
373            It is important that this be calculated prior to the duration
374            of the new video sample, as we want to sync to right after it.
375            (This is because of how durations for text tracks work in QT) */
376         if( job->chapter_markers && buf->new_chap )
377         {
378             struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
379             
380             if( !MP4WriteSample(m->file, 
381                                 m->chapter_track, 
382                                 sample->sample, 
383                                 sample->length, 
384                                 sample->duration, 
385                                 0, true) )
386             {
387                 hb_error("Failed to write to output file, disk full?");
388                 *job->die = 1;
389             }
390             free(sample);
391             m->current_chapter++;
392             m->chapter_duration = m->sum_dur;
393         }
394     
395         /* Video */
396         /* Because we use the audio samplerate as the timescale,
397            we have to use potentially variable durations so the video
398            doesn't go out of sync */
399         if ( job->vfr )
400         {
401             duration    = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
402         }
403         else
404         {
405             duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
406         }
407         m->sum_dur += duration;
408     }
409     else
410     {
411         /* Audio */
412         duration = MP4_INVALID_DURATION;
413     }
414
415     /* When we do get the first keyframe, use its duration as the
416        initial delay added to the frame order offset for b-frames.
417        Because of b-pyramid, double this duration when there are
418        b-pyramids, as denoted by job->areBframes equalling 2. */
419     if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
420     {
421         initDelay = buf->renderOffset;
422         thisSample++;
423     }
424
425     /* Here's where the sample actually gets muxed. 
426        If it's an audio sample, don't offset the sample's playback.
427        If it's a video sample and there are no b-frames, ditto.
428        If there are b-frames, offset by the initDelay plus the
429        difference between the presentation time stamp x264 gives
430        and the decoding time stamp from the buffer data. */
431     if( !MP4WriteSample( m->file, 
432                          mux_data->track, 
433                          buf->data, 
434                          buf->size,
435                          duration, 
436                          ((mux_data->track != 1) || 
437                           (job->areBframes==0) || 
438                           (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
439                          ((buf->frametype & HB_FRAME_KEY) != 0) ) )
440     {
441         hb_error("Failed to write to output file, disk full?");   
442         *job->die = 1;
443     }
444                                 
445     return 0;
446 }
447
448 static int MP4End( hb_mux_object_t * m )
449
450     hb_job_t   * job   = m->job;
451
452     /* Write our final chapter marker */
453     if( m->job->chapter_markers )
454     {
455         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
456     
457         if( !MP4WriteSample(m->file, 
458                             m->chapter_track, 
459                             sample->sample, 
460                             sample->length, 
461                             sample->duration, 
462                             0, true) )
463         {
464             hb_error("Failed to write to output file, disk full?");      
465             *job->die = 1;
466         }
467         free(sample);
468     }
469     
470     if (job->areBframes)
471     /* Walk the entire video sample table and find the minumum ctts value. */
472     {
473            MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
474            MP4SampleId i;
475            MP4Duration renderingOffset = 2000000000, tmp;
476            
477            // Find the smallest rendering offset
478            for(i = 1; i <= count; i++)
479            {
480                tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
481                if(tmp < renderingOffset)
482                    renderingOffset = tmp;
483            }
484            
485            // Adjust all ctts values down by renderingOffset
486            for(i = 1; i <= count; i++)
487            {
488                MP4SetSampleRenderingOffset(m->file,1,i,
489                    MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
490            }
491            
492            // Insert track edit to get A/V back in sync.  The edit amount is
493            // the rendering offset of the first sample.
494            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
495                MP4GetTrackDuration(m->file, 1), 0);
496      }
497
498     MP4Close( m->file );
499
500     if ( job->mp4_optimize )
501     {
502         hb_log( "muxmp4: optimizing file" );
503         char filename[1024]; memset( filename, 0, 1024 );
504         snprintf( filename, 1024, "%s.tmp", job->file );
505         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
506         remove( job->file );
507         rename( filename, job->file );
508     }
509
510     return 0;
511 }
512
513 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
514 {
515     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
516     m->init      = MP4Init;
517     m->mux       = MP4Mux;
518     m->end       = MP4End;
519     m->job       = job;
520     return m;
521 }
522