OSDN Git Service

Allow mac gui to select AC-3 + AAC at the same time, still needs CLI work for the...
[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 struct hb_mux_object_s
15 {
16     HB_MUX_COMMON;
17
18     hb_job_t * job;
19
20     /* libmp4v2 handle */
21     MP4FileHandle file;
22
23     /* Cumulated durations so far, in timescale units (see MP4Mux) */
24     uint64_t sum_dur;
25         
26     /* Chapter state information for muxing */
27     MP4TrackId chapter_track;
28     int current_chapter;
29     uint64_t chapter_duration;
30 };
31
32 struct hb_mux_data_s
33 {
34     MP4TrackId track;
35 };
36
37 struct hb_text_sample_s
38 {
39     uint8_t     sample[1280];
40     uint32_t    length;
41     MP4Duration duration;
42 };
43
44 /**********************************************************************
45  * MP4CreateTextSample
46  **********************************************************************
47  * Creates a buffer for a text track sample
48  *********************************************************************/
49 static struct hb_text_sample_s *MP4CreateTextSample( char *textString, uint64_t duration )
50 {
51     struct hb_text_sample_s *sample = NULL;
52     int stringLength = strlen(textString);
53     int x;
54     
55     if( stringLength < 1024 )
56     {
57         sample = malloc( sizeof( struct hb_text_sample_s ) );
58
59         //textLength = (stringLength; // Account for BOM     
60         sample->length = stringLength + 2 + 12; // Account for text length code and other marker
61         sample->duration = (MP4Duration)duration;
62         
63         // 2-byte length marker
64         sample->sample[0] = (stringLength >> 8) & 0xff;
65         sample->sample[1] = stringLength & 0xff;
66         
67         strncpy( (char *)&(sample->sample[2]), textString, stringLength );
68         
69         x = 2 + stringLength;
70
71         // Modifier Length Marker
72         sample->sample[x] = 0x00;
73         sample->sample[x+1] = 0x00;
74         sample->sample[x+2] = 0x00;
75         sample->sample[x+3] = 0x0C;
76         
77         // Modifier Type Code
78         sample->sample[x+4] = 'e';
79         sample->sample[x+5] = 'n';
80         sample->sample[x+6] = 'c';
81         sample->sample[x+7] = 'd';
82         
83         // Modifier Value
84         sample->sample[x+8] = 0x00;
85         sample->sample[x+9] = 0x00;
86         sample->sample[x+10] = (256 >> 8) & 0xff;
87         sample->sample[x+11] = 256 & 0xff;
88     }
89     
90     return sample;
91 }
92  
93 /**********************************************************************
94  * MP4GenerateChapterSample
95  **********************************************************************
96  * Creates a buffer for a text track sample
97  *********************************************************************/
98 static struct hb_text_sample_s *MP4GenerateChapterSample( hb_mux_object_t * m, uint64_t duration )
99 {
100     int chapter = m->current_chapter;
101     hb_chapter_t *chapter_data = hb_list_item( m->job->title->list_chapter, chapter - 1 );
102     char tmp_buffer[1024];
103     char *string = tmp_buffer;
104     
105     tmp_buffer[0] = '\0';
106     
107     if( chapter_data != NULL )
108     {
109         string = chapter_data->title;
110     }
111     
112     if( strlen(string) == 0 || strlen(string) >= 1024 )
113     {
114         snprintf( tmp_buffer, 1023, "Chapter %03i", chapter );
115         string = tmp_buffer;
116     }
117     
118     return MP4CreateTextSample( string, duration );
119 }
120
121  
122 /**********************************************************************
123  * MP4Init
124  **********************************************************************
125  * Allocates hb_mux_data_t structures, create file and write headers
126  *********************************************************************/
127 static int MP4Init( hb_mux_object_t * m )
128 {
129     hb_job_t   * job   = m->job;
130     hb_title_t * title = job->title;
131     
132     hb_audio_t    * audio;
133     hb_mux_data_t * mux_data;
134     int i;
135     u_int16_t language_code;
136     
137     /* Flags for enabling/disabling tracks in an MP4. */
138     typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8}  track_header_flags;
139     
140
141     /* Create an empty mp4 file */
142     if (job->largeFileSize)
143     /* Use 64-bit MP4 file */
144     {
145         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA ); 
146         hb_log("Using 64-bit MP4 formatting.");
147     }
148     else
149     /* Limit MP4s to less than 4 GB */
150     {
151         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
152     }
153     
154     if (m->file == MP4_INVALID_FILE_HANDLE)
155     {
156         hb_error("muxmp4.c: MP4Create failed!");
157         *job->die = 1;
158         return 0;
159     }
160
161     /* Video track */
162     mux_data      = malloc( sizeof( hb_mux_data_t ) );
163     job->mux_data = mux_data;
164
165     /* When using the standard 90000 timescale, QuickTime tends to have
166        synchronization issues (audio not playing at the correct speed).
167        To workaround this, we use the audio samplerate as the
168        timescale */
169     if (!(MP4SetTimeScale( m->file, job->arate )))
170     {
171         hb_error("muxmp4.c: MP4SetTimeScale failed!");
172         *job->die = 1;
173         return 0;
174     }
175
176     if( job->vcodec == HB_VCODEC_X264 )
177     {
178         /* Stolen from mp4creator */
179         if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
180         {
181             hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
182             *job->die = 1;
183             return 0;
184         }
185
186                 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
187                         MP4_INVALID_DURATION, job->width, job->height,
188                         job->config.h264.sps[1], /* AVCProfileIndication */
189                         job->config.h264.sps[2], /* profile_compat */
190                         job->config.h264.sps[3], /* AVCLevelIndication */
191                         3 );      /* 4 bytes length before each NAL unit */
192                 
193
194         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
195                 job->config.h264.sps, job->config.h264.sps_length );
196         MP4AddH264PictureParameterSet( m->file, mux_data->track,
197                 job->config.h264.pps, job->config.h264.pps_length );
198
199                 if( job->h264_level == 30 || job->ipod_atom)
200                 {
201                         hb_log("About to add iPod atom");
202                         AddIPodUUID(m->file, mux_data->track);
203                 }
204
205     }
206     else /* FFmpeg or XviD */
207     {
208         if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
209         {
210             hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
211             *job->die = 1;
212             return 0;
213         }
214         mux_data->track = MP4AddVideoTrack( m->file, job->arate,
215                 MP4_INVALID_DURATION, job->width, job->height,
216                 MP4_MPEG4_VIDEO_TYPE );
217         if (mux_data->track == MP4_INVALID_TRACK_ID)
218         {
219             hb_error("muxmp4.c: MP4AddVideoTrack failed!");
220             *job->die = 1;
221             return 0;
222         }
223         
224
225         /* VOL from FFmpeg or XviD */
226         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
227                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
228         {
229             hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
230             *job->die = 1;
231             return 0;
232         }
233     }
234
235         /* apply the anamorphic transformation matrix if needed */
236
237         if( job->pixel_ratio ) {
238
239                 uint8_t* val;
240                 uint8_t nval[38];
241                 uint32_t *ptr32 = (uint32_t*) (nval + 2);
242                 uint32_t size;
243
244                 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
245
246                 if (size == 38) {
247
248                         memcpy(nval, val, size);
249
250                         float width, height;
251                         width = job->pixel_aspect_width;
252                         height = job->pixel_aspect_height;
253
254            uint32_t pixelRatioInt;
255            if (width >= height)
256            {
257                pixelRatioInt = (uint32_t)((width / height) * 0x10000);
258
259 #ifdef WORDS_BIGENDIAN
260                ptr32[0] = pixelRatioInt;
261 #else
262                /* we need to switch the endianness, as the file format expects big endian */
263                ptr32[0] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
264 #endif
265
266            }
267            else
268            {
269                pixelRatioInt = (uint32_t)((height / width) * 0x10000);
270 #ifdef WORDS_BIGENDIAN
271                ptr32[4] = pixelRatioInt;
272 #else
273                 /* we need to switch the endianness, as the file format expects big endian */
274                 ptr32[4] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
275 #endif
276             }
277
278
279                         if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
280                                 hb_log("Problem setting transform matrix");
281                         }
282                         
283                 }
284
285         }
286
287         /* end of transformation matrix */
288
289         /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
290         MP4TrackId firstAudioTrack = 0;
291
292         /* add the audio tracks */
293     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
294     {
295         static u_int8_t reserved2[16] = {
296                 0x00, 0x00, 0x00, 0x00, 
297                 0x00, 0x00, 0x00, 0x00, 
298                 0x00, 0x02, 0x00, 0x10,
299                 0x00, 0x00, 0x00, 0x00, 
300             };
301             
302         audio = hb_list_item( title->list_audio, i );
303         mux_data = malloc( sizeof( hb_mux_data_t ) );
304         audio->mux_data = mux_data;
305
306         if( job->acodec & HB_ACODEC_AC3 ||
307             job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
308         {
309             mux_data->track = MP4AddAC3AudioTrack( 
310                 m->file,
311                 job->arate, 1536, MP4_MPEG4_AUDIO_TYPE );  
312             MP4SetTrackBytesProperty( 
313                 m->file, mux_data->track,
314                 "udta.name.value", 
315                 (const u_int8_t*)"Surround", strlen("Surround"));
316         } else {
317             mux_data->track = MP4AddAudioTrack( 
318                 m->file,
319                 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
320             MP4SetTrackBytesProperty( 
321                 m->file, mux_data->track,
322                 "udta.name.value", 
323                 (const u_int8_t*)"Stereo", strlen("Stereo"));
324             
325             MP4SetAudioProfileLevel( m->file, 0x0F );
326             MP4SetTrackESConfiguration( 
327                 m->file, mux_data->track,
328                 audio->config.aac.bytes, audio->config.aac.length );
329
330             /* Set the correct number of channels for this track */
331             reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
332             MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
333
334         }
335         /* Set the language for this track */
336         /* The language is stored as 5-bit text - 0x60 */
337         language_code = audio->iso639_2[0] - 0x60;   language_code <<= 5;
338         language_code |= audio->iso639_2[1] - 0x60;  language_code <<= 5;
339         language_code |= audio->iso639_2[2] - 0x60;
340         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
341         
342
343         /* Set the audio track alternate group */
344         MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
345         
346         /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
347 //        MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels",  (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
348         
349         /* store a reference to the first audio track,
350         so we can use it to feed the chapter text track's sample rate */
351         if (i == 0) {
352             firstAudioTrack = mux_data->track;
353             
354             /* Enable the first audio track */
355             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
356         }
357
358         else
359             /* Disable the other audio tracks so QuickTime doesn't play
360                them all at once. */
361         {
362             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
363             hb_log("Disabled extra audio track %i", mux_data->track-1);
364         }
365                 
366     }
367
368         if (job->chapter_markers) 
369     {
370                 /* add a text track for the chapters */
371                 MP4TrackId textTrack;
372                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
373         
374         m->chapter_track = textTrack;
375         m->chapter_duration = 0;
376         m->current_chapter = job->chapter_start;
377         }
378         
379     /* Add encoded-by metadata listing version and build date */
380     char *tool_string;
381     tool_string = (char *)malloc(80);
382     snprintf( tool_string, 80, "HandBrake %s %i", HB_VERSION, HB_BUILD);
383     MP4SetMetadataTool(m->file, tool_string);
384     free(tool_string);
385         
386     return 0;
387 }
388
389 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
390                    hb_buffer_t * buf )
391 {
392     hb_job_t * job = m->job;
393
394     uint64_t duration;
395
396     if( mux_data == job->mux_data )
397     {    
398         /* Add the sample before the new frame.
399            It is important that this be calculated prior to the duration
400            of the new video sample, as we want to sync to right after it.
401            (This is because of how durations for text tracks work in QT) */
402         if( job->chapter_markers && buf->new_chap )
403         {
404             struct hb_text_sample_s *sample;
405
406             /* If this is an x264 encode with bframes the IDR frame we're
407                trying to mark will be displayed offset by its renderOffset
408                so we need to offset the chapter by the same amount.
409                MP4 render offsets don't seem to work for text tracks so
410                we have to fudge the duration instead. */
411             duration = m->sum_dur - m->chapter_duration;
412
413             if ( job->areBframes )
414             {
415                 duration += buf->renderOffset * job->arate / 90000;
416             }
417
418             sample = MP4GenerateChapterSample( m, duration );
419             
420             if( !MP4WriteSample(m->file, 
421                                 m->chapter_track, 
422                                 sample->sample, 
423                                 sample->length, 
424                                 sample->duration, 
425                                 0, true) )
426             {
427                 hb_error("Failed to write to output file, disk full?");
428                 *job->die = 1;
429             }
430             free(sample);
431             m->current_chapter++;
432             m->chapter_duration += duration;
433         }
434     
435         /* Video */
436         /* Because we use the audio samplerate as the timescale,
437            we have to use potentially variable durations so the video
438            doesn't go out of sync */
439         int64_t bias = ( buf->start * job->arate / 90000 ) - m->sum_dur;
440         duration = ( buf->stop - buf->start ) * job->arate / 90000 + bias;
441         m->sum_dur += duration;
442     }
443     else
444     {
445         /* Audio */
446         duration = MP4_INVALID_DURATION;
447     }
448
449     /* Here's where the sample actually gets muxed. 
450        If it's an audio sample, don't offset the sample's playback.
451        If it's a video sample and there are no b-frames, ditto.
452        If there are b-frames, offset by the initDelay plus the
453        difference between the presentation time stamp x264 gives
454        and the decoding time stamp from the buffer data. */
455     if( !MP4WriteSample( m->file, 
456                          mux_data->track, 
457                          buf->data, 
458                          buf->size,
459                          duration, 
460                          ((mux_data->track != 1) || 
461                           (job->areBframes==0) || 
462                           (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
463                          ((buf->frametype & HB_FRAME_KEY) != 0) ) )
464     {
465         hb_error("Failed to write to output file, disk full?");   
466         *job->die = 1;
467     }
468                                 
469     return 0;
470 }
471
472 static int MP4End( hb_mux_object_t * m )
473
474     hb_job_t   * job   = m->job;
475
476     /* Write our final chapter marker */
477     if( m->job->chapter_markers )
478     {
479         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
480     
481         if( !MP4WriteSample(m->file, 
482                             m->chapter_track, 
483                             sample->sample, 
484                             sample->length, 
485                             sample->duration, 
486                             0, true) )
487         {
488             hb_error("Failed to write to output file, disk full?");      
489             *job->die = 1;
490         }
491         free(sample);
492     }
493     
494     if (job->areBframes)
495     {
496            // Insert track edit to get A/V back in sync.  The edit amount is
497            // the rendering offset of the first sample.
498            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
499                MP4GetTrackDuration(m->file, 1), 0);
500             if ( m->job->chapter_markers )
501             {
502                 // apply same edit to chapter track to keep it in sync with video
503                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
504                                 MP4GetSampleRenderingOffset(m->file,1,1),
505                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
506             }
507      }
508
509     MP4Close( m->file );
510
511     if ( job->mp4_optimize )
512     {
513         hb_log( "muxmp4: optimizing file" );
514         char filename[1024]; memset( filename, 0, 1024 );
515         snprintf( filename, 1024, "%s.tmp", job->file );
516         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
517         remove( job->file );
518         rename( filename, job->file );
519     }
520
521     return 0;
522 }
523
524 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
525 {
526     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
527     m->init      = MP4Init;
528     m->mux       = MP4Mux;
529     m->end       = MP4End;
530     m->job       = job;
531     return m;
532 }
533