OSDN Git Service

Don't drop subtitles when crossing PTS discontinuities by using buffer sequence numbe...
[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                         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             if( !MP4WriteSample(m->file, 
365                                 m->chapter_track, 
366                                 sample->sample, 
367                                 sample->length, 
368                                 sample->duration, 
369                                 0, true) )
370             {
371                 hb_error("Failed to write to output file, disk full?");
372                 *job->die = 1;
373             }
374             free(sample);
375             m->current_chapter++;
376             m->chapter_duration = m->sum_dur;
377         }
378     
379         /* Video */
380         /* Because we use the audio samplerate as the timescale,
381            we have to use potentially variable durations so the video
382            doesn't go out of sync */
383         duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
384         m->sum_dur += duration;
385     }
386     else
387     {
388         /* Audio */
389         duration = MP4_INVALID_DURATION;
390     }
391
392     /* When we do get the first keyframe, use its duration as the
393        initial delay added to the frame order offset for b-frames.
394        Because of b-pyramid, double this duration when there are
395        b-pyramids, as denoted by job->areBframes equalling 2. */
396     if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
397     {
398         initDelay = buf->renderOffset;
399         thisSample++;
400     }
401
402     /* Here's where the sample actually gets muxed. 
403        If it's an audio sample, don't offset the sample's playback.
404        If it's a video sample and there are no b-frames, ditto.
405        If there are b-frames, offset by the initDelay plus the
406        difference between the presentation time stamp x264 gives
407        and the decoding time stamp from the buffer data. */
408     if( !MP4WriteSample( m->file, 
409                          mux_data->track, 
410                          buf->data, 
411                          buf->size,
412                          duration, 
413                          ((mux_data->track != 1) || 
414                           (job->areBframes==0) || 
415                           (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
416                          ((buf->frametype & HB_FRAME_KEY) != 0) ) )
417     {
418         hb_error("Failed to write to output file, disk full?");   
419         *job->die = 1;
420     }
421                                 
422     return 0;
423 }
424
425 static int MP4End( hb_mux_object_t * m )
426
427     hb_job_t   * job   = m->job;
428
429     /* Write our final chapter marker */
430     if( m->job->chapter_markers )
431     {
432         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
433     
434         if( !MP4WriteSample(m->file, 
435                             m->chapter_track, 
436                             sample->sample, 
437                             sample->length, 
438                             sample->duration, 
439                             0, true) )
440         {
441             hb_error("Failed to write to output file, disk full?");      
442             *job->die = 1;
443         }
444         free(sample);
445     }
446     
447 #if 0
448     hb_job_t * job = m->job;
449     char filename[1024]; memset( filename, 0, 1024 );
450 #endif
451
452     if (job->areBframes)
453     /* Walk the entire video sample table and find the minumum ctts value. */
454     {
455            MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
456            MP4SampleId i;
457            MP4Duration renderingOffset = 2000000000, tmp;
458            
459            // Find the smallest rendering offset
460            for(i = 1; i <= count; i++)
461            {
462                tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
463                if(tmp < renderingOffset)
464                    renderingOffset = tmp;
465            }
466            
467            // Adjust all ctts values down by renderingOffset
468            for(i = 1; i <= count; i++)
469            {
470                MP4SetSampleRenderingOffset(m->file,1,i,
471                    MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
472            }
473            
474            // Insert track edit to get A/V back in sync.  The edit amount is
475            // the rendering offset of the first sample.
476            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
477                MP4GetTrackDuration(m->file, 1), 0);
478      }
479
480     MP4Close( m->file );
481
482 #if 0
483     hb_log( "muxmp4: optimizing file" );
484     snprintf( filename, 1024, "%s.tmp", job->file );
485     MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
486     remove( job->file );
487     rename( filename, job->file );
488 #endif
489
490     return 0;
491 }
492
493 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
494 {
495     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
496     m->init      = MP4Init;
497     m->mux       = MP4Mux;
498     m->end       = MP4End;
499     m->job       = job;
500     return m;
501 }
502