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));
525 if (job->chapter_markers)
527 /* add a text track for the chapters. We add the 'chap' atom to track
528 one which is usually the video track & should never be disabled.
529 The Quicktime spec says it doesn't matter which media track the
530 chap atom is on but it has to be an enabled track. */
531 MP4TrackId textTrack;
532 textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
534 m->chapter_track = textTrack;
535 m->chapter_duration = 0;
536 m->current_chapter = job->chapter_start;
539 /* Add encoded-by metadata listing version and build date */
541 tool_string = (char *)malloc(80);
542 snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
544 /* allocate,fetch,populate,store,free tags structure */
546 tags = MP4TagsAlloc();
547 MP4TagsFetch( tags, m->file );
548 MP4TagsSetEncodingTool( tags, tool_string );
549 MP4TagsStore( tags, m->file );
557 typedef struct stylerecord_s {
558 enum style_s {ITALIC, BOLD, UNDERLINE} style;
561 struct stylerecord_s *next;
564 static void hb_makestylerecord( stylerecord **stack,
565 enum style_s style, int start )
567 stylerecord *record = calloc( sizeof( stylerecord ), 1 );
571 record->style = style;
572 record->start = start;
573 record->next = *stack;
578 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
581 hb_deep_log(3, "Made style '%s' from %d to %d",
582 record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
584 switch( record->style )
600 style[0] = (record->start >> 8) & 0xff; // startChar
601 style[1] = record->start & 0xff;
602 style[2] = (record->stop >> 8) & 0xff; // endChar
603 style[3] = record->stop & 0xff;
604 style[4] = (1 >> 8) & 0xff; // font-ID
606 style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline
607 style[7] = 24; // font-size
610 style[10] = 255; // b
611 style[11] = 255; // a
616 * Copy the input to output removing markup and adding markup to the style
617 * atom where appropriate.
619 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
621 uint8_t *style, uint16_t *stylesize )
623 uint8_t *reader = input;
624 uint8_t *writer = output;
625 uint8_t stylecount = 0;
626 uint16_t utf8_count = 0; // utf8 count from start of subtitle
627 stylerecord *stylestack = NULL;
628 stylerecord *oldrecord = NULL;
630 while(*reader != '\0') {
631 if( ( *reader & 0xc0 ) == 0x80 )
634 * Track the utf8_count when doing markup so that we get the tx3g stops
635 * based on UTF8 chr counts rather than bytes.
638 hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far",
641 if (*reader == '<') {
643 * possible markup, peek at the next chr
645 switch(*(reader+1)) {
647 if (*(reader+2) == '>') {
649 hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
651 *writer++ = *reader++;
655 if (*(reader+2) == '>') {
657 hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
659 *writer++ = *reader++;
663 if (*(reader+2) == '>') {
665 hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
667 *writer++ = *reader++;
671 switch(*(reader+2)) {
673 if (*(reader+3) == '>') {
675 * Check whether we then immediately start more markup of the same type, if so then
676 * lets not close it now and instead continue this markup.
678 if ((*(reader+4) && *(reader+4) == '<') &&
679 (*(reader+5) && *(reader+5) == 'i') &&
680 (*(reader+6) && *(reader+6) == '>')) {
682 * Opening italics right after, so don't close off these italics.
684 hb_deep_log(3, "Joining two sets of italics");
690 if ((*(reader+4) && *(reader+4) == ' ') &&
691 (*(reader+5) && *(reader+5) == '<') &&
692 (*(reader+6) && *(reader+6) == 'i') &&
693 (*(reader+7) && *(reader+7) == '>')) {
695 * Opening italics right after, so don't close off these italics.
697 hb_deep_log(3, "Joining two sets of italics (plus space)");
702 if (stylestack && stylestack->style == ITALIC) {
703 uint8_t style_record[12];
704 stylestack->stop = writer - output - utf8_count;
705 hb_makestyleatom(stylestack, style_record);
707 memcpy(style + 10 + (12 * stylecount), style_record, 12);
710 oldrecord = stylestack;
711 stylestack = stylestack->next;
714 hb_error("Mismatched Subtitle markup '%s'", input);
718 *writer++ = *reader++;
722 if (*(reader+3) == '>') {
723 if (stylestack && stylestack->style == BOLD) {
724 uint8_t style_record[12];
725 stylestack->stop = writer - output - utf8_count;
726 hb_makestyleatom(stylestack, style_record);
728 memcpy(style + 10 + (12 * stylecount), style_record, 12);
730 oldrecord = stylestack;
731 stylestack = stylestack->next;
734 hb_error("Mismatched Subtitle markup '%s'", input);
739 *writer++ = *reader++;
743 if (*(reader+3) == '>') {
744 if (stylestack && stylestack->style == UNDERLINE) {
745 uint8_t style_record[12];
746 stylestack->stop = writer - output - utf8_count;
747 hb_makestyleatom(stylestack, style_record);
749 memcpy(style + 10 + (12 * stylecount), style_record, 12);
752 oldrecord = stylestack;
753 stylestack = stylestack->next;
756 hb_error("Mismatched Subtitle markup '%s'", input);
760 *writer++ = *reader++;
764 *writer++ = *reader++;
769 *writer++ = *reader++;
773 *writer++ = *reader++;
780 *stylesize = 10 + ( stylecount * 12 );
782 memcpy( style + 4, "styl", 4);
786 style[2] = (*stylesize >> 8) & 0xff;
787 style[3] = *stylesize & 0xff;
788 style[8] = (stylecount >> 8) & 0xff;
789 style[9] = stylecount & 0xff;
795 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
798 hb_job_t * job = m->job;
803 if( mux_data == job->mux_data )
807 if( job->vcodec == HB_VCODEC_X264 )
809 if ( buf && buf->start < buf->renderOffset )
811 hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
812 buf->start, buf->renderOffset );
813 buf->renderOffset = buf->start;
817 // We delay muxing video by one frame so that we can calculate
818 // the dts to dts duration of the frames.
826 if( job->vcodec == HB_VCODEC_X264 )
828 // x264 supplies us with DTS, so offset is PTS - DTS
829 offset = buf->start - buf->renderOffset;
832 /* Add the sample before the new frame.
833 It is important that this be calculated prior to the duration
834 of the new video sample, as we want to sync to right after it.
835 (This is because of how durations for text tracks work in QT) */
836 if( job->chapter_markers && buf->new_chap )
838 hb_chapter_t *chapter = NULL;
840 // this chapter is postioned by writing out the previous chapter.
841 // the duration of the previous chapter is the duration up to but
842 // not including the current frame minus the duration of all
843 // chapters up to the previous.
844 // The initial and final chapters can be very short (a second or
845 // less) since they're not really chapters but just a placeholder to
846 // insert a cell command. We don't write chapters shorter than 1.5 sec.
847 duration = m->sum_dur - m->chapter_duration + offset;
848 if ( duration >= (90000*3)/2 )
850 chapter = hb_list_item( m->job->title->list_chapter,
853 MP4AddChapter( m->file,
856 (chapter != NULL) ? chapter->title : NULL);
858 m->current_chapter = buf->new_chap;
859 m->chapter_duration += duration;
863 if( job->vcodec == HB_VCODEC_X264 )
865 // x264 supplies us with DTS
868 duration = m->delay_buf->renderOffset - buf->renderOffset;
872 duration = buf->stop - m->sum_dur;
873 // Due to how libx264 generates DTS, it's possible for the
874 // above calculation to be negative.
876 // x264 generates DTS by rearranging PTS in this sequence:
877 // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
879 // where delay == pts2. This guarantees that DTS <= PTS for
880 // any frame, but also generates this sequence of durations:
881 // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
883 // so the sum up to the last frame is:
884 // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
886 // while the original total duration of the video was:
887 // duration = d0 + d1 + d2 + d3 ... + d(N)
889 // Note that if d0 + d1 != d(N-1) + d(N), the total
890 // length of the video changes since d(N-1) and d(N) are
891 // replaced by d0 and d1 in the final duration sum.
893 // To keep the total length of the video the same as the source
895 // d(N-2) = duration - sum_dur
897 // But if d0 + d1 >= d(N-1) + d(N), the above calculation
898 // results in a nagative value and we need to fix it.
900 duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
905 // We're getting the frames in decode order but the timestamps are
906 // for presentation so we have to use durations and effectively
908 duration = buf->stop - buf->start;
913 /* We got an illegal mp4/h264 duration. This shouldn't
914 be possible and usually indicates a bug in the upstream code.
915 Complain in the hope that someone will go find the bug but
916 try to fix the error so that the file will still be playable. */
917 hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
918 "stop %"PRId64", sum_dur %"PRId64,
919 duration, buf->start, buf->stop, m->sum_dur );
920 /* we don't know when the next frame starts so we can't pick a
921 valid duration for this one. we pick something "short"
922 (roughly 1/3 of an NTSC frame time) to take time from
926 m->sum_dur += duration;
931 duration = MP4_INVALID_DURATION;
934 /* Here's where the sample actually gets muxed. */
935 if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
937 /* Compute dependency flags.
939 * This mechanism is (optionally) used by media players such as QuickTime
940 * to offer better scrubbing performance. The most influential bits are
941 * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
943 * Other bits are possible but no example media using such bits have been
946 * It is acceptable to supply 0-bits for any samples which characteristics
947 * cannot be positively guaranteed.
952 /* encoding layer signals if frame is referenced by other frames */
953 if( buf->flags & HB_FRAME_REF )
954 dflags |= MP4_SDT_HAS_DEPENDENTS;
956 dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
958 switch( buf->frametype )
964 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
967 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
972 break; /* nothing to mark */
975 if( !MP4WriteSampleDependency( m->file,
984 hb_error("Failed to write to output file, disk full?");
988 else if (mux_data->subtitle)
990 if( mux_data->sub_format == TEXTSUB )
992 /* Write an empty sample */
993 if ( mux_data->sum_dur < buf->start )
995 uint8_t empty[2] = {0,0};
996 if( !MP4WriteSample( m->file,
1000 buf->start - mux_data->sum_dur,
1004 hb_error("Failed to write to output file, disk full?");
1007 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1009 uint8_t styleatom[2048];;
1010 uint16_t stylesize = 0;
1011 uint8_t buffer[2048];
1012 uint16_t buffersize = 0;
1013 uint8_t output[2048];
1018 * Copy the subtitle into buffer stripping markup and creating
1019 * style atoms for them.
1021 hb_muxmp4_process_subtitle_style( buf->data,
1023 styleatom, &stylesize );
1025 buffersize = strlen((char*)buffer);
1027 hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
1028 (float)buf->start / 90000, buf->start, buf->stop,
1029 (buf->stop - buf->start), buffer);
1031 /* Write the subtitle sample */
1032 memcpy( output + 2, buffer, buffersize );
1033 memcpy( output + 2 + buffersize, styleatom, stylesize);
1034 output[0] = ( buffersize >> 8 ) & 0xff;
1035 output[1] = buffersize & 0xff;
1037 if( !MP4WriteSample( m->file,
1040 buffersize + stylesize + 2,
1041 buf->stop - buf->start,
1045 hb_error("Failed to write to output file, disk full?");
1049 mux_data->sum_dur += (buf->stop - buf->start);
1057 if( !MP4WriteSample( m->file,
1063 ( buf->frametype & HB_FRAME_KEY ) != 0 ))
1065 hb_error("Failed to write to output file, disk full?");
1069 hb_buffer_close( &buf );
1074 static int MP4End( hb_mux_object_t * m )
1076 hb_job_t * job = m->job;
1077 hb_title_t * title = job->title;
1079 // Flush the delayed frame
1081 MP4Mux( m, job->mux_data, NULL );
1083 /* Write our final chapter marker */
1084 if( m->job->chapter_markers )
1086 hb_chapter_t *chapter = NULL;
1087 int64_t duration = m->sum_dur - m->chapter_duration;
1088 /* The final chapter can have a very short duration - if it's less
1089 * than 1.5 seconds just skip it. */
1090 if ( duration >= (90000*3)/2 )
1093 chapter = hb_list_item( m->job->title->list_chapter,
1094 m->current_chapter - 1 );
1096 MP4AddChapter( m->file,
1099 (chapter != NULL) ? chapter->title : NULL);
1103 if ( job->config.h264.init_delay )
1105 // Insert track edit to get A/V back in sync. The edit amount is
1107 int64_t edit_amt = job->config.h264.init_delay;
1108 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1109 MP4GetTrackDuration(m->file, 1), 0);
1110 if ( m->job->chapter_markers )
1112 // apply same edit to chapter track to keep it in sync with video
1113 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1115 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1120 * Write the MP4 iTunes metadata if we have any metadata
1122 if( title->metadata )
1124 hb_metadata_t *md = title->metadata;
1125 const MP4Tags* tags;
1127 hb_deep_log( 2, "Writing Metadata to output file...");
1129 /* allocate tags structure */
1130 tags = MP4TagsAlloc();
1131 /* fetch data from MP4 file (in case it already has some data) */
1132 MP4TagsFetch( tags, m->file );
1135 if( strlen( md->name ))
1136 MP4TagsSetName( tags, md->name );
1137 if( strlen( md->artist ))
1138 MP4TagsSetArtist( tags, md->artist );
1139 if( strlen( md->composer ))
1140 MP4TagsSetComposer( tags, md->composer );
1141 if( strlen( md->comment ))
1142 MP4TagsSetComments( tags, md->comment );
1143 if( strlen( md->release_date ))
1144 MP4TagsSetReleaseDate( tags, md->release_date );
1145 if( strlen( md->album ))
1146 MP4TagsSetAlbum( tags, md->album );
1147 if( strlen( md->genre ))
1148 MP4TagsSetGenre( tags, md->genre );
1153 art.data = md->coverart;
1154 art.size = md->coverart_size;
1155 art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1156 MP4TagsAddArtwork( tags, &art );
1159 /* push data to MP4 file */
1160 MP4TagsStore( tags, m->file );
1161 /* free memory associated with structure */
1162 MP4TagsFree( tags );
1165 MP4Close( m->file );
1167 if ( job->mp4_optimize )
1169 hb_log( "muxmp4: optimizing file" );
1170 char filename[1024]; memset( filename, 0, 1024 );
1171 snprintf( filename, 1024, "%s.tmp", job->file );
1172 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1173 remove( job->file );
1174 rename( filename, job->file );
1180 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1182 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );