OSDN Git Service

Ooops, I wanted height not width there.
[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.fr/>.
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 output & input timescale units (see MP4Mux) */
24     int64_t sum_dur;        // duration in output timescale units
25     int64_t sum_dur_in;     // duration in input 90KHz timescale units
26
27     // bias to keep render offsets in ctts atom positive (set up by encx264)
28     int64_t init_delay;
29
30     /* Chapter state information for muxing */
31     MP4TrackId chapter_track;
32     int current_chapter;
33     uint64_t chapter_duration;
34
35     /* Sample rate of the first audio track.
36      * Used for the timescale
37      */
38     int samplerate;
39 };
40
41 struct hb_mux_data_s
42 {
43     MP4TrackId track;
44 };
45
46 struct hb_text_sample_s
47 {
48     uint8_t     sample[1280];
49     uint32_t    length;
50     MP4Duration duration;
51 };
52
53 /**********************************************************************
54  * MP4CreateTextSample
55  **********************************************************************
56  * Creates a buffer for a text track sample
57  *********************************************************************/
58 static struct hb_text_sample_s *MP4CreateTextSample( char *textString, uint64_t duration )
59 {
60     struct hb_text_sample_s *sample = NULL;
61     int stringLength = strlen(textString);
62     int x;
63
64     if( stringLength < 1024 )
65     {
66         sample = malloc( sizeof( struct hb_text_sample_s ) );
67
68         //textLength = (stringLength; // Account for BOM
69         sample->length = stringLength + 2 + 12; // Account for text length code and other marker
70         sample->duration = (MP4Duration)duration;
71
72         // 2-byte length marker
73         sample->sample[0] = (stringLength >> 8) & 0xff;
74         sample->sample[1] = stringLength & 0xff;
75
76         strncpy( (char *)&(sample->sample[2]), textString, stringLength );
77
78         x = 2 + stringLength;
79
80         // Modifier Length Marker
81         sample->sample[x] = 0x00;
82         sample->sample[x+1] = 0x00;
83         sample->sample[x+2] = 0x00;
84         sample->sample[x+3] = 0x0C;
85
86         // Modifier Type Code
87         sample->sample[x+4] = 'e';
88         sample->sample[x+5] = 'n';
89         sample->sample[x+6] = 'c';
90         sample->sample[x+7] = 'd';
91
92         // Modifier Value
93         sample->sample[x+8] = 0x00;
94         sample->sample[x+9] = 0x00;
95         sample->sample[x+10] = (256 >> 8) & 0xff;
96         sample->sample[x+11] = 256 & 0xff;
97     }
98
99     return sample;
100 }
101
102 /**********************************************************************
103  * MP4GenerateChapterSample
104  **********************************************************************
105  * Creates a buffer for a text track sample
106  *********************************************************************/
107 static struct hb_text_sample_s *MP4GenerateChapterSample( hb_mux_object_t * m,
108                                                           uint64_t duration,
109                                                           int chapter )
110 {
111     // We substract 1 from the chapter number because the chapters start at
112     // 1 but our name array starts at 0. We substract another 1 because we're
113     // writing the text of the previous chapter mark (when we get the start
114     // of chapter 2 we know the duration of chapter 1 & can write its mark).
115     hb_chapter_t *chapter_data = hb_list_item( m->job->title->list_chapter,
116                                                chapter - 2 );
117     char tmp_buffer[1024];
118     char *string = tmp_buffer;
119
120     tmp_buffer[0] = '\0';
121
122     if( chapter_data != NULL )
123     {
124         string = chapter_data->title;
125     }
126
127     if( strlen(string) == 0 || strlen(string) >= 1024 )
128     {
129         snprintf( tmp_buffer, 1023, "Chapter %03i", chapter - 2 );
130         string = tmp_buffer;
131     }
132
133     return MP4CreateTextSample( string, duration );
134 }
135
136
137 /**********************************************************************
138  * MP4Init
139  **********************************************************************
140  * Allocates hb_mux_data_t structures, create file and write headers
141  *********************************************************************/
142 static int MP4Init( hb_mux_object_t * m )
143 {
144     hb_job_t   * job   = m->job;
145     hb_title_t * title = job->title;
146
147     hb_audio_t    * audio;
148     hb_mux_data_t * mux_data;
149     int i;
150     uint16_t language_code;
151
152     /* Flags for enabling/disabling tracks in an MP4. */
153     typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8}  track_header_flags;
154
155     if( (audio = hb_list_item(title->list_audio, 0)) != NULL )
156     {
157         /* Need the sample rate of the first audio track to use as the timescale. */
158         m->samplerate = audio->config.out.samplerate;
159         audio = NULL;
160     }
161     else
162     {
163         m->samplerate = 90000;
164     }
165
166     /* Create an empty mp4 file */
167     if (job->largeFileSize)
168     /* Use 64-bit MP4 file */
169     {
170         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
171         hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
172     }
173     else
174     /* Limit MP4s to less than 4 GB */
175     {
176         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
177     }
178
179     if (m->file == MP4_INVALID_FILE_HANDLE)
180     {
181         hb_error("muxmp4.c: MP4Create failed!");
182         *job->die = 1;
183         return 0;
184     }
185
186     /* Video track */
187     mux_data      = malloc( sizeof( hb_mux_data_t ) );
188     job->mux_data = mux_data;
189
190     /* When using the standard 90000 timescale, QuickTime tends to have
191        synchronization issues (audio not playing at the correct speed).
192        To workaround this, we use the audio samplerate as the
193        timescale */
194     if (!(MP4SetTimeScale( m->file, m->samplerate )))
195     {
196         hb_error("muxmp4.c: MP4SetTimeScale failed!");
197         *job->die = 1;
198         return 0;
199     }
200
201     if( job->vcodec == HB_VCODEC_X264 )
202     {
203         /* Stolen from mp4creator */
204         MP4SetVideoProfileLevel( m->file, 0x7F );
205                 mux_data->track = MP4AddH264VideoTrack( m->file, m->samplerate,
206                         MP4_INVALID_DURATION, job->width, job->height,
207                         job->config.h264.sps[1], /* AVCProfileIndication */
208                         job->config.h264.sps[2], /* profile_compat */
209                         job->config.h264.sps[3], /* AVCLevelIndication */
210                         3 );      /* 4 bytes length before each NAL unit */
211
212
213         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
214                 job->config.h264.sps, job->config.h264.sps_length );
215         MP4AddH264PictureParameterSet( m->file, mux_data->track,
216                 job->config.h264.pps, job->config.h264.pps_length );
217
218                 if( job->h264_level == 30 || job->ipod_atom)
219                 {
220                         hb_deep_log( 2, "muxmp4: adding iPod atom");
221                         MP4AddIPodUUID(m->file, mux_data->track);
222                 }
223
224         m->init_delay = job->config.h264.init_delay;
225     }
226     else /* FFmpeg or XviD */
227     {
228         MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
229         mux_data->track = MP4AddVideoTrack( m->file, m->samplerate,
230                 MP4_INVALID_DURATION, job->width, job->height,
231                 MP4_MPEG4_VIDEO_TYPE );
232         if (mux_data->track == MP4_INVALID_TRACK_ID)
233         {
234             hb_error("muxmp4.c: MP4AddVideoTrack failed!");
235             *job->die = 1;
236             return 0;
237         }
238
239
240         /* VOL from FFmpeg or XviD */
241         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
242                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
243         {
244             hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
245             *job->die = 1;
246             return 0;
247         }
248     }
249
250     // COLR atom for color and gamma correction.
251     // Per the notes at:
252     //   http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
253     //   http://forum.doom9.org/showthread.php?t=133982#post1090068
254     // the user can set it from job->color_matrix, otherwise by default
255     // we say anything that's likely to be HD content is ITU BT.709 and
256     // DVD, SD TV & other content is ITU BT.601.  We look at the title height
257     // rather than the job height here to get uncropped input dimensions.
258     if( job->color_matrix == 1 )
259     {
260         // ITU BT.601 DVD or SD TV content
261         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
262     }
263     else if( job->color_matrix == 2 )
264     {
265         // ITU BT.709 HD content
266         MP4AddColr(m->file, mux_data->track, 1, 1, 1);        
267     }
268     else if ( job->title->width >= 1280 || job->title->height >= 720 )
269     {
270         // we guess that 720p or above is ITU BT.709 HD content
271         MP4AddColr(m->file, mux_data->track, 1, 1, 1);
272     }
273     else
274     {
275         // ITU BT.601 DVD or SD TV content
276         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
277     }
278
279     if( job->pixel_ratio )
280     {
281         /* PASP atom for anamorphic video */
282         float width, height;
283
284         width = job->pixel_aspect_width;
285
286         height = job->pixel_aspect_height;
287
288         MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
289
290         MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
291     }
292
293         /* add the audio tracks */
294     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
295     {
296         static uint8_t reserved2[16] = {
297                 0x00, 0x00, 0x00, 0x00,
298                 0x00, 0x00, 0x00, 0x00,
299                 0x00, 0x02, 0x00, 0x10,
300                 0x00, 0x00, 0x00, 0x00,
301             };
302
303         audio = hb_list_item( title->list_audio, i );
304         mux_data = malloc( sizeof( hb_mux_data_t ) );
305         audio->priv.mux_data = mux_data;
306
307         if( audio->config.out.codec == HB_ACODEC_AC3 )
308         {
309             mux_data->track = MP4AddAC3AudioTrack(
310                 m->file,
311                 m->samplerate, 1536, MP4_MPEG4_AUDIO_TYPE );
312             if (audio->config.out.name == NULL) {
313                 MP4SetTrackBytesProperty(
314                     m->file, mux_data->track,
315                     "udta.name.value",
316                     (const uint8_t*)"Surround", strlen("Surround"));
317             }
318             else {
319                 MP4SetTrackBytesProperty(
320                     m->file, mux_data->track,
321                     "udta.name.value",
322                     (const uint8_t*)(audio->config.out.name),
323                     strlen(audio->config.out.name));
324             }
325         } else {
326             mux_data->track = MP4AddAudioTrack(
327                 m->file,
328                 m->samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
329             if (audio->config.out.name == NULL) {
330                 MP4SetTrackBytesProperty(
331                     m->file, mux_data->track,
332                     "udta.name.value",
333                     (const uint8_t*)"Stereo", strlen("Stereo"));
334             }
335             else {
336                 MP4SetTrackBytesProperty(
337                     m->file, mux_data->track,
338                     "udta.name.value",
339                     (const uint8_t*)(audio->config.out.name),
340                     strlen(audio->config.out.name));
341             }
342
343             MP4SetAudioProfileLevel( m->file, 0x0F );
344             MP4SetTrackESConfiguration(
345                 m->file, mux_data->track,
346                 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
347
348             /* Set the correct number of channels for this track */
349              MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
350         }
351
352         /* Set the language for this track */
353         /* The language is stored as 5-bit text - 0x60 */
354         language_code = audio->config.lang.iso639_2[0] - 0x60;   language_code <<= 5;
355         language_code |= audio->config.lang.iso639_2[1] - 0x60;  language_code <<= 5;
356         language_code |= audio->config.lang.iso639_2[2] - 0x60;
357         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
358
359         if( hb_list_count( title->list_audio ) > 1 )
360         {
361             /* Set the audio track alternate group */
362             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
363         }
364
365         if (i == 0) {
366             /* Enable the first audio track */
367             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
368         }
369         else
370             /* Disable the other audio tracks so QuickTime doesn't play
371                them all at once. */
372         {
373             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
374             hb_deep_log( 2, "muxp4: disabled extra audio track %i", mux_data->track-1);
375         }
376
377     }
378
379         if (job->chapter_markers)
380     {
381         /* add a text track for the chapters. We add the 'chap' atom to track
382            one which is usually the video track & should never be disabled.
383            The Quicktime spec says it doesn't matter which media track the
384            chap atom is on but it has to be an enabled track. */
385         MP4TrackId textTrack;
386         textTrack = MP4AddChapterTextTrack(m->file, 1);
387
388         m->chapter_track = textTrack;
389         m->chapter_duration = 0;
390         m->current_chapter = job->chapter_start;
391     }
392
393     /* Add encoded-by metadata listing version and build date */
394     char *tool_string;
395     tool_string = (char *)malloc(80);
396     snprintf( tool_string, 80, "HandBrake %s %i", HB_VERSION, HB_BUILD);
397     MP4SetMetadataTool(m->file, tool_string);
398     free(tool_string);
399
400     return 0;
401 }
402
403 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
404                    hb_buffer_t * buf )
405 {
406     hb_job_t * job = m->job;
407     int64_t duration;
408     int64_t offset = 0;
409
410     if( mux_data == job->mux_data )
411     {
412         /* Video */
413
414         // if there are b-frames compute the render offset
415         // (we'll need it for both the video frame & the chapter track)
416         if ( m->init_delay )
417         {
418             offset = ( buf->start + m->init_delay ) * m->samplerate / 90000 -
419                      m->sum_dur;
420         }
421         /* Add the sample before the new frame.
422            It is important that this be calculated prior to the duration
423            of the new video sample, as we want to sync to right after it.
424            (This is because of how durations for text tracks work in QT) */
425         if( job->chapter_markers && buf->new_chap )
426         {
427             struct hb_text_sample_s *sample;
428
429             // this chapter is postioned by writing out the previous chapter.
430             // the duration of the previous chapter is the duration up to but
431             // not including the current frame minus the duration of all
432             // chapters up to the previous.
433             duration = m->sum_dur - m->chapter_duration + offset;
434             if ( duration <= 0 )
435             {
436                 /* The initial & final chapters can have very short durations
437                  * (less than the error in our total duration estimate) so
438                  * the duration calc above can result in a negative number.
439                  * when this happens give the chapter a short duration (1/3
440                  * of an ntsc frame time). */
441                 duration = 1000 * m->samplerate / 90000;
442             }
443
444             sample = MP4GenerateChapterSample( m, duration, buf->new_chap );
445
446             if( !MP4WriteSample(m->file,
447                                 m->chapter_track,
448                                 sample->sample,
449                                 sample->length,
450                                 sample->duration,
451                                 0, true) )
452             {
453                 hb_error("Failed to write to output file, disk full?");
454                 *job->die = 1;
455             }
456             free(sample);
457             m->current_chapter = buf->new_chap;
458             m->chapter_duration += duration;
459         }
460
461         // since we're changing the sample rate we need to keep track of
462         // the truncation bias so that the audio and video don't go out
463         // of sync. m->sum_dur_in is the sum of the input durations so far.
464         // m->sum_dur is the sum of the output durations. Their difference
465         // (in output sample rate units) is the accumulated truncation bias.
466         int64_t bias = ( m->sum_dur_in * m->samplerate / 90000 ) - m->sum_dur;
467         int64_t dur_in = buf->stop - buf->start;
468         duration = dur_in * m->samplerate / 90000 + bias;
469         if ( duration <= 0 )
470         {
471             /* We got an illegal mp4/h264 duration. This shouldn't
472                be possible and usually indicates a bug in the upstream code.
473                Complain in the hope that someone will go find the bug but
474                try to fix the error so that the file will still be playable. */
475             hb_log("MP4Mux: illegal duration %lld, bias %lld, start %lld (%lld),"
476                    "stop %lld (%lld), sum_dur %lld",
477                    duration, bias, buf->start * m->samplerate / 90000, buf->start,
478                    buf->stop * m->samplerate / 90000, buf->stop, m->sum_dur );
479             /* we don't know when the next frame starts so we can't pick a
480                valid duration for this one so we pick something "short"
481                (roughly 1/3 of an NTSC frame time) and rely on the bias calc
482                for the next frame to correct things (a duration underestimate
483                just results in a large bias on the next frame). */
484             duration = 1000 * m->samplerate / 90000;
485         }
486         m->sum_dur += duration;
487         m->sum_dur_in += dur_in;
488     }
489     else
490     {
491         /* Audio */
492         duration = MP4_INVALID_DURATION;
493     }
494
495     // Here's where the sample actually gets muxed.
496     if( !MP4WriteSample( m->file,
497                          mux_data->track,
498                          buf->data,
499                          buf->size,
500                          duration,
501                          offset,
502                          ((buf->frametype & HB_FRAME_KEY) != 0) ) )
503     {
504         hb_error("Failed to write to output file, disk full?");
505         *job->die = 1;
506     }
507
508     return 0;
509 }
510
511 static int MP4End( hb_mux_object_t * m )
512 {
513     hb_job_t   * job   = m->job;
514
515     /* Write our final chapter marker */
516     if( m->job->chapter_markers )
517     {
518         int64_t duration = m->sum_dur - m->chapter_duration;
519         /* The final chapter can have a very short duration - if it's less
520          * than a second just skip it. */
521         if ( duration >= m->samplerate )
522         {
523
524             struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, duration,
525                                                     m->current_chapter + 1 );
526             if( ! MP4WriteSample(m->file, m->chapter_track, sample->sample,
527                                  sample->length, sample->duration, 0, true) )
528             {
529                 hb_error("Failed to write to output file, disk full?");
530                 *job->die = 1;
531             }
532             free(sample);
533         }
534     }
535
536     if (job->areBframes)
537     {
538            // Insert track edit to get A/V back in sync.  The edit amount is
539            // the init_delay.
540            int64_t edit_amt = m->init_delay * m->samplerate / 90000;
541            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
542                            MP4GetTrackDuration(m->file, 1), 0);
543             if ( m->job->chapter_markers )
544             {
545                 // apply same edit to chapter track to keep it in sync with video
546                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
547                                 edit_amt,
548                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
549             }
550      }
551
552     MP4Close( m->file );
553
554     if ( job->mp4_optimize )
555     {
556         hb_log( "muxmp4: optimizing file" );
557         char filename[1024]; memset( filename, 0, 1024 );
558         snprintf( filename, 1024, "%s.tmp", job->file );
559         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
560         remove( job->file );
561         rename( filename, job->file );
562     }
563
564     return 0;
565 }
566
567 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
568 {
569     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
570     m->init      = MP4Init;
571     m->mux       = MP4Mux;
572     m->end       = MP4End;
573     m->job       = job;
574     return m;
575 }
576