OSDN Git Service

Various fixes:
[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     /* Create an empty mp4 file */
142     m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
143     if (m->file == MP4_INVALID_FILE_HANDLE)
144     {
145         hb_log("muxmp4.c: MP4Create failed!");
146         *job->die = 1;
147         return 0;
148     }
149
150     /* Video track */
151     mux_data      = malloc( sizeof( hb_mux_data_t ) );
152     job->mux_data = mux_data;
153
154     /* When using the standard 90000 timescale, QuickTime tends to have
155        synchronization issues (audio not playing at the correct speed).
156        To workaround this, we use the audio samplerate as the
157        timescale */
158     if (!(MP4SetTimeScale( m->file, job->arate )))
159     {
160         hb_log("muxmp4.c: MP4SetTimeScale failed!");
161         *job->die = 1;
162         return 0;
163     }
164
165     if( job->vcodec == HB_VCODEC_X264 )
166     {
167         /* Stolen from mp4creator */
168         if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
169         {
170             hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
171             *job->die = 1;
172             return 0;
173         }
174
175                 mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
176                         MP4_INVALID_DURATION, job->width, job->height,
177                         job->config.h264.sps[1], /* AVCProfileIndication */
178                         job->config.h264.sps[2], /* profile_compat */
179                         job->config.h264.sps[3], /* AVCLevelIndication */
180                         3 );      /* 4 bytes length before each NAL unit */
181                 
182
183         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
184                 job->config.h264.sps, job->config.h264.sps_length );
185         MP4AddH264PictureParameterSet( m->file, mux_data->track,
186                 job->config.h264.pps, job->config.h264.pps_length );
187
188                 if( job->h264_level == 30)
189                 {
190                         hb_log("About to add iPod atom");
191                         AddIPodUUID(m->file, mux_data->track);
192                 }
193
194     }
195     else /* FFmpeg or XviD */
196     {
197         if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
198         {
199             hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
200             *job->die = 1;
201             return 0;
202         }
203         mux_data->track = MP4AddVideoTrack( m->file, job->arate,
204                 MP4_INVALID_DURATION, job->width, job->height,
205                 MP4_MPEG4_VIDEO_TYPE );
206         if (mux_data->track == MP4_INVALID_TRACK_ID)
207         {
208             hb_log("muxmp4.c: MP4AddVideoTrack failed!");
209             *job->die = 1;
210             return 0;
211         }
212         
213
214         /* VOL from FFmpeg or XviD */
215         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
216                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
217         {
218             hb_log("muxmp4.c: MP4SetTrackESConfiguration failed!");
219             *job->die = 1;
220             return 0;
221         }
222     }
223
224         /* apply the anamorphic transformation matrix if needed */
225
226         if( job->pixel_ratio ) {
227
228                 uint8_t* val;
229                 uint8_t nval[38];
230                 uint32_t *ptr32 = (uint32_t*) (nval + 2);
231                 uint32_t size;
232
233                 MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
234
235                 if (size == 38) {
236
237                         memcpy(nval, val, size);
238
239                         float width, height;
240                         float widthRatio;
241                         width = job->pixel_aspect_width;
242                         height = job->pixel_aspect_height;
243                         widthRatio = (width / height) * 0x10000;
244
245                         uint32_t widthRatioInt;
246                         widthRatioInt = (uint32_t)widthRatio;
247
248 #ifdef WORDS_BIGENDIAN
249                         ptr32[0] = widthRatioInt;
250 #else
251                         /* we need to switch the endianness, as the file format expects big endian */
252                         ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
253 #endif
254
255                         if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
256                                 hb_log("Problem setting transform matrix");
257                         }
258                         
259                 }
260
261         }
262
263         /* end of transformation matrix */
264
265         /* firstAudioTrack will be used to reference the first audio track when we add a chapter track */
266         MP4TrackId firstAudioTrack = 0;
267
268         /* add the audio tracks */
269     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
270     {
271         static u_int8_t reserved2[16] = {
272                 0x00, 0x00, 0x00, 0x00, 
273                 0x00, 0x00, 0x00, 0x00, 
274                 0x00, 0x02, 0x00, 0x10,
275                 0x00, 0x00, 0x00, 0x00, 
276             };
277             
278         audio = hb_list_item( title->list_audio, i );
279         mux_data = malloc( sizeof( hb_mux_data_t ) );
280         audio->mux_data = mux_data;
281
282         mux_data->track = MP4AddAudioTrack( m->file,
283                 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
284         MP4SetAudioProfileLevel( m->file, 0x0F );
285         MP4SetTrackESConfiguration( m->file, mux_data->track,
286                 audio->config.aac.bytes, audio->config.aac.length );
287                 
288         /* Set the language for this track */
289         /* The language is stored as 5-bit text - 0x60 */
290         language_code = audio->iso639_2[0] - 0x60;   language_code <<= 5;
291         language_code |= audio->iso639_2[1] - 0x60;  language_code <<= 5;
292         language_code |= audio->iso639_2[2] - 0x60;
293         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
294         
295         /* Set the correct number of channels for this track */
296         reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
297         MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
298                                 
299                 /* store a reference to the first audio track,
300                 so we can use it to feed the chapter text track's sample rate */
301                 if (i == 0) {
302                         firstAudioTrack = mux_data->track;
303                 }
304                 
305     }
306
307         if (job->chapter_markers) 
308     {
309                 /* add a text track for the chapters */
310                 MP4TrackId textTrack;
311                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
312         
313         m->chapter_track = textTrack;
314         m->chapter_duration = 0;
315         m->current_chapter = job->chapter_start;
316         }
317         
318     return 0;
319 }
320
321 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
322                    hb_buffer_t * buf )
323 {
324     hb_job_t * job = m->job;
325
326     uint64_t duration;
327
328     if( mux_data == job->mux_data )
329     {    
330         /* Add the sample before the new frame.
331            It is important that this be calculated prior to the duration
332            of the new video sample, as we want to sync to right after it.
333            (This is because of how durations for text tracks work in QT) */
334         if( job->chapter_markers && buf->new_chap )
335         {
336             struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
337         
338             MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
339             free(sample);
340             m->current_chapter++;
341             m->chapter_duration = m->sum_dur;
342         }
343     
344         /* Video */
345         /* Because we use the audio samplerate as the timescale,
346            we have to use potentially variable durations so the video
347            doesn't go out of sync */
348         duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
349         m->sum_dur += duration;
350     }
351     else
352     {
353         /* Audio */
354         duration = MP4_INVALID_DURATION;
355     }
356
357     /* When we do get the first keyframe, use its duration as the
358        initial delay added to the frame order offset for b-frames.
359        Because of b-pyramid, double this duration when there are
360        b-pyramids, as denoted by job->areBframes equalling 2. */
361     if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1) && (job->vcodec == HB_VCODEC_X264))
362     {
363         initDelay = buf->renderOffset;
364         thisSample++;
365     }
366
367     /* Here's where the sample actually gets muxed. 
368        If it's an audio sample, don't offset the sample's playback.
369        If it's a video sample and there are no b-frames, ditto.
370        If there are b-frames, offset by the initDelay plus the
371        difference between the presentation time stamp x264 gives
372        and the decoding time stamp from the buffer data. */
373        MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
374             duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
375             (buf->key == 1) );
376                                 
377     return 0;
378 }
379
380 static int MP4End( hb_mux_object_t * m )
381 {
382     /* Write our final chapter marker */
383     if( m->job->chapter_markers )
384     {
385         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
386     
387         MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
388         free(sample);
389     }
390     
391 #if 0
392     hb_job_t * job = m->job;
393     char filename[1024]; memset( filename, 0, 1024 );
394 #endif
395
396     hb_job_t * job = m->job;
397     
398     if (job->areBframes)
399     /* Walk the entire video sample table and find the minumum ctts value. */
400     {
401            MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
402            MP4SampleId i;
403            MP4Duration renderingOffset = 2000000000, tmp;
404            
405            // Find the smallest rendering offset
406            for(i = 1; i <= count; i++)
407            {
408                tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
409                if(tmp < renderingOffset)
410                    renderingOffset = tmp;
411            }
412            
413            // Adjust all ctts values down by renderingOffset
414            for(i = 1; i <= count; i++)
415            {
416                MP4SetSampleRenderingOffset(m->file,1,i,
417                    MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
418            }
419            
420            // Insert track edit to get A/V back in sync.  The edit amount is
421            // the rendering offset of the first sample.
422            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
423                MP4GetTrackDuration(m->file, 1), 0);
424      }
425
426     MP4Close( m->file );
427
428 #if 0
429     hb_log( "muxmp4: optimizing file" );
430     snprintf( filename, 1024, "%s.tmp", job->file );
431     MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
432     remove( job->file );
433     rename( filename, job->file );
434 #endif
435
436     return 0;
437 }
438
439 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
440 {
441     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
442     m->init      = MP4Init;
443     m->mux       = MP4Mux;
444     m->end       = MP4End;
445     m->job       = job;
446     return m;
447 }
448