1 /* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 titer Exp $
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. */
7 #include "mp4v2/mp4v2.h"
8 #include "a52dec/a52.h"
12 struct hb_mux_object_s
21 int64_t sum_dur; // sum of video frame durations so far
23 hb_buffer_t *delay_buf;
25 /* Chapter state information for muxing */
26 MP4TrackId chapter_track;
28 uint64_t chapter_duration;
37 uint64_t sum_dur; // sum of the frame durations so far
40 /* Tune video track chunk duration.
41 * libmp4v2 default duration == dusamplerate == 1 second.
42 * Per van's suggestion we desire duration == 4 frames.
43 * Should be invoked immediately after track creation.
45 * return true on fail, false on success.
47 static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId )
52 tscale = MP4GetTrackTimeScale( m->file, trackId );
53 dur = (MP4Duration)ceil( (double)tscale * (double)m->job->vrate_base / (double)m->job->vrate * 4.0 );
55 if( !MP4SetTrackDurationPerChunk( m->file, trackId, dur ))
57 hb_error( "muxmp4.c: MP4SetTrackDurationPerChunk failed!" );
62 hb_deep_log( 2, "muxmp4: track %u, chunk duration %"PRIu64, MP4FindTrackIndex( m->file, trackId ), dur );
66 static const uint16_t ac3_sample_rate_tab[3] = { 48000, 44100, 32000 };
67 /* possible bitrates */
68 static const uint16_t ac3_bitrate_tab[19] = {
69 32, 40, 48, 56, 64, 80, 96, 112, 128,
70 160, 192, 224, 256, 320, 384, 448, 512, 576, 640
74 /**********************************************************************
76 **********************************************************************
77 * Allocates hb_mux_data_t structures, create file and write headers
78 *********************************************************************/
79 static int MP4Init( hb_mux_object_t * m )
81 hb_job_t * job = m->job;
82 hb_title_t * title = job->title;
85 hb_mux_data_t * mux_data;
89 /* Flags for enabling/disabling tracks in an MP4. */
90 typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
92 /* Create an empty mp4 file */
93 if (job->largeFileSize)
94 /* Use 64-bit MP4 file */
96 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
97 hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
100 /* Limit MP4s to less than 4 GB */
102 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
105 if (m->file == MP4_INVALID_FILE_HANDLE)
107 hb_error("muxmp4.c: MP4Create failed!");
113 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
114 job->mux_data = mux_data;
116 if (!(MP4SetTimeScale( m->file, 90000 )))
118 hb_error("muxmp4.c: MP4SetTimeScale failed!");
123 if( job->vcodec == HB_VCODEC_X264 )
125 /* Stolen from mp4creator */
126 MP4SetVideoProfileLevel( m->file, 0x7F );
127 mux_data->track = MP4AddH264VideoTrack( m->file, 90000,
128 MP4_INVALID_DURATION, job->width, job->height,
129 job->config.h264.sps[1], /* AVCProfileIndication */
130 job->config.h264.sps[2], /* profile_compat */
131 job->config.h264.sps[3], /* AVCLevelIndication */
132 3 ); /* 4 bytes length before each NAL unit */
133 if ( mux_data->track == MP4_INVALID_TRACK_ID )
135 hb_error( "muxmp4.c: MP4AddH264VideoTrack failed!" );
140 /* Tune track chunk duration */
141 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
146 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
147 job->config.h264.sps, job->config.h264.sps_length );
148 MP4AddH264PictureParameterSet( m->file, mux_data->track,
149 job->config.h264.pps, job->config.h264.pps_length );
153 hb_deep_log( 2, "muxmp4: adding iPod atom");
154 MP4AddIPodUUID(m->file, mux_data->track);
157 else /* FFmpeg or XviD */
159 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
160 mux_data->track = MP4AddVideoTrack( m->file, 90000,
161 MP4_INVALID_DURATION, job->width, job->height,
162 MP4_MPEG4_VIDEO_TYPE );
163 if (mux_data->track == MP4_INVALID_TRACK_ID)
165 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
170 /* Tune track chunk duration */
171 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
176 /* VOL from FFmpeg or XviD */
177 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
178 job->config.mpeg4.bytes, job->config.mpeg4.length )))
180 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
186 // COLR atom for color and gamma correction.
188 // http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
189 // http://forum.doom9.org/showthread.php?t=133982#post1090068
190 // the user can set it from job->color_matrix, otherwise by default
191 // we say anything that's likely to be HD content is ITU BT.709 and
192 // DVD, SD TV & other content is ITU BT.601. We look at the title height
193 // rather than the job height here to get uncropped input dimensions.
194 if( job->color_matrix == 1 )
196 // ITU BT.601 DVD or SD TV content
197 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
199 else if( job->color_matrix == 2 )
201 // ITU BT.709 HD content
202 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
204 else if ( job->title->width >= 1280 || job->title->height >= 720 )
206 // we guess that 720p or above is ITU BT.709 HD content
207 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
211 // ITU BT.601 DVD or SD TV content
212 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
215 if( job->anamorphic.mode )
217 /* PASP atom for anamorphic video */
220 width = job->anamorphic.par_width;
222 height = job->anamorphic.par_height;
224 MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
226 MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
229 /* add the audio tracks */
230 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
232 audio = hb_list_item( title->list_audio, i );
233 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
234 audio->priv.mux_data = mux_data;
236 if( audio->config.out.codec == HB_ACODEC_AC3_PASS )
238 uint8_t bsid = audio->config.in.version;
239 uint8_t bsmod = audio->config.in.mode;
240 uint8_t acmod = audio->config.flags.ac3 & 0x7;
241 uint8_t lfeon = (audio->config.flags.ac3 & A52_LFE) ? 1 : 0;
242 uint8_t bit_rate_code = 0;
244 int freq = audio->config.in.samplerate;
245 int bitrate = audio->config.in.bitrate;
246 int sr_shift, sr_code;
248 for (ii = 0; ii < 3; ii++)
250 for (jj = 0; jj < 3; jj++)
252 if ((ac3_sample_rate_tab[jj] >> ii) == freq)
258 hb_error("Unknown AC3 samplerate");
263 for (ii = 0; ii < 19; ii++)
265 if ((ac3_bitrate_tab[ii] >> sr_shift)*1000 == bitrate)
270 hb_error("Unknown AC3 bitrate");
275 mux_data->track = MP4AddAC3AudioTrack(
277 audio->config.in.samplerate,
285 /* Tune track chunk duration */
286 MP4TuneTrackDurationPerChunk( m, mux_data->track );
288 if (audio->config.out.name == NULL) {
289 MP4SetTrackBytesProperty(
290 m->file, mux_data->track,
292 (const uint8_t*)"Surround", strlen("Surround"));
295 MP4SetTrackBytesProperty(
296 m->file, mux_data->track,
298 (const uint8_t*)(audio->config.out.name),
299 strlen(audio->config.out.name));
302 else if( audio->config.out.codec == HB_ACODEC_AC3 )
308 uint8_t bit_rate_code = 0;
310 int freq = audio->config.out.samplerate;
311 int bitrate = audio->config.out.bitrate;
312 int sr_shift, sr_code;
314 for (ii = 0; ii < 3; ii++)
316 for (jj = 0; jj < 3; jj++)
318 if ((ac3_sample_rate_tab[jj] >> ii) == freq)
324 hb_error("Unknown AC3 samplerate");
330 for (ii = 0; ii < 19; ii++)
332 if ((ac3_bitrate_tab[ii] >> sr_shift) == bitrate)
337 hb_error("Unknown AC3 bitrate");
342 switch( audio->config.out.mixdown )
344 case HB_AMIXDOWN_MONO:
348 case HB_AMIXDOWN_STEREO:
349 case HB_AMIXDOWN_DOLBY:
350 case HB_AMIXDOWN_DOLBYPLII:
354 case HB_AMIXDOWN_6CH:
360 hb_log(" MP4Init: bad mixdown" );
364 mux_data->track = MP4AddAC3AudioTrack(
366 audio->config.out.samplerate,
374 /* Tune track chunk duration */
375 MP4TuneTrackDurationPerChunk( m, mux_data->track );
377 if (audio->config.out.name == NULL) {
378 MP4SetTrackBytesProperty(
379 m->file, mux_data->track,
381 (const uint8_t*)"Surround", strlen("Surround"));
384 MP4SetTrackBytesProperty(
385 m->file, mux_data->track,
387 (const uint8_t*)(audio->config.out.name),
388 strlen(audio->config.out.name));
391 else if( audio->config.out.codec == HB_ACODEC_FAAC ||
392 audio->config.out.codec == HB_ACODEC_CA_AAC )
394 mux_data->track = MP4AddAudioTrack(
396 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
398 /* Tune track chunk duration */
399 MP4TuneTrackDurationPerChunk( m, mux_data->track );
401 if (audio->config.out.name == NULL) {
402 MP4SetTrackBytesProperty(
403 m->file, mux_data->track,
405 (const uint8_t*)"Stereo", strlen("Stereo"));
408 MP4SetTrackBytesProperty(
409 m->file, mux_data->track,
411 (const uint8_t*)(audio->config.out.name),
412 strlen(audio->config.out.name));
415 MP4SetAudioProfileLevel( m->file, 0x0F );
416 MP4SetTrackESConfiguration(
417 m->file, mux_data->track,
418 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
420 /* Set the correct number of channels for this track */
421 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
422 } else if( audio->config.out.codec == HB_ACODEC_LAME ) {
423 mux_data->track = MP4AddAudioTrack(
425 audio->config.out.samplerate, 1152, MP4_MPEG2_AUDIO_TYPE );
427 /* Tune track chunk duration */
428 MP4TuneTrackDurationPerChunk( m, mux_data->track );
430 if (audio->config.out.name == NULL) {
431 MP4SetTrackBytesProperty(
432 m->file, mux_data->track,
434 (const uint8_t*)"Stereo", strlen("Stereo"));
437 MP4SetTrackBytesProperty(
438 m->file, mux_data->track,
440 (const uint8_t*)(audio->config.out.name),
441 strlen(audio->config.out.name));
444 MP4SetAudioProfileLevel( m->file, 0x0F );
446 /* Set the correct number of channels for this track */
447 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
450 /* Set the language for this track */
451 MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
453 if( hb_list_count( title->list_audio ) > 1 )
455 /* Set the audio track alternate group */
456 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
460 /* Enable the first audio track */
461 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
464 /* Disable the other audio tracks so QuickTime doesn't play
467 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
468 hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
473 // Quicktime requires that at least one subtitle is enabled,
474 // else it doesn't show any of the subtitles.
475 // So check to see if any of the subtitles are flagged to be
476 // the defualt. The default will the the enabled track, else
477 // enable the first track.
478 subtitle_default = 0;
479 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
481 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
483 if( subtitle && subtitle->format == TEXTSUB &&
484 subtitle->config.dest == PASSTHRUSUB )
486 if ( subtitle->config.default_track )
487 subtitle_default = 1;
490 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
492 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
494 if( subtitle && subtitle->format == TEXTSUB &&
495 subtitle->config.dest == PASSTHRUSUB )
497 uint64_t width, height = 60;
498 if( job->anamorphic.mode )
499 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
503 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
504 subtitle->mux_data = mux_data;
505 mux_data->subtitle = 1;
506 mux_data->sub_format = subtitle->format;
507 mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
509 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
511 /* Tune track chunk duration */
512 MP4TuneTrackDurationPerChunk( m, mux_data->track );
514 const uint8_t textColor[4] = { 255,255,255,255 };
516 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
518 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
519 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
520 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
522 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
524 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
525 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
527 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
528 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
530 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
531 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
532 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
533 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
535 /* translate the track */
538 uint32_t *ptr32 = (uint32_t*) nval;
541 MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
542 memcpy(nval, val, size);
544 const uint32_t ytranslation = (job->height - height) * 0x10000;
546 #ifdef WORDS_BIGENDIAN
547 ptr32[7] = ytranslation;
549 /* we need to switch the endianness, as the file format expects big endian */
550 ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) +
551 ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
554 MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);
555 if ( !subtitle_default || subtitle->config.default_track ) {
556 /* Enable the default subtitle track */
557 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
558 subtitle_default = 1;
562 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
565 else if( subtitle && subtitle->format == PICTURESUB &&
566 subtitle->config.dest == PASSTHRUSUB )
568 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
569 subtitle->mux_data = mux_data;
570 mux_data->subtitle = 1;
571 mux_data->sub_format = subtitle->format;
573 mux_data->track = MP4AddSubpicTrack( m->file, 90000, subtitle->width, subtitle->height );
575 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
577 /* Tune track chunk duration */
578 MP4TuneTrackDurationPerChunk( m, mux_data->track );
579 uint8_t palette[16][4];
581 for ( ii = 0; ii < 16; ii++ )
584 palette[ii][1] = (subtitle->palette[ii] >> 16) & 0xff;
585 palette[ii][2] = (subtitle->palette[ii] >> 8) & 0xff;
586 palette[ii][3] = (subtitle->palette[ii]) & 0xff;
588 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
589 (uint8_t*)palette, 16 * 4 )))
591 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
595 if ( !subtitle_default || subtitle->config.default_track ) {
596 /* Enable the default subtitle track */
597 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
598 subtitle_default = 1;
602 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
607 if (job->chapter_markers)
609 /* add a text track for the chapters. We add the 'chap' atom to track
610 one which is usually the video track & should never be disabled.
611 The Quicktime spec says it doesn't matter which media track the
612 chap atom is on but it has to be an enabled track. */
613 MP4TrackId textTrack;
614 textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
616 m->chapter_track = textTrack;
617 m->chapter_duration = 0;
618 m->current_chapter = job->chapter_start;
621 /* Add encoded-by metadata listing version and build date */
623 tool_string = (char *)malloc(80);
624 snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
626 /* allocate,fetch,populate,store,free tags structure */
628 tags = MP4TagsAlloc();
629 MP4TagsFetch( tags, m->file );
630 MP4TagsSetEncodingTool( tags, tool_string );
631 MP4TagsStore( tags, m->file );
639 typedef struct stylerecord_s {
640 enum style_s {ITALIC, BOLD, UNDERLINE} style;
643 struct stylerecord_s *next;
646 static void hb_makestylerecord( stylerecord **stack,
647 enum style_s style, int start )
649 stylerecord *record = calloc( sizeof( stylerecord ), 1 );
653 record->style = style;
654 record->start = start;
655 record->next = *stack;
660 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
663 hb_deep_log(3, "Made style '%s' from %d to %d",
664 record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
666 switch( record->style )
682 style[0] = (record->start >> 8) & 0xff; // startChar
683 style[1] = record->start & 0xff;
684 style[2] = (record->stop >> 8) & 0xff; // endChar
685 style[3] = record->stop & 0xff;
686 style[4] = (1 >> 8) & 0xff; // font-ID
688 style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline
689 style[7] = 24; // font-size
692 style[10] = 255; // b
693 style[11] = 255; // a
698 * Copy the input to output removing markup and adding markup to the style
699 * atom where appropriate.
701 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
703 uint8_t *style, uint16_t *stylesize )
705 uint8_t *reader = input;
706 uint8_t *writer = output;
707 uint8_t stylecount = 0;
708 uint16_t utf8_count = 0; // utf8 count from start of subtitle
709 stylerecord *stylestack = NULL;
710 stylerecord *oldrecord = NULL;
712 while(*reader != '\0') {
713 if( ( *reader & 0xc0 ) == 0x80 )
716 * Track the utf8_count when doing markup so that we get the tx3g stops
717 * based on UTF8 chr counts rather than bytes.
720 hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far",
723 if (*reader == '<') {
725 * possible markup, peek at the next chr
727 switch(*(reader+1)) {
729 if (*(reader+2) == '>') {
731 hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
733 *writer++ = *reader++;
737 if (*(reader+2) == '>') {
739 hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
741 *writer++ = *reader++;
745 if (*(reader+2) == '>') {
747 hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
749 *writer++ = *reader++;
753 switch(*(reader+2)) {
755 if (*(reader+3) == '>') {
757 * Check whether we then immediately start more markup of the same type, if so then
758 * lets not close it now and instead continue this markup.
760 if ((*(reader+4) && *(reader+4) == '<') &&
761 (*(reader+5) && *(reader+5) == 'i') &&
762 (*(reader+6) && *(reader+6) == '>')) {
764 * Opening italics right after, so don't close off these italics.
766 hb_deep_log(3, "Joining two sets of italics");
772 if ((*(reader+4) && *(reader+4) == ' ') &&
773 (*(reader+5) && *(reader+5) == '<') &&
774 (*(reader+6) && *(reader+6) == 'i') &&
775 (*(reader+7) && *(reader+7) == '>')) {
777 * Opening italics right after, so don't close off these italics.
779 hb_deep_log(3, "Joining two sets of italics (plus space)");
784 if (stylestack && stylestack->style == ITALIC) {
785 uint8_t style_record[12];
786 stylestack->stop = writer - output - utf8_count;
787 hb_makestyleatom(stylestack, style_record);
789 memcpy(style + 10 + (12 * stylecount), style_record, 12);
792 oldrecord = stylestack;
793 stylestack = stylestack->next;
796 hb_error("Mismatched Subtitle markup '%s'", input);
800 *writer++ = *reader++;
804 if (*(reader+3) == '>') {
805 if (stylestack && stylestack->style == BOLD) {
806 uint8_t style_record[12];
807 stylestack->stop = writer - output - utf8_count;
808 hb_makestyleatom(stylestack, style_record);
810 memcpy(style + 10 + (12 * stylecount), style_record, 12);
812 oldrecord = stylestack;
813 stylestack = stylestack->next;
816 hb_error("Mismatched Subtitle markup '%s'", input);
821 *writer++ = *reader++;
825 if (*(reader+3) == '>') {
826 if (stylestack && stylestack->style == UNDERLINE) {
827 uint8_t style_record[12];
828 stylestack->stop = writer - output - utf8_count;
829 hb_makestyleatom(stylestack, style_record);
831 memcpy(style + 10 + (12 * stylecount), style_record, 12);
834 oldrecord = stylestack;
835 stylestack = stylestack->next;
838 hb_error("Mismatched Subtitle markup '%s'", input);
842 *writer++ = *reader++;
846 *writer++ = *reader++;
851 *writer++ = *reader++;
855 *writer++ = *reader++;
862 *stylesize = 10 + ( stylecount * 12 );
864 memcpy( style + 4, "styl", 4);
868 style[2] = (*stylesize >> 8) & 0xff;
869 style[3] = *stylesize & 0xff;
870 style[8] = (stylecount >> 8) & 0xff;
871 style[9] = stylecount & 0xff;
877 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
880 hb_job_t * job = m->job;
885 if( mux_data == job->mux_data )
889 if( job->vcodec == HB_VCODEC_X264 )
891 if ( buf && buf->start < buf->renderOffset )
893 hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
894 buf->start, buf->renderOffset );
895 buf->renderOffset = buf->start;
899 // We delay muxing video by one frame so that we can calculate
900 // the dts to dts duration of the frames.
908 if( job->vcodec == HB_VCODEC_X264 )
910 // x264 supplies us with DTS, so offset is PTS - DTS
911 offset = buf->start - buf->renderOffset;
914 /* Add the sample before the new frame.
915 It is important that this be calculated prior to the duration
916 of the new video sample, as we want to sync to right after it.
917 (This is because of how durations for text tracks work in QT) */
918 if( job->chapter_markers && buf->new_chap )
920 hb_chapter_t *chapter = NULL;
922 // this chapter is postioned by writing out the previous chapter.
923 // the duration of the previous chapter is the duration up to but
924 // not including the current frame minus the duration of all
925 // chapters up to the previous.
926 // The initial and final chapters can be very short (a second or
927 // less) since they're not really chapters but just a placeholder to
928 // insert a cell command. We don't write chapters shorter than 1.5 sec.
929 duration = m->sum_dur - m->chapter_duration + offset;
930 if ( duration >= (90000*3)/2 )
932 chapter = hb_list_item( m->job->title->list_chapter,
935 MP4AddChapter( m->file,
938 (chapter != NULL) ? chapter->title : NULL);
940 m->current_chapter = buf->new_chap;
941 m->chapter_duration += duration;
945 if( job->vcodec == HB_VCODEC_X264 )
947 // x264 supplies us with DTS
950 duration = m->delay_buf->renderOffset - buf->renderOffset;
954 duration = buf->stop - m->sum_dur;
955 // Due to how libx264 generates DTS, it's possible for the
956 // above calculation to be negative.
958 // x264 generates DTS by rearranging PTS in this sequence:
959 // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
961 // where delay == pts2. This guarantees that DTS <= PTS for
962 // any frame, but also generates this sequence of durations:
963 // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
965 // so the sum up to the last frame is:
966 // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
968 // while the original total duration of the video was:
969 // duration = d0 + d1 + d2 + d3 ... + d(N)
971 // Note that if d0 + d1 != d(N-1) + d(N), the total
972 // length of the video changes since d(N-1) and d(N) are
973 // replaced by d0 and d1 in the final duration sum.
975 // To keep the total length of the video the same as the source
977 // d(N-2) = duration - sum_dur
979 // But if d0 + d1 >= d(N-1) + d(N), the above calculation
980 // results in a nagative value and we need to fix it.
982 duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
987 // We're getting the frames in decode order but the timestamps are
988 // for presentation so we have to use durations and effectively
990 duration = buf->stop - buf->start;
995 /* We got an illegal mp4/h264 duration. This shouldn't
996 be possible and usually indicates a bug in the upstream code.
997 Complain in the hope that someone will go find the bug but
998 try to fix the error so that the file will still be playable. */
999 hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
1000 "stop %"PRId64", sum_dur %"PRId64,
1001 duration, buf->start, buf->stop, m->sum_dur );
1002 /* we don't know when the next frame starts so we can't pick a
1003 valid duration for this one. we pick something "short"
1004 (roughly 1/3 of an NTSC frame time) to take time from
1008 m->sum_dur += duration;
1013 duration = MP4_INVALID_DURATION;
1016 /* Here's where the sample actually gets muxed. */
1017 if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
1019 /* Compute dependency flags.
1021 * This mechanism is (optionally) used by media players such as QuickTime
1022 * to offer better scrubbing performance. The most influential bits are
1023 * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
1025 * Other bits are possible but no example media using such bits have been
1028 * It is acceptable to supply 0-bits for any samples which characteristics
1029 * cannot be positively guaranteed.
1032 uint32_t dflags = 0;
1034 /* encoding layer signals if frame is referenced by other frames */
1035 if( buf->flags & HB_FRAME_REF )
1036 dflags |= MP4_SDT_HAS_DEPENDENTS;
1038 dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
1040 switch( buf->frametype )
1046 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
1049 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
1054 break; /* nothing to mark */
1057 if( !MP4WriteSampleDependency( m->file,
1066 hb_error("Failed to write to output file, disk full?");
1070 else if (mux_data->subtitle)
1072 if( mux_data->sub_format == TEXTSUB )
1074 /* Write an empty sample */
1075 if ( mux_data->sum_dur < buf->start )
1077 uint8_t empty[2] = {0,0};
1078 if( !MP4WriteSample( m->file,
1082 buf->start - mux_data->sum_dur,
1086 hb_error("Failed to write to output file, disk full?");
1089 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1091 uint8_t styleatom[2048];;
1092 uint16_t stylesize = 0;
1093 uint8_t buffer[2048];
1094 uint16_t buffersize = 0;
1095 uint8_t output[2048];
1100 * Copy the subtitle into buffer stripping markup and creating
1101 * style atoms for them.
1103 hb_muxmp4_process_subtitle_style( buf->data,
1105 styleatom, &stylesize );
1107 buffersize = strlen((char*)buffer);
1109 hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
1110 (float)buf->start / 90000, buf->start, buf->stop,
1111 (buf->stop - buf->start), buffer);
1113 /* Write the subtitle sample */
1114 memcpy( output + 2, buffer, buffersize );
1115 memcpy( output + 2 + buffersize, styleatom, stylesize);
1116 output[0] = ( buffersize >> 8 ) & 0xff;
1117 output[1] = buffersize & 0xff;
1119 if( !MP4WriteSample( m->file,
1122 buffersize + stylesize + 2,
1123 buf->stop - buf->start,
1127 hb_error("Failed to write to output file, disk full?");
1131 mux_data->sum_dur += (buf->stop - buf->start);
1133 else if( mux_data->sub_format == PICTURESUB )
1135 /* Write an empty sample */
1136 if ( mux_data->sum_dur < buf->start )
1138 uint8_t empty[2] = {0,0};
1139 if( !MP4WriteSample( m->file,
1143 buf->start - mux_data->sum_dur,
1147 hb_error("Failed to write to output file, disk full?");
1150 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1152 if( !MP4WriteSample( m->file,
1156 buf->stop - buf->start,
1160 hb_error("Failed to write to output file, disk full?");
1164 mux_data->sum_dur += (buf->stop - buf->start);
1172 if( !MP4WriteSample( m->file,
1178 ( buf->frametype & HB_FRAME_KEY ) != 0 ))
1180 hb_error("Failed to write to output file, disk full?");
1184 hb_buffer_close( &buf );
1189 static int MP4End( hb_mux_object_t * m )
1191 hb_job_t * job = m->job;
1192 hb_title_t * title = job->title;
1194 // Flush the delayed frame
1196 MP4Mux( m, job->mux_data, NULL );
1198 /* Write our final chapter marker */
1199 if( m->job->chapter_markers )
1201 hb_chapter_t *chapter = NULL;
1202 int64_t duration = m->sum_dur - m->chapter_duration;
1203 /* The final chapter can have a very short duration - if it's less
1204 * than 1.5 seconds just skip it. */
1205 if ( duration >= (90000*3)/2 )
1208 chapter = hb_list_item( m->job->title->list_chapter,
1209 m->current_chapter - 1 );
1211 MP4AddChapter( m->file,
1214 (chapter != NULL) ? chapter->title : NULL);
1218 if ( job->config.h264.init_delay )
1220 // Insert track edit to get A/V back in sync. The edit amount is
1222 int64_t edit_amt = job->config.h264.init_delay;
1223 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1224 MP4GetTrackDuration(m->file, 1), 0);
1225 if ( m->job->chapter_markers )
1227 // apply same edit to chapter track to keep it in sync with video
1228 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1230 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1235 * Write the MP4 iTunes metadata if we have any metadata
1237 if( title->metadata )
1239 hb_metadata_t *md = title->metadata;
1240 const MP4Tags* tags;
1242 hb_deep_log( 2, "Writing Metadata to output file...");
1244 /* allocate tags structure */
1245 tags = MP4TagsAlloc();
1246 /* fetch data from MP4 file (in case it already has some data) */
1247 MP4TagsFetch( tags, m->file );
1250 if( strlen( md->name ))
1251 MP4TagsSetName( tags, md->name );
1252 if( strlen( md->artist ))
1253 MP4TagsSetArtist( tags, md->artist );
1254 if( strlen( md->composer ))
1255 MP4TagsSetComposer( tags, md->composer );
1256 if( strlen( md->comment ))
1257 MP4TagsSetComments( tags, md->comment );
1258 if( strlen( md->release_date ))
1259 MP4TagsSetReleaseDate( tags, md->release_date );
1260 if( strlen( md->album ))
1261 MP4TagsSetAlbum( tags, md->album );
1262 if( strlen( md->genre ))
1263 MP4TagsSetGenre( tags, md->genre );
1268 art.data = md->coverart;
1269 art.size = md->coverart_size;
1270 art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1271 MP4TagsAddArtwork( tags, &art );
1274 /* push data to MP4 file */
1275 MP4TagsStore( tags, m->file );
1276 /* free memory associated with structure */
1277 MP4TagsFree( tags );
1280 MP4Close( m->file );
1282 if ( job->mp4_optimize )
1284 hb_log( "muxmp4: optimizing file" );
1285 char filename[1024]; memset( filename, 0, 1024 );
1286 snprintf( filename, 1024, "%s.tmp", job->file );
1287 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1288 remove( job->file );
1289 rename( filename, job->file );
1295 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1297 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );