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));
351 mux_data->track = MP4AddAudioTrack(
353 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
355 /* Tune track chunk duration */
356 MP4TuneTrackDurationPerChunk( m, mux_data->track );
358 if (audio->config.out.name == NULL) {
359 MP4SetTrackBytesProperty(
360 m->file, mux_data->track,
362 (const uint8_t*)"Stereo", strlen("Stereo"));
365 MP4SetTrackBytesProperty(
366 m->file, mux_data->track,
368 (const uint8_t*)(audio->config.out.name),
369 strlen(audio->config.out.name));
372 MP4SetAudioProfileLevel( m->file, 0x0F );
373 MP4SetTrackESConfiguration(
374 m->file, mux_data->track,
375 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
377 /* Set the correct number of channels for this track */
378 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
381 /* Set the language for this track */
382 MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
384 if( hb_list_count( title->list_audio ) > 1 )
386 /* Set the audio track alternate group */
387 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
391 /* Enable the first audio track */
392 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
395 /* Disable the other audio tracks so QuickTime doesn't play
398 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
399 hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
404 // Quicktime requires that at least one subtitle is enabled,
405 // else it doesn't show any of the subtitles.
406 // So check to see if any of the subtitles are flagged to be
407 // the defualt. The default will the the enabled track, else
408 // enable the first track.
409 subtitle_default = 0;
410 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
412 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
414 if( subtitle && subtitle->format == TEXTSUB &&
415 subtitle->config.dest == PASSTHRUSUB )
417 if ( subtitle->config.default_track )
418 subtitle_default = 1;
421 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
423 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
425 if( subtitle && subtitle->format == TEXTSUB &&
426 subtitle->config.dest == PASSTHRUSUB )
428 uint64_t width, height = 60;
429 if( job->anamorphic.mode )
430 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
434 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
435 subtitle->mux_data = mux_data;
436 mux_data->subtitle = 1;
437 mux_data->sub_format = subtitle->format;
438 mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
440 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
442 /* Tune track chunk duration */
443 MP4TuneTrackDurationPerChunk( m, mux_data->track );
445 const uint8_t textColor[4] = { 255,255,255,255 };
447 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
449 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
450 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
451 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
453 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
455 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
456 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
458 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
459 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
461 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
462 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
463 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
464 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
466 /* translate the track */
469 uint32_t *ptr32 = (uint32_t*) nval;
472 MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
473 memcpy(nval, val, size);
475 const uint32_t ytranslation = (job->height - height) * 0x10000;
477 #ifdef WORDS_BIGENDIAN
478 ptr32[7] = ytranslation;
480 /* we need to switch the endianness, as the file format expects big endian */
481 ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) +
482 ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
485 MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);
486 if ( !subtitle_default || subtitle->config.default_track ) {
487 /* Enable the default subtitle track */
488 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
489 subtitle_default = 1;
493 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
498 if (job->chapter_markers)
500 /* add a text track for the chapters. We add the 'chap' atom to track
501 one which is usually the video track & should never be disabled.
502 The Quicktime spec says it doesn't matter which media track the
503 chap atom is on but it has to be an enabled track. */
504 MP4TrackId textTrack;
505 textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
507 m->chapter_track = textTrack;
508 m->chapter_duration = 0;
509 m->current_chapter = job->chapter_start;
512 /* Add encoded-by metadata listing version and build date */
514 tool_string = (char *)malloc(80);
515 snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
517 /* allocate,fetch,populate,store,free tags structure */
519 tags = MP4TagsAlloc();
520 MP4TagsFetch( tags, m->file );
521 MP4TagsSetEncodingTool( tags, tool_string );
522 MP4TagsStore( tags, m->file );
530 typedef struct stylerecord_s {
531 enum style_s {ITALIC, BOLD, UNDERLINE} style;
534 struct stylerecord_s *next;
537 static void hb_makestylerecord( stylerecord **stack,
538 enum style_s style, int start )
540 stylerecord *record = calloc( sizeof( stylerecord ), 1 );
544 record->style = style;
545 record->start = start;
546 record->next = *stack;
551 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
554 hb_deep_log(3, "Made style '%s' from %d to %d",
555 record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
557 switch( record->style )
573 style[0] = (record->start >> 8) & 0xff; // startChar
574 style[1] = record->start & 0xff;
575 style[2] = (record->stop >> 8) & 0xff; // endChar
576 style[3] = record->stop & 0xff;
577 style[4] = (1 >> 8) & 0xff; // font-ID
579 style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline
580 style[7] = 24; // font-size
583 style[10] = 255; // b
584 style[11] = 255; // a
589 * Copy the input to output removing markup and adding markup to the style
590 * atom where appropriate.
592 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
594 uint8_t *style, uint16_t *stylesize )
596 uint8_t *reader = input;
597 uint8_t *writer = output;
598 uint8_t stylecount = 0;
599 uint16_t utf8_count = 0; // utf8 count from start of subtitle
600 stylerecord *stylestack = NULL;
601 stylerecord *oldrecord = NULL;
603 while(*reader != '\0') {
604 if( ( *reader & 0xc0 ) == 0x80 )
607 * Track the utf8_count when doing markup so that we get the tx3g stops
608 * based on UTF8 chr counts rather than bytes.
611 hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far",
614 if (*reader == '<') {
616 * possible markup, peek at the next chr
618 switch(*(reader+1)) {
620 if (*(reader+2) == '>') {
622 hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
624 *writer++ = *reader++;
628 if (*(reader+2) == '>') {
630 hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
632 *writer++ = *reader++;
636 if (*(reader+2) == '>') {
638 hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
640 *writer++ = *reader++;
644 switch(*(reader+2)) {
646 if (*(reader+3) == '>') {
648 * Check whether we then immediately start more markup of the same type, if so then
649 * lets not close it now and instead continue this markup.
651 if ((*(reader+4) && *(reader+4) == '<') &&
652 (*(reader+5) && *(reader+5) == 'i') &&
653 (*(reader+6) && *(reader+6) == '>')) {
655 * Opening italics right after, so don't close off these italics.
657 hb_deep_log(3, "Joining two sets of italics");
663 if ((*(reader+4) && *(reader+4) == ' ') &&
664 (*(reader+5) && *(reader+5) == '<') &&
665 (*(reader+6) && *(reader+6) == 'i') &&
666 (*(reader+7) && *(reader+7) == '>')) {
668 * Opening italics right after, so don't close off these italics.
670 hb_deep_log(3, "Joining two sets of italics (plus space)");
675 if (stylestack && stylestack->style == ITALIC) {
676 uint8_t style_record[12];
677 stylestack->stop = writer - output - utf8_count;
678 hb_makestyleatom(stylestack, style_record);
680 memcpy(style + 10 + (12 * stylecount), style_record, 12);
683 oldrecord = stylestack;
684 stylestack = stylestack->next;
687 hb_error("Mismatched Subtitle markup '%s'", input);
691 *writer++ = *reader++;
695 if (*(reader+3) == '>') {
696 if (stylestack && stylestack->style == BOLD) {
697 uint8_t style_record[12];
698 stylestack->stop = writer - output - utf8_count;
699 hb_makestyleatom(stylestack, style_record);
701 memcpy(style + 10 + (12 * stylecount), style_record, 12);
703 oldrecord = stylestack;
704 stylestack = stylestack->next;
707 hb_error("Mismatched Subtitle markup '%s'", input);
712 *writer++ = *reader++;
716 if (*(reader+3) == '>') {
717 if (stylestack && stylestack->style == UNDERLINE) {
718 uint8_t style_record[12];
719 stylestack->stop = writer - output - utf8_count;
720 hb_makestyleatom(stylestack, style_record);
722 memcpy(style + 10 + (12 * stylecount), style_record, 12);
725 oldrecord = stylestack;
726 stylestack = stylestack->next;
729 hb_error("Mismatched Subtitle markup '%s'", input);
733 *writer++ = *reader++;
737 *writer++ = *reader++;
742 *writer++ = *reader++;
746 *writer++ = *reader++;
753 *stylesize = 10 + ( stylecount * 12 );
755 memcpy( style + 4, "styl", 4);
759 style[2] = (*stylesize >> 8) & 0xff;
760 style[3] = *stylesize & 0xff;
761 style[8] = (stylecount >> 8) & 0xff;
762 style[9] = stylecount & 0xff;
768 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
771 hb_job_t * job = m->job;
776 if( mux_data == job->mux_data )
780 if( job->vcodec == HB_VCODEC_X264 )
782 if ( buf && buf->start < buf->renderOffset )
784 hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
785 buf->start, buf->renderOffset );
786 buf->renderOffset = buf->start;
790 // We delay muxing video by one frame so that we can calculate
791 // the dts to dts duration of the frames.
799 if( job->vcodec == HB_VCODEC_X264 )
801 // x264 supplies us with DTS, so offset is PTS - DTS
802 offset = buf->start - buf->renderOffset;
805 /* Add the sample before the new frame.
806 It is important that this be calculated prior to the duration
807 of the new video sample, as we want to sync to right after it.
808 (This is because of how durations for text tracks work in QT) */
809 if( job->chapter_markers && buf->new_chap )
811 hb_chapter_t *chapter = NULL;
813 // this chapter is postioned by writing out the previous chapter.
814 // the duration of the previous chapter is the duration up to but
815 // not including the current frame minus the duration of all
816 // chapters up to the previous.
817 // The initial and final chapters can be very short (a second or
818 // less) since they're not really chapters but just a placeholder to
819 // insert a cell command. We don't write chapters shorter than 1.5 sec.
820 duration = m->sum_dur - m->chapter_duration + offset;
821 if ( duration >= (90000*3)/2 )
823 chapter = hb_list_item( m->job->title->list_chapter,
826 MP4AddChapter( m->file,
829 (chapter != NULL) ? chapter->title : NULL);
831 m->current_chapter = buf->new_chap;
832 m->chapter_duration += duration;
836 if( job->vcodec == HB_VCODEC_X264 )
838 // x264 supplies us with DTS
841 duration = m->delay_buf->renderOffset - buf->renderOffset;
845 duration = buf->stop - m->sum_dur;
846 // Due to how libx264 generates DTS, it's possible for the
847 // above calculation to be negative.
849 // x264 generates DTS by rearranging PTS in this sequence:
850 // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
852 // where delay == pts2. This guarantees that DTS <= PTS for
853 // any frame, but also generates this sequence of durations:
854 // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
856 // so the sum up to the last frame is:
857 // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
859 // while the original total duration of the video was:
860 // duration = d0 + d1 + d2 + d3 ... + d(N)
862 // Note that if d0 + d1 != d(N-1) + d(N), the total
863 // length of the video changes since d(N-1) and d(N) are
864 // replaced by d0 and d1 in the final duration sum.
866 // To keep the total length of the video the same as the source
868 // d(N-2) = duration - sum_dur
870 // But if d0 + d1 >= d(N-1) + d(N), the above calculation
871 // results in a nagative value and we need to fix it.
873 duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
878 // We're getting the frames in decode order but the timestamps are
879 // for presentation so we have to use durations and effectively
881 duration = buf->stop - buf->start;
886 /* We got an illegal mp4/h264 duration. This shouldn't
887 be possible and usually indicates a bug in the upstream code.
888 Complain in the hope that someone will go find the bug but
889 try to fix the error so that the file will still be playable. */
890 hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
891 "stop %"PRId64", sum_dur %"PRId64,
892 duration, buf->start, buf->stop, m->sum_dur );
893 /* we don't know when the next frame starts so we can't pick a
894 valid duration for this one. we pick something "short"
895 (roughly 1/3 of an NTSC frame time) to take time from
899 m->sum_dur += duration;
904 duration = MP4_INVALID_DURATION;
907 /* Here's where the sample actually gets muxed. */
908 if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
910 /* Compute dependency flags.
912 * This mechanism is (optionally) used by media players such as QuickTime
913 * to offer better scrubbing performance. The most influential bits are
914 * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
916 * Other bits are possible but no example media using such bits have been
919 * It is acceptable to supply 0-bits for any samples which characteristics
920 * cannot be positively guaranteed.
925 /* encoding layer signals if frame is referenced by other frames */
926 if( buf->flags & HB_FRAME_REF )
927 dflags |= MP4_SDT_HAS_DEPENDENTS;
929 dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
931 switch( buf->frametype )
937 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
940 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
945 break; /* nothing to mark */
948 if( !MP4WriteSampleDependency( m->file,
957 hb_error("Failed to write to output file, disk full?");
961 else if (mux_data->subtitle)
963 if( mux_data->sub_format == TEXTSUB )
965 /* Write an empty sample */
966 if ( mux_data->sum_dur < buf->start )
968 uint8_t empty[2] = {0,0};
969 if( !MP4WriteSample( m->file,
973 buf->start - mux_data->sum_dur,
977 hb_error("Failed to write to output file, disk full?");
980 mux_data->sum_dur += buf->start - mux_data->sum_dur;
982 uint8_t styleatom[2048];;
983 uint16_t stylesize = 0;
984 uint8_t buffer[2048];
985 uint16_t buffersize = 0;
986 uint8_t output[2048];
991 * Copy the subtitle into buffer stripping markup and creating
992 * style atoms for them.
994 hb_muxmp4_process_subtitle_style( buf->data,
996 styleatom, &stylesize );
998 buffersize = strlen((char*)buffer);
1000 hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
1001 (float)buf->start / 90000, buf->start, buf->stop,
1002 (buf->stop - buf->start), buffer);
1004 /* Write the subtitle sample */
1005 memcpy( output + 2, buffer, buffersize );
1006 memcpy( output + 2 + buffersize, styleatom, stylesize);
1007 output[0] = ( buffersize >> 8 ) & 0xff;
1008 output[1] = buffersize & 0xff;
1010 if( !MP4WriteSample( m->file,
1013 buffersize + stylesize + 2,
1014 buf->stop - buf->start,
1018 hb_error("Failed to write to output file, disk full?");
1022 mux_data->sum_dur += (buf->stop - buf->start);
1030 if( !MP4WriteSample( m->file,
1036 ( buf->frametype & HB_FRAME_KEY ) != 0 ))
1038 hb_error("Failed to write to output file, disk full?");
1042 hb_buffer_close( &buf );
1047 static int MP4End( hb_mux_object_t * m )
1049 hb_job_t * job = m->job;
1050 hb_title_t * title = job->title;
1052 // Flush the delayed frame
1054 MP4Mux( m, job->mux_data, NULL );
1056 /* Write our final chapter marker */
1057 if( m->job->chapter_markers )
1059 hb_chapter_t *chapter = NULL;
1060 int64_t duration = m->sum_dur - m->chapter_duration;
1061 /* The final chapter can have a very short duration - if it's less
1062 * than 1.5 seconds just skip it. */
1063 if ( duration >= (90000*3)/2 )
1066 chapter = hb_list_item( m->job->title->list_chapter,
1067 m->current_chapter - 1 );
1069 MP4AddChapter( m->file,
1072 (chapter != NULL) ? chapter->title : NULL);
1076 if ( job->config.h264.init_delay )
1078 // Insert track edit to get A/V back in sync. The edit amount is
1080 int64_t edit_amt = job->config.h264.init_delay;
1081 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1082 MP4GetTrackDuration(m->file, 1), 0);
1083 if ( m->job->chapter_markers )
1085 // apply same edit to chapter track to keep it in sync with video
1086 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1088 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1093 * Write the MP4 iTunes metadata if we have any metadata
1095 if( title->metadata )
1097 hb_metadata_t *md = title->metadata;
1098 const MP4Tags* tags;
1100 hb_deep_log( 2, "Writing Metadata to output file...");
1102 /* allocate tags structure */
1103 tags = MP4TagsAlloc();
1104 /* fetch data from MP4 file (in case it already has some data) */
1105 MP4TagsFetch( tags, m->file );
1108 if( strlen( md->name ))
1109 MP4TagsSetName( tags, md->name );
1110 if( strlen( md->artist ))
1111 MP4TagsSetArtist( tags, md->artist );
1112 if( strlen( md->composer ))
1113 MP4TagsSetComposer( tags, md->composer );
1114 if( strlen( md->comment ))
1115 MP4TagsSetComments( tags, md->comment );
1116 if( strlen( md->release_date ))
1117 MP4TagsSetReleaseDate( tags, md->release_date );
1118 if( strlen( md->album ))
1119 MP4TagsSetAlbum( tags, md->album );
1120 if( strlen( md->genre ))
1121 MP4TagsSetGenre( tags, md->genre );
1126 art.data = md->coverart;
1127 art.size = md->coverart_size;
1128 art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1129 MP4TagsAddArtwork( tags, &art );
1132 /* push data to MP4 file */
1133 MP4TagsStore( tags, m->file );
1134 /* free memory associated with structure */
1135 MP4TagsFree( tags );
1138 MP4Close( m->file );
1140 if ( job->mp4_optimize )
1142 hb_log( "muxmp4: optimizing file" );
1143 char filename[1024]; memset( filename, 0, 1024 );
1144 snprintf( filename, 1024, "%s.tmp", job->file );
1145 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1146 remove( job->file );
1147 rename( filename, job->file );
1153 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1155 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );