OSDN Git Service

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