OSDN Git Service

First attempt at variable frame rate detelecining for NTSC video sources.
[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)
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         /* store a reference to the first audio track,
328         so we can use it to feed the chapter text track's sample rate */
329         if (i == 0) {
330             firstAudioTrack = mux_data->track;
331             
332             /* Enable the first audio track */
333             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
334         }
335
336         else
337             /* Disable the other audio tracks so QuickTime doesn't play
338                them all at once. */
339         {
340             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
341             hb_log("Disabled extra audio track %i", mux_data->track-1);
342         }
343                 
344     }
345
346         if (job->chapter_markers) 
347     {
348                 /* add a text track for the chapters */
349                 MP4TrackId textTrack;
350                 textTrack = MP4AddChapterTextTrack(m->file, firstAudioTrack);
351         
352         m->chapter_track = textTrack;
353         m->chapter_duration = 0;
354         m->current_chapter = job->chapter_start;
355         }
356         
357     return 0;
358 }
359
360 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
361                    hb_buffer_t * buf )
362 {
363     hb_job_t * job = m->job;
364
365     uint64_t duration;
366
367     if( mux_data == job->mux_data )
368     {    
369         /* Add the sample before the new frame.
370            It is important that this be calculated prior to the duration
371            of the new video sample, as we want to sync to right after it.
372            (This is because of how durations for text tracks work in QT) */
373         if( job->chapter_markers && buf->new_chap )
374         {
375             struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
376             
377             if( !MP4WriteSample(m->file, 
378                                 m->chapter_track, 
379                                 sample->sample, 
380                                 sample->length, 
381                                 sample->duration, 
382                                 0, true) )
383             {
384                 hb_error("Failed to write to output file, disk full?");
385                 *job->die = 1;
386             }
387             free(sample);
388             m->current_chapter++;
389             m->chapter_duration = m->sum_dur;
390         }
391     
392         /* Video */
393         /* Because we use the audio samplerate as the timescale,
394            we have to use potentially variable durations so the video
395            doesn't go out of sync */
396         if ( job->vfr )
397         {
398             duration    = ( ( buf->stop * job->arate / 90000 ) - ( buf->start * job->arate / 90000 ) );
399         }
400         else
401         {
402             duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
403         }
404         m->sum_dur += duration;
405     }
406     else
407     {
408         /* Audio */
409         duration = MP4_INVALID_DURATION;
410     }
411
412     /* When we do get the first keyframe, use its duration as the
413        initial delay added to the frame order offset for b-frames.
414        Because of b-pyramid, double this duration when there are
415        b-pyramids, as denoted by job->areBframes equalling 2. */
416     if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
417     {
418         initDelay = buf->renderOffset;
419         thisSample++;
420     }
421
422     /* Here's where the sample actually gets muxed. 
423        If it's an audio sample, don't offset the sample's playback.
424        If it's a video sample and there are no b-frames, ditto.
425        If there are b-frames, offset by the initDelay plus the
426        difference between the presentation time stamp x264 gives
427        and the decoding time stamp from the buffer data. */
428     if( !MP4WriteSample( m->file, 
429                          mux_data->track, 
430                          buf->data, 
431                          buf->size,
432                          duration, 
433                          ((mux_data->track != 1) || 
434                           (job->areBframes==0) || 
435                           (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
436                          ((buf->frametype & HB_FRAME_KEY) != 0) ) )
437     {
438         hb_error("Failed to write to output file, disk full?");   
439         *job->die = 1;
440     }
441                                 
442     return 0;
443 }
444
445 static int MP4End( hb_mux_object_t * m )
446
447     hb_job_t   * job   = m->job;
448
449     /* Write our final chapter marker */
450     if( m->job->chapter_markers )
451     {
452         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
453     
454         if( !MP4WriteSample(m->file, 
455                             m->chapter_track, 
456                             sample->sample, 
457                             sample->length, 
458                             sample->duration, 
459                             0, true) )
460         {
461             hb_error("Failed to write to output file, disk full?");      
462             *job->die = 1;
463         }
464         free(sample);
465     }
466     
467 #if 0
468     hb_job_t * job = m->job;
469     char filename[1024]; memset( filename, 0, 1024 );
470 #endif
471
472     if (job->areBframes)
473     /* Walk the entire video sample table and find the minumum ctts value. */
474     {
475            MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
476            MP4SampleId i;
477            MP4Duration renderingOffset = 2000000000, tmp;
478            
479            // Find the smallest rendering offset
480            for(i = 1; i <= count; i++)
481            {
482                tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
483                if(tmp < renderingOffset)
484                    renderingOffset = tmp;
485            }
486            
487            // Adjust all ctts values down by renderingOffset
488            for(i = 1; i <= count; i++)
489            {
490                MP4SetSampleRenderingOffset(m->file,1,i,
491                    MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
492            }
493            
494            // Insert track edit to get A/V back in sync.  The edit amount is
495            // the rendering offset of the first sample.
496            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
497                MP4GetTrackDuration(m->file, 1), 0);
498      }
499
500     MP4Close( m->file );
501
502 #if 0
503     hb_log( "muxmp4: optimizing file" );
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 #endif
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