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 /**********************************************************************
68 **********************************************************************
69 * Allocates hb_mux_data_t structures, create file and write headers
70 *********************************************************************/
71 static int MP4Init( hb_mux_object_t * m )
73 hb_job_t * job = m->job;
74 hb_title_t * title = job->title;
77 hb_mux_data_t * mux_data;
81 /* Flags for enabling/disabling tracks in an MP4. */
82 typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
84 /* Create an empty mp4 file */
85 if (job->largeFileSize)
86 /* Use 64-bit MP4 file */
88 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
89 hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
92 /* Limit MP4s to less than 4 GB */
94 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
97 if (m->file == MP4_INVALID_FILE_HANDLE)
99 hb_error("muxmp4.c: MP4Create failed!");
105 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
106 job->mux_data = mux_data;
108 if (!(MP4SetTimeScale( m->file, 90000 )))
110 hb_error("muxmp4.c: MP4SetTimeScale failed!");
115 if( job->vcodec == HB_VCODEC_X264 )
117 /* Stolen from mp4creator */
118 MP4SetVideoProfileLevel( m->file, 0x7F );
119 mux_data->track = MP4AddH264VideoTrack( m->file, 90000,
120 MP4_INVALID_DURATION, job->width, job->height,
121 job->config.h264.sps[1], /* AVCProfileIndication */
122 job->config.h264.sps[2], /* profile_compat */
123 job->config.h264.sps[3], /* AVCLevelIndication */
124 3 ); /* 4 bytes length before each NAL unit */
125 if ( mux_data->track == MP4_INVALID_TRACK_ID )
127 hb_error( "muxmp4.c: MP4AddH264VideoTrack failed!" );
132 /* Tune track chunk duration */
133 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
138 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
139 job->config.h264.sps, job->config.h264.sps_length );
140 MP4AddH264PictureParameterSet( m->file, mux_data->track,
141 job->config.h264.pps, job->config.h264.pps_length );
143 if( job->h264_level == 30 || job->ipod_atom)
145 hb_deep_log( 2, "muxmp4: adding iPod atom");
146 MP4AddIPodUUID(m->file, mux_data->track);
149 else /* FFmpeg or XviD */
151 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
152 mux_data->track = MP4AddVideoTrack( m->file, 90000,
153 MP4_INVALID_DURATION, job->width, job->height,
154 MP4_MPEG4_VIDEO_TYPE );
155 if (mux_data->track == MP4_INVALID_TRACK_ID)
157 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
162 /* Tune track chunk duration */
163 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
168 /* VOL from FFmpeg or XviD */
169 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
170 job->config.mpeg4.bytes, job->config.mpeg4.length )))
172 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
178 // COLR atom for color and gamma correction.
180 // http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
181 // http://forum.doom9.org/showthread.php?t=133982#post1090068
182 // the user can set it from job->color_matrix, otherwise by default
183 // we say anything that's likely to be HD content is ITU BT.709 and
184 // DVD, SD TV & other content is ITU BT.601. We look at the title height
185 // rather than the job height here to get uncropped input dimensions.
186 if( job->color_matrix == 1 )
188 // ITU BT.601 DVD or SD TV content
189 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
191 else if( job->color_matrix == 2 )
193 // ITU BT.709 HD content
194 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
196 else if ( job->title->width >= 1280 || job->title->height >= 720 )
198 // we guess that 720p or above is ITU BT.709 HD content
199 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
203 // ITU BT.601 DVD or SD TV content
204 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
207 if( job->anamorphic.mode )
209 /* PASP atom for anamorphic video */
212 width = job->anamorphic.par_width;
214 height = job->anamorphic.par_height;
216 MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
218 MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
221 /* add the audio tracks */
222 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
224 audio = hb_list_item( title->list_audio, i );
225 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
226 audio->priv.mux_data = mux_data;
228 if( audio->config.out.codec == HB_ACODEC_AC3 )
231 uint8_t bsid = audio->config.in.version;
232 uint8_t bsmod = audio->config.in.mode;
233 uint8_t acmod = audio->config.flags.ac3 & 0x7;
234 uint8_t lfeon = (audio->config.flags.ac3 & A52_LFE) ? 1 : 0;
235 uint8_t bit_rate_code = 0;
238 * Rewrite AC3 information into correct format for dac3 atom
240 switch( audio->config.in.samplerate )
253 * Error value, tells decoder to not decode this audio.
259 switch( audio->config.in.bitrate )
319 hb_error("Unknown AC3 bitrate");
324 mux_data->track = MP4AddAC3AudioTrack(
326 audio->config.out.samplerate,
334 /* Tune track chunk duration */
335 MP4TuneTrackDurationPerChunk( m, mux_data->track );
337 if (audio->config.out.name == NULL) {
338 MP4SetTrackBytesProperty(
339 m->file, mux_data->track,
341 (const uint8_t*)"Surround", strlen("Surround"));
344 MP4SetTrackBytesProperty(
345 m->file, mux_data->track,
347 (const uint8_t*)(audio->config.out.name),
348 strlen(audio->config.out.name));
350 } else if( audio->config.out.codec == HB_ACODEC_FAAC ||
351 audio->config.out.codec == HB_ACODEC_CA_AAC ) {
352 mux_data->track = MP4AddAudioTrack(
354 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
356 /* Tune track chunk duration */
357 MP4TuneTrackDurationPerChunk( m, mux_data->track );
359 if (audio->config.out.name == NULL) {
360 MP4SetTrackBytesProperty(
361 m->file, mux_data->track,
363 (const uint8_t*)"Stereo", strlen("Stereo"));
366 MP4SetTrackBytesProperty(
367 m->file, mux_data->track,
369 (const uint8_t*)(audio->config.out.name),
370 strlen(audio->config.out.name));
373 MP4SetAudioProfileLevel( m->file, 0x0F );
374 MP4SetTrackESConfiguration(
375 m->file, mux_data->track,
376 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
378 /* Set the correct number of channels for this track */
379 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
380 } else if( audio->config.out.codec == HB_ACODEC_LAME ) {
381 mux_data->track = MP4AddAudioTrack(
383 audio->config.out.samplerate, 1152, MP4_MPEG2_AUDIO_TYPE );
385 /* Tune track chunk duration */
386 MP4TuneTrackDurationPerChunk( m, mux_data->track );
388 if (audio->config.out.name == NULL) {
389 MP4SetTrackBytesProperty(
390 m->file, mux_data->track,
392 (const uint8_t*)"Stereo", strlen("Stereo"));
395 MP4SetTrackBytesProperty(
396 m->file, mux_data->track,
398 (const uint8_t*)(audio->config.out.name),
399 strlen(audio->config.out.name));
402 MP4SetAudioProfileLevel( m->file, 0x0F );
404 /* Set the correct number of channels for this track */
405 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
408 /* Set the language for this track */
409 MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
411 if( hb_list_count( title->list_audio ) > 1 )
413 /* Set the audio track alternate group */
414 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
418 /* Enable the first audio track */
419 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
422 /* Disable the other audio tracks so QuickTime doesn't play
425 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
426 hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
431 // Quicktime requires that at least one subtitle is enabled,
432 // else it doesn't show any of the subtitles.
433 // So check to see if any of the subtitles are flagged to be
434 // the defualt. The default will the the enabled track, else
435 // enable the first track.
436 subtitle_default = 0;
437 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
439 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
441 if( subtitle && subtitle->format == TEXTSUB &&
442 subtitle->config.dest == PASSTHRUSUB )
444 if ( subtitle->config.default_track )
445 subtitle_default = 1;
448 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
450 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
452 if( subtitle && subtitle->format == TEXTSUB &&
453 subtitle->config.dest == PASSTHRUSUB )
455 uint64_t width, height = 60;
456 if( job->anamorphic.mode )
457 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
461 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
462 subtitle->mux_data = mux_data;
463 mux_data->subtitle = 1;
464 mux_data->sub_format = subtitle->format;
465 mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
467 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
469 /* Tune track chunk duration */
470 MP4TuneTrackDurationPerChunk( m, mux_data->track );
472 const uint8_t textColor[4] = { 255,255,255,255 };
474 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
476 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
477 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
478 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
480 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
482 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
483 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
485 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
486 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
488 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
489 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
490 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
491 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
493 /* translate the track */
496 uint32_t *ptr32 = (uint32_t*) nval;
499 MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
500 memcpy(nval, val, size);
502 const uint32_t ytranslation = (job->height - height) * 0x10000;
504 #ifdef WORDS_BIGENDIAN
505 ptr32[7] = ytranslation;
507 /* we need to switch the endianness, as the file format expects big endian */
508 ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) +
509 ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
512 MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);
513 if ( !subtitle_default || subtitle->config.default_track ) {
514 /* Enable the default subtitle track */
515 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
516 subtitle_default = 1;
520 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
523 else if( subtitle && subtitle->format == PICTURESUB &&
524 subtitle->config.dest == PASSTHRUSUB )
526 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
527 subtitle->mux_data = mux_data;
528 mux_data->subtitle = 1;
529 mux_data->sub_format = subtitle->format;
531 mux_data->track = MP4AddSubpicTrack( m->file, 90000, subtitle->width, subtitle->height );
533 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
535 /* Tune track chunk duration */
536 MP4TuneTrackDurationPerChunk( m, mux_data->track );
537 uint8_t palette[16][4];
539 for ( ii = 0; ii < 16; ii++ )
542 palette[ii][1] = (subtitle->palette[ii] >> 16) & 0xff;
543 palette[ii][2] = (subtitle->palette[ii] >> 8) & 0xff;
544 palette[ii][3] = (subtitle->palette[ii]) & 0xff;
546 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
547 (uint8_t*)palette, 16 * 4 )))
549 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
553 if ( !subtitle_default || subtitle->config.default_track ) {
554 /* Enable the default subtitle track */
555 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
556 subtitle_default = 1;
560 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
565 if (job->chapter_markers)
567 /* add a text track for the chapters. We add the 'chap' atom to track
568 one which is usually the video track & should never be disabled.
569 The Quicktime spec says it doesn't matter which media track the
570 chap atom is on but it has to be an enabled track. */
571 MP4TrackId textTrack;
572 textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
574 m->chapter_track = textTrack;
575 m->chapter_duration = 0;
576 m->current_chapter = job->chapter_start;
579 /* Add encoded-by metadata listing version and build date */
581 tool_string = (char *)malloc(80);
582 snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
584 /* allocate,fetch,populate,store,free tags structure */
586 tags = MP4TagsAlloc();
587 MP4TagsFetch( tags, m->file );
588 MP4TagsSetEncodingTool( tags, tool_string );
589 MP4TagsStore( tags, m->file );
597 typedef struct stylerecord_s {
598 enum style_s {ITALIC, BOLD, UNDERLINE} style;
601 struct stylerecord_s *next;
604 static void hb_makestylerecord( stylerecord **stack,
605 enum style_s style, int start )
607 stylerecord *record = calloc( sizeof( stylerecord ), 1 );
611 record->style = style;
612 record->start = start;
613 record->next = *stack;
618 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
621 hb_deep_log(3, "Made style '%s' from %d to %d",
622 record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
624 switch( record->style )
640 style[0] = (record->start >> 8) & 0xff; // startChar
641 style[1] = record->start & 0xff;
642 style[2] = (record->stop >> 8) & 0xff; // endChar
643 style[3] = record->stop & 0xff;
644 style[4] = (1 >> 8) & 0xff; // font-ID
646 style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline
647 style[7] = 24; // font-size
650 style[10] = 255; // b
651 style[11] = 255; // a
656 * Copy the input to output removing markup and adding markup to the style
657 * atom where appropriate.
659 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
661 uint8_t *style, uint16_t *stylesize )
663 uint8_t *reader = input;
664 uint8_t *writer = output;
665 uint8_t stylecount = 0;
666 uint16_t utf8_count = 0; // utf8 count from start of subtitle
667 stylerecord *stylestack = NULL;
668 stylerecord *oldrecord = NULL;
670 while(*reader != '\0') {
671 if( ( *reader & 0xc0 ) == 0x80 )
674 * Track the utf8_count when doing markup so that we get the tx3g stops
675 * based on UTF8 chr counts rather than bytes.
678 hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far",
681 if (*reader == '<') {
683 * possible markup, peek at the next chr
685 switch(*(reader+1)) {
687 if (*(reader+2) == '>') {
689 hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
691 *writer++ = *reader++;
695 if (*(reader+2) == '>') {
697 hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
699 *writer++ = *reader++;
703 if (*(reader+2) == '>') {
705 hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
707 *writer++ = *reader++;
711 switch(*(reader+2)) {
713 if (*(reader+3) == '>') {
715 * Check whether we then immediately start more markup of the same type, if so then
716 * lets not close it now and instead continue this markup.
718 if ((*(reader+4) && *(reader+4) == '<') &&
719 (*(reader+5) && *(reader+5) == 'i') &&
720 (*(reader+6) && *(reader+6) == '>')) {
722 * Opening italics right after, so don't close off these italics.
724 hb_deep_log(3, "Joining two sets of italics");
730 if ((*(reader+4) && *(reader+4) == ' ') &&
731 (*(reader+5) && *(reader+5) == '<') &&
732 (*(reader+6) && *(reader+6) == 'i') &&
733 (*(reader+7) && *(reader+7) == '>')) {
735 * Opening italics right after, so don't close off these italics.
737 hb_deep_log(3, "Joining two sets of italics (plus space)");
742 if (stylestack && stylestack->style == ITALIC) {
743 uint8_t style_record[12];
744 stylestack->stop = writer - output - utf8_count;
745 hb_makestyleatom(stylestack, style_record);
747 memcpy(style + 10 + (12 * stylecount), style_record, 12);
750 oldrecord = stylestack;
751 stylestack = stylestack->next;
754 hb_error("Mismatched Subtitle markup '%s'", input);
758 *writer++ = *reader++;
762 if (*(reader+3) == '>') {
763 if (stylestack && stylestack->style == BOLD) {
764 uint8_t style_record[12];
765 stylestack->stop = writer - output - utf8_count;
766 hb_makestyleatom(stylestack, style_record);
768 memcpy(style + 10 + (12 * stylecount), style_record, 12);
770 oldrecord = stylestack;
771 stylestack = stylestack->next;
774 hb_error("Mismatched Subtitle markup '%s'", input);
779 *writer++ = *reader++;
783 if (*(reader+3) == '>') {
784 if (stylestack && stylestack->style == UNDERLINE) {
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 *writer++ = *reader++;
809 *writer++ = *reader++;
813 *writer++ = *reader++;
820 *stylesize = 10 + ( stylecount * 12 );
822 memcpy( style + 4, "styl", 4);
826 style[2] = (*stylesize >> 8) & 0xff;
827 style[3] = *stylesize & 0xff;
828 style[8] = (stylecount >> 8) & 0xff;
829 style[9] = stylecount & 0xff;
835 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
838 hb_job_t * job = m->job;
843 if( mux_data == job->mux_data )
847 if( job->vcodec == HB_VCODEC_X264 )
849 if ( buf && buf->start < buf->renderOffset )
851 hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
852 buf->start, buf->renderOffset );
853 buf->renderOffset = buf->start;
857 // We delay muxing video by one frame so that we can calculate
858 // the dts to dts duration of the frames.
866 if( job->vcodec == HB_VCODEC_X264 )
868 // x264 supplies us with DTS, so offset is PTS - DTS
869 offset = buf->start - buf->renderOffset;
872 /* Add the sample before the new frame.
873 It is important that this be calculated prior to the duration
874 of the new video sample, as we want to sync to right after it.
875 (This is because of how durations for text tracks work in QT) */
876 if( job->chapter_markers && buf->new_chap )
878 hb_chapter_t *chapter = NULL;
880 // this chapter is postioned by writing out the previous chapter.
881 // the duration of the previous chapter is the duration up to but
882 // not including the current frame minus the duration of all
883 // chapters up to the previous.
884 // The initial and final chapters can be very short (a second or
885 // less) since they're not really chapters but just a placeholder to
886 // insert a cell command. We don't write chapters shorter than 1.5 sec.
887 duration = m->sum_dur - m->chapter_duration + offset;
888 if ( duration >= (90000*3)/2 )
890 chapter = hb_list_item( m->job->title->list_chapter,
893 MP4AddChapter( m->file,
896 (chapter != NULL) ? chapter->title : NULL);
898 m->current_chapter = buf->new_chap;
899 m->chapter_duration += duration;
903 if( job->vcodec == HB_VCODEC_X264 )
905 // x264 supplies us with DTS
908 duration = m->delay_buf->renderOffset - buf->renderOffset;
912 duration = buf->stop - m->sum_dur;
913 // Due to how libx264 generates DTS, it's possible for the
914 // above calculation to be negative.
916 // x264 generates DTS by rearranging PTS in this sequence:
917 // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
919 // where delay == pts2. This guarantees that DTS <= PTS for
920 // any frame, but also generates this sequence of durations:
921 // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
923 // so the sum up to the last frame is:
924 // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
926 // while the original total duration of the video was:
927 // duration = d0 + d1 + d2 + d3 ... + d(N)
929 // Note that if d0 + d1 != d(N-1) + d(N), the total
930 // length of the video changes since d(N-1) and d(N) are
931 // replaced by d0 and d1 in the final duration sum.
933 // To keep the total length of the video the same as the source
935 // d(N-2) = duration - sum_dur
937 // But if d0 + d1 >= d(N-1) + d(N), the above calculation
938 // results in a nagative value and we need to fix it.
940 duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
945 // We're getting the frames in decode order but the timestamps are
946 // for presentation so we have to use durations and effectively
948 duration = buf->stop - buf->start;
953 /* We got an illegal mp4/h264 duration. This shouldn't
954 be possible and usually indicates a bug in the upstream code.
955 Complain in the hope that someone will go find the bug but
956 try to fix the error so that the file will still be playable. */
957 hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
958 "stop %"PRId64", sum_dur %"PRId64,
959 duration, buf->start, buf->stop, m->sum_dur );
960 /* we don't know when the next frame starts so we can't pick a
961 valid duration for this one. we pick something "short"
962 (roughly 1/3 of an NTSC frame time) to take time from
966 m->sum_dur += duration;
971 duration = MP4_INVALID_DURATION;
974 /* Here's where the sample actually gets muxed. */
975 if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
977 /* Compute dependency flags.
979 * This mechanism is (optionally) used by media players such as QuickTime
980 * to offer better scrubbing performance. The most influential bits are
981 * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
983 * Other bits are possible but no example media using such bits have been
986 * It is acceptable to supply 0-bits for any samples which characteristics
987 * cannot be positively guaranteed.
992 /* encoding layer signals if frame is referenced by other frames */
993 if( buf->flags & HB_FRAME_REF )
994 dflags |= MP4_SDT_HAS_DEPENDENTS;
996 dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
998 switch( buf->frametype )
1004 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
1007 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
1012 break; /* nothing to mark */
1015 if( !MP4WriteSampleDependency( m->file,
1024 hb_error("Failed to write to output file, disk full?");
1028 else if (mux_data->subtitle)
1030 if( mux_data->sub_format == TEXTSUB )
1032 /* Write an empty sample */
1033 if ( mux_data->sum_dur < buf->start )
1035 uint8_t empty[2] = {0,0};
1036 if( !MP4WriteSample( m->file,
1040 buf->start - mux_data->sum_dur,
1044 hb_error("Failed to write to output file, disk full?");
1047 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1049 uint8_t styleatom[2048];;
1050 uint16_t stylesize = 0;
1051 uint8_t buffer[2048];
1052 uint16_t buffersize = 0;
1053 uint8_t output[2048];
1058 * Copy the subtitle into buffer stripping markup and creating
1059 * style atoms for them.
1061 hb_muxmp4_process_subtitle_style( buf->data,
1063 styleatom, &stylesize );
1065 buffersize = strlen((char*)buffer);
1067 hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
1068 (float)buf->start / 90000, buf->start, buf->stop,
1069 (buf->stop - buf->start), buffer);
1071 /* Write the subtitle sample */
1072 memcpy( output + 2, buffer, buffersize );
1073 memcpy( output + 2 + buffersize, styleatom, stylesize);
1074 output[0] = ( buffersize >> 8 ) & 0xff;
1075 output[1] = buffersize & 0xff;
1077 if( !MP4WriteSample( m->file,
1080 buffersize + stylesize + 2,
1081 buf->stop - buf->start,
1085 hb_error("Failed to write to output file, disk full?");
1089 mux_data->sum_dur += (buf->stop - buf->start);
1091 else if( mux_data->sub_format == PICTURESUB )
1093 /* Write an empty sample */
1094 if ( mux_data->sum_dur < buf->start )
1096 uint8_t empty[2] = {0,0};
1097 if( !MP4WriteSample( m->file,
1101 buf->start - mux_data->sum_dur,
1105 hb_error("Failed to write to output file, disk full?");
1108 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1110 if( !MP4WriteSample( m->file,
1114 buf->stop - buf->start,
1118 hb_error("Failed to write to output file, disk full?");
1122 mux_data->sum_dur += (buf->stop - buf->start);
1130 if( !MP4WriteSample( m->file,
1136 ( buf->frametype & HB_FRAME_KEY ) != 0 ))
1138 hb_error("Failed to write to output file, disk full?");
1142 hb_buffer_close( &buf );
1147 static int MP4End( hb_mux_object_t * m )
1149 hb_job_t * job = m->job;
1150 hb_title_t * title = job->title;
1152 // Flush the delayed frame
1154 MP4Mux( m, job->mux_data, NULL );
1156 /* Write our final chapter marker */
1157 if( m->job->chapter_markers )
1159 hb_chapter_t *chapter = NULL;
1160 int64_t duration = m->sum_dur - m->chapter_duration;
1161 /* The final chapter can have a very short duration - if it's less
1162 * than 1.5 seconds just skip it. */
1163 if ( duration >= (90000*3)/2 )
1166 chapter = hb_list_item( m->job->title->list_chapter,
1167 m->current_chapter - 1 );
1169 MP4AddChapter( m->file,
1172 (chapter != NULL) ? chapter->title : NULL);
1176 if ( job->config.h264.init_delay )
1178 // Insert track edit to get A/V back in sync. The edit amount is
1180 int64_t edit_amt = job->config.h264.init_delay;
1181 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1182 MP4GetTrackDuration(m->file, 1), 0);
1183 if ( m->job->chapter_markers )
1185 // apply same edit to chapter track to keep it in sync with video
1186 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1188 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1193 * Write the MP4 iTunes metadata if we have any metadata
1195 if( title->metadata )
1197 hb_metadata_t *md = title->metadata;
1198 const MP4Tags* tags;
1200 hb_deep_log( 2, "Writing Metadata to output file...");
1202 /* allocate tags structure */
1203 tags = MP4TagsAlloc();
1204 /* fetch data from MP4 file (in case it already has some data) */
1205 MP4TagsFetch( tags, m->file );
1208 if( strlen( md->name ))
1209 MP4TagsSetName( tags, md->name );
1210 if( strlen( md->artist ))
1211 MP4TagsSetArtist( tags, md->artist );
1212 if( strlen( md->composer ))
1213 MP4TagsSetComposer( tags, md->composer );
1214 if( strlen( md->comment ))
1215 MP4TagsSetComments( tags, md->comment );
1216 if( strlen( md->release_date ))
1217 MP4TagsSetReleaseDate( tags, md->release_date );
1218 if( strlen( md->album ))
1219 MP4TagsSetAlbum( tags, md->album );
1220 if( strlen( md->genre ))
1221 MP4TagsSetGenre( tags, md->genre );
1226 art.data = md->coverart;
1227 art.size = md->coverart_size;
1228 art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1229 MP4TagsAddArtwork( tags, &art );
1232 /* push data to MP4 file */
1233 MP4TagsStore( tags, m->file );
1234 /* free memory associated with structure */
1235 MP4TagsFree( tags );
1238 MP4Close( m->file );
1240 if ( job->mp4_optimize )
1242 hb_log( "muxmp4: optimizing file" );
1243 char filename[1024]; memset( filename, 0, 1024 );
1244 snprintf( filename, 1024, "%s.tmp", job->file );
1245 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1246 remove( job->file );
1247 rename( filename, job->file );
1253 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1255 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );