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 // bias to keep render offsets in ctts atom positive (set up by encx264)
26 /* Chapter state information for muxing */
27 MP4TrackId chapter_track;
29 uint64_t chapter_duration;
38 uint64_t sum_dur; // sum of the frame durations so far
41 /* Tune video track chunk duration.
42 * libmp4v2 default duration == dusamplerate == 1 second.
43 * Per van's suggestion we desire duration == 4 frames.
44 * Should be invoked immediately after track creation.
46 * return true on fail, false on success.
48 static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId )
53 tscale = MP4GetTrackTimeScale( m->file, trackId );
54 dur = (MP4Duration)ceil( (double)tscale * (double)m->job->vrate_base / (double)m->job->vrate * 4.0 );
56 if( !MP4SetTrackDurationPerChunk( m->file, trackId, dur ))
58 hb_error( "muxmp4.c: MP4SetTrackDurationPerChunk failed!" );
63 hb_deep_log( 2, "muxmp4: track %u, chunk duration %"PRIu64, MP4FindTrackIndex( m->file, trackId ), dur );
67 /**********************************************************************
69 **********************************************************************
70 * Allocates hb_mux_data_t structures, create file and write headers
71 *********************************************************************/
72 static int MP4Init( hb_mux_object_t * m )
74 hb_job_t * job = m->job;
75 hb_title_t * title = job->title;
78 hb_mux_data_t * mux_data;
82 /* Flags for enabling/disabling tracks in an MP4. */
83 typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
85 /* Create an empty mp4 file */
86 if (job->largeFileSize)
87 /* Use 64-bit MP4 file */
89 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
90 hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
93 /* Limit MP4s to less than 4 GB */
95 m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
98 if (m->file == MP4_INVALID_FILE_HANDLE)
100 hb_error("muxmp4.c: MP4Create failed!");
106 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
107 job->mux_data = mux_data;
109 if (!(MP4SetTimeScale( m->file, 90000 )))
111 hb_error("muxmp4.c: MP4SetTimeScale failed!");
116 if( job->vcodec == HB_VCODEC_X264 )
118 /* Stolen from mp4creator */
119 MP4SetVideoProfileLevel( m->file, 0x7F );
120 mux_data->track = MP4AddH264VideoTrack( m->file, 90000,
121 MP4_INVALID_DURATION, job->width, job->height,
122 job->config.h264.sps[1], /* AVCProfileIndication */
123 job->config.h264.sps[2], /* profile_compat */
124 job->config.h264.sps[3], /* AVCLevelIndication */
125 3 ); /* 4 bytes length before each NAL unit */
126 if ( mux_data->track == MP4_INVALID_TRACK_ID )
128 hb_error( "muxmp4.c: MP4AddH264VideoTrack failed!" );
133 /* Tune track chunk duration */
134 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
139 MP4AddH264SequenceParameterSet( m->file, mux_data->track,
140 job->config.h264.sps, job->config.h264.sps_length );
141 MP4AddH264PictureParameterSet( m->file, mux_data->track,
142 job->config.h264.pps, job->config.h264.pps_length );
144 if( job->h264_level == 30 || job->ipod_atom)
146 hb_deep_log( 2, "muxmp4: adding iPod atom");
147 MP4AddIPodUUID(m->file, mux_data->track);
150 m->init_delay = job->config.h264.init_delay;
152 else /* FFmpeg or XviD */
154 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
155 mux_data->track = MP4AddVideoTrack( m->file, 90000,
156 MP4_INVALID_DURATION, job->width, job->height,
157 MP4_MPEG4_VIDEO_TYPE );
158 if (mux_data->track == MP4_INVALID_TRACK_ID)
160 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
165 /* Tune track chunk duration */
166 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
171 /* VOL from FFmpeg or XviD */
172 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
173 job->config.mpeg4.bytes, job->config.mpeg4.length )))
175 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
181 // COLR atom for color and gamma correction.
183 // http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
184 // http://forum.doom9.org/showthread.php?t=133982#post1090068
185 // the user can set it from job->color_matrix, otherwise by default
186 // we say anything that's likely to be HD content is ITU BT.709 and
187 // DVD, SD TV & other content is ITU BT.601. We look at the title height
188 // rather than the job height here to get uncropped input dimensions.
189 if( job->color_matrix == 1 )
191 // ITU BT.601 DVD or SD TV content
192 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
194 else if( job->color_matrix == 2 )
196 // ITU BT.709 HD content
197 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
199 else if ( job->title->width >= 1280 || job->title->height >= 720 )
201 // we guess that 720p or above is ITU BT.709 HD content
202 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
206 // ITU BT.601 DVD or SD TV content
207 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
210 if( job->anamorphic.mode )
212 /* PASP atom for anamorphic video */
215 width = job->anamorphic.par_width;
217 height = job->anamorphic.par_height;
219 MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
221 MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
224 /* add the audio tracks */
225 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
227 audio = hb_list_item( title->list_audio, i );
228 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
229 audio->priv.mux_data = mux_data;
231 if( audio->config.out.codec == HB_ACODEC_AC3 )
234 uint8_t bsid = audio->config.in.version;
235 uint8_t bsmod = audio->config.in.mode;
236 uint8_t acmod = audio->config.flags.ac3 & 0x7;
237 uint8_t lfeon = (audio->config.flags.ac3 & A52_LFE) ? 1 : 0;
238 uint8_t bit_rate_code = 0;
241 * Rewrite AC3 information into correct format for dac3 atom
243 switch( audio->config.in.samplerate )
256 * Error value, tells decoder to not decode this audio.
262 switch( audio->config.in.bitrate )
322 hb_error("Unknown AC3 bitrate");
327 mux_data->track = MP4AddAC3AudioTrack(
329 audio->config.out.samplerate,
337 /* Tune track chunk duration */
338 MP4TuneTrackDurationPerChunk( m, mux_data->track );
340 if (audio->config.out.name == NULL) {
341 MP4SetTrackBytesProperty(
342 m->file, mux_data->track,
344 (const uint8_t*)"Surround", strlen("Surround"));
347 MP4SetTrackBytesProperty(
348 m->file, mux_data->track,
350 (const uint8_t*)(audio->config.out.name),
351 strlen(audio->config.out.name));
354 mux_data->track = MP4AddAudioTrack(
356 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
358 /* Tune track chunk duration */
359 MP4TuneTrackDurationPerChunk( m, mux_data->track );
361 if (audio->config.out.name == NULL) {
362 MP4SetTrackBytesProperty(
363 m->file, mux_data->track,
365 (const uint8_t*)"Stereo", strlen("Stereo"));
368 MP4SetTrackBytesProperty(
369 m->file, mux_data->track,
371 (const uint8_t*)(audio->config.out.name),
372 strlen(audio->config.out.name));
375 MP4SetAudioProfileLevel( m->file, 0x0F );
376 MP4SetTrackESConfiguration(
377 m->file, mux_data->track,
378 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
380 /* Set the correct number of channels for this track */
381 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
384 /* Set the language for this track */
385 MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
387 if( hb_list_count( title->list_audio ) > 1 )
389 /* Set the audio track alternate group */
390 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
394 /* Enable the first audio track */
395 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
398 /* Disable the other audio tracks so QuickTime doesn't play
401 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
402 hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
407 // Quicktime requires that at least one subtitle is enabled,
408 // else it doesn't show any of the subtitles.
409 // So check to see if any of the subtitles are flagged to be
410 // the defualt. The default will the the enabled track, else
411 // enable the first track.
412 subtitle_default = 0;
413 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
415 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
417 if( subtitle && subtitle->format == TEXTSUB &&
418 subtitle->config.dest == PASSTHRUSUB )
420 if ( subtitle->config.default_track )
421 subtitle_default = 1;
424 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
426 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
428 if( subtitle && subtitle->format == TEXTSUB &&
429 subtitle->config.dest == PASSTHRUSUB )
431 uint64_t width, height = 60;
432 if( job->anamorphic.mode )
433 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
437 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
438 subtitle->mux_data = mux_data;
439 mux_data->subtitle = 1;
440 mux_data->sub_format = subtitle->format;
441 mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
443 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
445 /* Tune track chunk duration */
446 MP4TuneTrackDurationPerChunk( m, mux_data->track );
448 const uint8_t textColor[4] = { 255,255,255,255 };
450 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
452 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
453 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
454 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
456 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
458 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
459 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
461 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
462 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
464 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
465 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
466 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
467 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
469 /* translate the track */
472 uint32_t *ptr32 = (uint32_t*) nval;
475 MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
476 memcpy(nval, val, size);
478 const uint32_t ytranslation = (job->height - height) * 0x10000;
480 #ifdef WORDS_BIGENDIAN
481 ptr32[7] = ytranslation;
483 /* we need to switch the endianness, as the file format expects big endian */
484 ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) +
485 ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
488 MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);
489 if ( !subtitle_default || subtitle->config.default_track ) {
490 /* Enable the default subtitle track */
491 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
492 subtitle_default = 1;
496 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
501 if (job->chapter_markers)
503 /* add a text track for the chapters. We add the 'chap' atom to track
504 one which is usually the video track & should never be disabled.
505 The Quicktime spec says it doesn't matter which media track the
506 chap atom is on but it has to be an enabled track. */
507 MP4TrackId textTrack;
508 textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
510 m->chapter_track = textTrack;
511 m->chapter_duration = 0;
512 m->current_chapter = job->chapter_start;
515 /* Add encoded-by metadata listing version and build date */
517 tool_string = (char *)malloc(80);
518 snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
520 /* allocate,fetch,populate,store,free tags structure */
522 tags = MP4TagsAlloc();
523 MP4TagsFetch( tags, m->file );
524 MP4TagsSetEncodingTool( tags, tool_string );
525 MP4TagsStore( tags, m->file );
533 typedef struct stylerecord_s {
534 enum style_s {ITALIC, BOLD, UNDERLINE} style;
537 struct stylerecord_s *next;
540 static void hb_makestylerecord( stylerecord **stack,
541 enum style_s style, int start )
543 stylerecord *record = calloc( sizeof( stylerecord ), 1 );
547 record->style = style;
548 record->start = start;
549 record->next = *stack;
554 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
557 hb_deep_log(3, "Made style '%s' from %d to %d",
558 record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
560 switch( record->style )
576 style[0] = (record->start >> 8) & 0xff; // startChar
577 style[1] = record->start & 0xff;
578 style[2] = (record->stop >> 8) & 0xff; // endChar
579 style[3] = record->stop & 0xff;
580 style[4] = (1 >> 8) & 0xff; // font-ID
582 style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline
583 style[7] = 24; // font-size
586 style[10] = 255; // b
587 style[11] = 255; // a
592 * Copy the input to output removing markup and adding markup to the style
593 * atom where appropriate.
595 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
597 uint8_t *style, uint16_t *stylesize )
599 uint8_t *reader = input;
600 uint8_t *writer = output;
601 uint8_t stylecount = 0;
602 uint16_t utf8_count = 0; // utf8 count from start of subtitle
603 stylerecord *stylestack = NULL;
604 stylerecord *oldrecord = NULL;
606 while(*reader != '\0') {
607 if( ( *reader & 0xc0 ) == 0x80 )
610 * Track the utf8_count when doing markup so that we get the tx3g stops
611 * based on UTF8 chr counts rather than bytes.
614 hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far",
617 if (*reader == '<') {
619 * possible markup, peek at the next chr
621 switch(*(reader+1)) {
623 if (*(reader+2) == '>') {
625 hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
627 *writer++ = *reader++;
631 if (*(reader+2) == '>') {
633 hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
635 *writer++ = *reader++;
639 if (*(reader+2) == '>') {
641 hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
643 *writer++ = *reader++;
647 switch(*(reader+2)) {
649 if (*(reader+3) == '>') {
651 * Check whether we then immediately start more markup of the same type, if so then
652 * lets not close it now and instead continue this markup.
654 if ((*(reader+4) && *(reader+4) == '<') &&
655 (*(reader+5) && *(reader+5) == 'i') &&
656 (*(reader+6) && *(reader+6) == '>')) {
658 * Opening italics right after, so don't close off these italics.
660 hb_deep_log(3, "Joining two sets of italics");
666 if ((*(reader+4) && *(reader+4) == ' ') &&
667 (*(reader+5) && *(reader+5) == '<') &&
668 (*(reader+6) && *(reader+6) == 'i') &&
669 (*(reader+7) && *(reader+7) == '>')) {
671 * Opening italics right after, so don't close off these italics.
673 hb_deep_log(3, "Joining two sets of italics (plus space)");
678 if (stylestack && stylestack->style == ITALIC) {
679 uint8_t style_record[12];
680 stylestack->stop = writer - output - utf8_count;
681 hb_makestyleatom(stylestack, style_record);
683 memcpy(style + 10 + (12 * stylecount), style_record, 12);
686 oldrecord = stylestack;
687 stylestack = stylestack->next;
690 hb_error("Mismatched Subtitle markup '%s'", input);
694 *writer++ = *reader++;
698 if (*(reader+3) == '>') {
699 if (stylestack && stylestack->style == BOLD) {
700 uint8_t style_record[12];
701 stylestack->stop = writer - output - utf8_count;
702 hb_makestyleatom(stylestack, style_record);
704 memcpy(style + 10 + (12 * stylecount), style_record, 12);
706 oldrecord = stylestack;
707 stylestack = stylestack->next;
710 hb_error("Mismatched Subtitle markup '%s'", input);
715 *writer++ = *reader++;
719 if (*(reader+3) == '>') {
720 if (stylestack && stylestack->style == UNDERLINE) {
721 uint8_t style_record[12];
722 stylestack->stop = writer - output - utf8_count;
723 hb_makestyleatom(stylestack, style_record);
725 memcpy(style + 10 + (12 * stylecount), style_record, 12);
728 oldrecord = stylestack;
729 stylestack = stylestack->next;
732 hb_error("Mismatched Subtitle markup '%s'", input);
736 *writer++ = *reader++;
740 *writer++ = *reader++;
745 *writer++ = *reader++;
749 *writer++ = *reader++;
756 *stylesize = 10 + ( stylecount * 12 );
758 memcpy( style + 4, "styl", 4);
762 style[2] = (*stylesize >> 8) & 0xff;
763 style[3] = *stylesize & 0xff;
764 style[8] = (stylecount >> 8) & 0xff;
765 style[9] = stylecount & 0xff;
771 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
774 hb_job_t * job = m->job;
778 if( mux_data == job->mux_data )
782 // if there are b-frames compute the render offset
783 // (we'll need it for both the video frame & the chapter track)
786 offset = buf->start + m->init_delay - m->sum_dur;
789 hb_log("MP4Mux: illegal render offset %"PRId64", start %"PRId64","
790 "stop %"PRId64", sum_dur %"PRId64,
791 offset, buf->start, buf->stop, m->sum_dur );
796 /* Add the sample before the new frame.
797 It is important that this be calculated prior to the duration
798 of the new video sample, as we want to sync to right after it.
799 (This is because of how durations for text tracks work in QT) */
800 if( job->chapter_markers && buf->new_chap )
802 hb_chapter_t *chapter = NULL;
804 // this chapter is postioned by writing out the previous chapter.
805 // the duration of the previous chapter is the duration up to but
806 // not including the current frame minus the duration of all
807 // chapters up to the previous.
808 // The initial and final chapters can be very short (a second or
809 // less) since they're not really chapters but just a placeholder to
810 // insert a cell command. We don't write chapters shorter than 1.5 sec.
811 duration = m->sum_dur - m->chapter_duration + offset;
812 if ( duration >= (90000*3)/2 )
814 chapter = hb_list_item( m->job->title->list_chapter,
817 MP4AddChapter( m->file,
820 (chapter != NULL) ? chapter->title : NULL);
822 m->current_chapter = buf->new_chap;
823 m->chapter_duration += duration;
827 // We're getting the frames in decode order but the timestamps are
828 // for presentation so we have to use durations and effectively
830 duration = buf->stop - buf->start;
833 /* We got an illegal mp4/h264 duration. This shouldn't
834 be possible and usually indicates a bug in the upstream code.
835 Complain in the hope that someone will go find the bug but
836 try to fix the error so that the file will still be playable. */
837 hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
838 "stop %"PRId64", sum_dur %"PRId64,
839 duration, buf->start, buf->stop, m->sum_dur );
840 /* we don't know when the next frame starts so we can't pick a
841 valid duration for this one. we pick something "short"
842 (roughly 1/3 of an NTSC frame time) to take time from
846 m->sum_dur += duration;
851 duration = MP4_INVALID_DURATION;
854 /* Here's where the sample actually gets muxed. */
855 if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
857 /* Compute dependency flags.
859 * This mechanism is (optionally) used by media players such as QuickTime
860 * to offer better scrubbing performance. The most influential bits are
861 * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
863 * Other bits are possible but no example media using such bits have been
866 * It is acceptable to supply 0-bits for any samples which characteristics
867 * cannot be positively guaranteed.
872 /* encoding layer signals if frame is referenced by other frames */
873 if( buf->flags & HB_FRAME_REF )
874 dflags |= MP4_SDT_HAS_DEPENDENTS;
876 dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
878 switch( buf->frametype )
884 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
887 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
892 break; /* nothing to mark */
895 if( !MP4WriteSampleDependency( m->file,
904 hb_error("Failed to write to output file, disk full?");
908 else if (mux_data->subtitle)
910 if( mux_data->sub_format == TEXTSUB )
912 /* Write an empty sample */
913 if ( mux_data->sum_dur < buf->start )
915 uint8_t empty[2] = {0,0};
916 if( !MP4WriteSample( m->file,
920 buf->start - mux_data->sum_dur,
924 hb_error("Failed to write to output file, disk full?");
927 mux_data->sum_dur += buf->start - mux_data->sum_dur;
929 uint8_t styleatom[2048];;
930 uint16_t stylesize = 0;
931 uint8_t buffer[2048];
932 uint16_t buffersize = 0;
933 uint8_t output[2048];
938 * Copy the subtitle into buffer stripping markup and creating
939 * style atoms for them.
941 hb_muxmp4_process_subtitle_style( buf->data,
943 styleatom, &stylesize );
945 buffersize = strlen((char*)buffer);
947 hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
948 (float)buf->start / 90000, buf->start, buf->stop,
949 (buf->stop - buf->start), buffer);
951 /* Write the subtitle sample */
952 memcpy( output + 2, buffer, buffersize );
953 memcpy( output + 2 + buffersize, styleatom, stylesize);
954 output[0] = ( buffersize >> 8 ) & 0xff;
955 output[1] = buffersize & 0xff;
957 if( !MP4WriteSample( m->file,
960 buffersize + stylesize + 2,
961 buf->stop - buf->start,
965 hb_error("Failed to write to output file, disk full?");
969 mux_data->sum_dur += (buf->stop - buf->start);
977 if( !MP4WriteSample( m->file,
983 ( buf->frametype & HB_FRAME_KEY ) != 0 ))
985 hb_error("Failed to write to output file, disk full?");
994 static int MP4End( hb_mux_object_t * m )
996 hb_job_t * job = m->job;
997 hb_title_t * title = job->title;
999 /* Write our final chapter marker */
1000 if( m->job->chapter_markers )
1002 hb_chapter_t *chapter = NULL;
1003 int64_t duration = m->sum_dur - m->chapter_duration;
1004 /* The final chapter can have a very short duration - if it's less
1005 * than 1.5 seconds just skip it. */
1006 if ( duration >= (90000*3)/2 )
1009 chapter = hb_list_item( m->job->title->list_chapter,
1010 m->current_chapter - 1 );
1012 MP4AddChapter( m->file,
1015 (chapter != NULL) ? chapter->title : NULL);
1019 if (job->areBframes)
1021 // Insert track edit to get A/V back in sync. The edit amount is
1023 int64_t edit_amt = m->init_delay;
1024 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1025 MP4GetTrackDuration(m->file, 1), 0);
1026 if ( m->job->chapter_markers )
1028 // apply same edit to chapter track to keep it in sync with video
1029 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1031 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1036 * Write the MP4 iTunes metadata if we have any metadata
1038 if( title->metadata )
1040 hb_metadata_t *md = title->metadata;
1041 const MP4Tags* tags;
1043 hb_deep_log( 2, "Writing Metadata to output file...");
1045 /* allocate tags structure */
1046 tags = MP4TagsAlloc();
1047 /* fetch data from MP4 file (in case it already has some data) */
1048 MP4TagsFetch( tags, m->file );
1051 if( strlen( md->name ))
1052 MP4TagsSetName( tags, md->name );
1053 if( strlen( md->artist ))
1054 MP4TagsSetArtist( tags, md->artist );
1055 if( strlen( md->composer ))
1056 MP4TagsSetComposer( tags, md->composer );
1057 if( strlen( md->comment ))
1058 MP4TagsSetComments( tags, md->comment );
1059 if( strlen( md->release_date ))
1060 MP4TagsSetReleaseDate( tags, md->release_date );
1061 if( strlen( md->album ))
1062 MP4TagsSetAlbum( tags, md->album );
1063 if( strlen( md->genre ))
1064 MP4TagsSetGenre( tags, md->genre );
1069 art.data = md->coverart;
1070 art.size = md->coverart_size;
1071 art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1072 MP4TagsAddArtwork( tags, &art );
1075 /* push data to MP4 file */
1076 MP4TagsStore( tags, m->file );
1077 /* free memory associated with structure */
1078 MP4TagsFree( tags );
1081 MP4Close( m->file );
1083 if ( job->mp4_optimize )
1085 hb_log( "muxmp4: optimizing file" );
1086 char filename[1024]; memset( filename, 0, 1024 );
1087 snprintf( filename, 1024, "%s.tmp", job->file );
1088 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1089 remove( job->file );
1090 rename( filename, job->file );
1096 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1098 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );