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 %llu", 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;
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 m->init_delay = job->config.h264.init_delay;
151 else /* FFmpeg or XviD */
153 MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
154 mux_data->track = MP4AddVideoTrack( m->file, 90000,
155 MP4_INVALID_DURATION, job->width, job->height,
156 MP4_MPEG4_VIDEO_TYPE );
157 if (mux_data->track == MP4_INVALID_TRACK_ID)
159 hb_error("muxmp4.c: MP4AddVideoTrack failed!");
164 /* Tune track chunk duration */
165 if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
170 /* VOL from FFmpeg or XviD */
171 if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
172 job->config.mpeg4.bytes, job->config.mpeg4.length )))
174 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
180 // COLR atom for color and gamma correction.
182 // http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
183 // http://forum.doom9.org/showthread.php?t=133982#post1090068
184 // the user can set it from job->color_matrix, otherwise by default
185 // we say anything that's likely to be HD content is ITU BT.709 and
186 // DVD, SD TV & other content is ITU BT.601. We look at the title height
187 // rather than the job height here to get uncropped input dimensions.
188 if( job->color_matrix == 1 )
190 // ITU BT.601 DVD or SD TV content
191 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
193 else if( job->color_matrix == 2 )
195 // ITU BT.709 HD content
196 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
198 else if ( job->title->width >= 1280 || job->title->height >= 720 )
200 // we guess that 720p or above is ITU BT.709 HD content
201 MP4AddColr(m->file, mux_data->track, 1, 1, 1);
205 // ITU BT.601 DVD or SD TV content
206 MP4AddColr(m->file, mux_data->track, 6, 1, 6);
209 if( job->anamorphic.mode )
211 /* PASP atom for anamorphic video */
214 width = job->anamorphic.par_width;
216 height = job->anamorphic.par_height;
218 MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
220 MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
223 /* add the audio tracks */
224 for( i = 0; i < hb_list_count( title->list_audio ); i++ )
226 audio = hb_list_item( title->list_audio, i );
227 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
228 audio->priv.mux_data = mux_data;
230 if( audio->config.out.codec == HB_ACODEC_AC3 )
233 uint8_t bsid = audio->config.in.version;
234 uint8_t bsmod = audio->config.in.mode;
235 uint8_t acmod = audio->config.flags.ac3 & 0x7;
236 uint8_t lfeon = (audio->config.flags.ac3 & A52_LFE) ? 1 : 0;
237 uint8_t bit_rate_code = 0;
240 * Rewrite AC3 information into correct format for dac3 atom
242 switch( audio->config.in.samplerate )
255 * Error value, tells decoder to not decode this audio.
261 switch( audio->config.in.bitrate )
321 hb_error("Unknown AC3 bitrate");
326 mux_data->track = MP4AddAC3AudioTrack(
328 audio->config.out.samplerate,
336 /* Tune track chunk duration */
337 MP4TuneTrackDurationPerChunk( m, mux_data->track );
339 if (audio->config.out.name == NULL) {
340 MP4SetTrackBytesProperty(
341 m->file, mux_data->track,
343 (const uint8_t*)"Surround", strlen("Surround"));
346 MP4SetTrackBytesProperty(
347 m->file, mux_data->track,
349 (const uint8_t*)(audio->config.out.name),
350 strlen(audio->config.out.name));
353 mux_data->track = MP4AddAudioTrack(
355 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
357 /* Tune track chunk duration */
358 MP4TuneTrackDurationPerChunk( m, mux_data->track );
360 if (audio->config.out.name == NULL) {
361 MP4SetTrackBytesProperty(
362 m->file, mux_data->track,
364 (const uint8_t*)"Stereo", strlen("Stereo"));
367 MP4SetTrackBytesProperty(
368 m->file, mux_data->track,
370 (const uint8_t*)(audio->config.out.name),
371 strlen(audio->config.out.name));
374 MP4SetAudioProfileLevel( m->file, 0x0F );
375 MP4SetTrackESConfiguration(
376 m->file, mux_data->track,
377 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
379 /* Set the correct number of channels for this track */
380 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
383 /* Set the language for this track */
384 MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
386 if( hb_list_count( title->list_audio ) > 1 )
388 /* Set the audio track alternate group */
389 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
393 /* Enable the first audio track */
394 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
397 /* Disable the other audio tracks so QuickTime doesn't play
400 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
401 hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
406 for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
408 hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
410 if( subtitle && subtitle->format == TEXTSUB &&
411 subtitle->config.dest == PASSTHRUSUB )
413 uint64_t width, height = 60;
414 if( job->anamorphic.mode )
415 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
419 mux_data = calloc(1, sizeof( hb_mux_data_t ) );
420 subtitle->mux_data = mux_data;
421 mux_data->subtitle = 1;
422 mux_data->sub_format = subtitle->format;
423 mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
425 MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
427 /* Tune track chunk duration */
428 MP4TuneTrackDurationPerChunk( m, mux_data->track );
430 const uint8_t textColor[4] = { 255,255,255,255 };
432 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
434 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
435 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
436 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 0);
438 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
440 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
441 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
443 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
444 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
446 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
447 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
448 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
449 MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
451 /* translate the track */
454 uint32_t *ptr32 = (uint32_t*) nval;
457 MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
458 memcpy(nval, val, size);
460 const uint32_t ytranslation = (job->height - height) * 0x10000;
462 #ifdef WORDS_BIGENDIAN
463 ptr32[7] = ytranslation;
465 /* we need to switch the endianness, as the file format expects big endian */
466 ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) +
467 ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
470 MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);
474 if (job->chapter_markers)
476 /* add a text track for the chapters. We add the 'chap' atom to track
477 one which is usually the video track & should never be disabled.
478 The Quicktime spec says it doesn't matter which media track the
479 chap atom is on but it has to be an enabled track. */
480 MP4TrackId textTrack;
481 textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
483 m->chapter_track = textTrack;
484 m->chapter_duration = 0;
485 m->current_chapter = job->chapter_start;
488 /* Add encoded-by metadata listing version and build date */
490 tool_string = (char *)malloc(80);
491 snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
493 /* allocate,fetch,populate,store,free tags structure */
495 tags = MP4TagsAlloc();
496 MP4TagsFetch( tags, m->file );
497 MP4TagsSetEncodingTool( tags, tool_string );
498 MP4TagsStore( tags, m->file );
506 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
509 hb_job_t * job = m->job;
513 if( mux_data == job->mux_data )
517 // if there are b-frames compute the render offset
518 // (we'll need it for both the video frame & the chapter track)
521 offset = buf->start + m->init_delay - m->sum_dur;
524 hb_log("MP4Mux: illegal render offset %lld, start %lld,"
525 "stop %lld, sum_dur %lld",
526 offset, buf->start, buf->stop, m->sum_dur );
531 /* Add the sample before the new frame.
532 It is important that this be calculated prior to the duration
533 of the new video sample, as we want to sync to right after it.
534 (This is because of how durations for text tracks work in QT) */
535 if( job->chapter_markers && buf->new_chap )
537 hb_chapter_t *chapter = NULL;
539 // this chapter is postioned by writing out the previous chapter.
540 // the duration of the previous chapter is the duration up to but
541 // not including the current frame minus the duration of all
542 // chapters up to the previous.
543 // The initial and final chapters can be very short (a second or
544 // less) since they're not really chapters but just a placeholder to
545 // insert a cell command. We don't write chapters shorter than 1.5 sec.
546 duration = m->sum_dur - m->chapter_duration + offset;
547 if ( duration >= (90000*3)/2 )
549 chapter = hb_list_item( m->job->title->list_chapter,
552 MP4AddChapter( m->file,
555 (chapter != NULL) ? chapter->title : NULL);
557 m->current_chapter = buf->new_chap;
558 m->chapter_duration += duration;
562 // We're getting the frames in decode order but the timestamps are
563 // for presentation so we have to use durations and effectively
565 duration = buf->stop - buf->start;
568 /* We got an illegal mp4/h264 duration. This shouldn't
569 be possible and usually indicates a bug in the upstream code.
570 Complain in the hope that someone will go find the bug but
571 try to fix the error so that the file will still be playable. */
572 hb_log("MP4Mux: illegal duration %lld, start %lld,"
573 "stop %lld, sum_dur %lld",
574 duration, buf->start, buf->stop, m->sum_dur );
575 /* we don't know when the next frame starts so we can't pick a
576 valid duration for this one. we pick something "short"
577 (roughly 1/3 of an NTSC frame time) to take time from
581 m->sum_dur += duration;
586 duration = MP4_INVALID_DURATION;
589 /* Here's where the sample actually gets muxed. */
590 if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
592 /* Compute dependency flags.
594 * This mechanism is (optionally) used by media players such as QuickTime
595 * to offer better scrubbing performance. The most influential bits are
596 * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
598 * Other bits are possible but no example media using such bits have been
601 * It is acceptable to supply 0-bits for any samples which characteristics
602 * cannot be positively guaranteed.
607 /* encoding layer signals if frame is referenced by other frames */
608 if( buf->flags & HB_FRAME_REF )
609 dflags |= MP4_SDT_HAS_DEPENDENTS;
611 dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
613 switch( buf->frametype )
619 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
622 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
627 break; /* nothing to mark */
630 if( !MP4WriteSampleDependency( m->file,
639 hb_error("Failed to write to output file, disk full?");
643 else if (mux_data->subtitle)
645 if( mux_data->sub_format == TEXTSUB )
647 /* Write an empty sample */
648 if ( mux_data->sum_dur < buf->start )
650 uint8_t empty[2] = {0,0};
651 if( !MP4WriteSample( m->file,
655 buf->start - mux_data->sum_dur,
659 hb_error("Failed to write to output file, disk full?");
662 mux_data->sum_dur += buf->start - mux_data->sum_dur;
665 /* Write the subtitle sample */
666 uint8_t buffer[2048];
667 memcpy( buffer + 2, buf->data, buf->size );
668 buffer[0] = ( buf->size >> 8 ) & 0xff;
669 buffer[1] = buf->size & 0xff;
671 if( !MP4WriteSample( m->file,
675 buf->stop - buf->start,
679 hb_error("Failed to write to output file, disk full?");
683 mux_data->sum_dur += (buf->stop - buf->start);
684 hb_deep_log(3, "MuxMP4:Sub:%fs:%lld:%lld:%lld: %s", (float)buf->start / 90000, buf->start, buf->stop,
685 (buf->stop - buf->start), buf->data);
686 hb_deep_log(3, "MuxMP4:Total time elapsed:%lld", mux_data->sum_dur);
694 if( !MP4WriteSample( m->file,
700 ( buf->frametype & HB_FRAME_KEY ) != 0 ))
702 hb_error("Failed to write to output file, disk full?");
711 static int MP4End( hb_mux_object_t * m )
713 hb_job_t * job = m->job;
714 hb_title_t * title = job->title;
716 /* Write our final chapter marker */
717 if( m->job->chapter_markers )
719 hb_chapter_t *chapter = NULL;
720 int64_t duration = m->sum_dur - m->chapter_duration;
721 /* The final chapter can have a very short duration - if it's less
722 * than 1.5 seconds just skip it. */
723 if ( duration >= (90000*3)/2 )
726 chapter = hb_list_item( m->job->title->list_chapter,
727 m->current_chapter - 1 );
729 MP4AddChapter( m->file,
732 (chapter != NULL) ? chapter->title : NULL);
738 // Insert track edit to get A/V back in sync. The edit amount is
740 int64_t edit_amt = m->init_delay;
741 MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
742 MP4GetTrackDuration(m->file, 1), 0);
743 if ( m->job->chapter_markers )
745 // apply same edit to chapter track to keep it in sync with video
746 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
748 MP4GetTrackDuration(m->file, m->chapter_track), 0);
753 * Write the MP4 iTunes metadata if we have any metadata
755 if( title->metadata )
757 hb_metadata_t *md = title->metadata;
760 hb_deep_log( 2, "Writing Metadata to output file...");
762 /* allocate tags structure */
763 tags = MP4TagsAlloc();
764 /* fetch data from MP4 file (in case it already has some data) */
765 MP4TagsFetch( tags, m->file );
768 if( strlen( md->name ))
769 MP4TagsSetName( tags, md->name );
770 if( strlen( md->artist ))
771 MP4TagsSetArtist( tags, md->artist );
772 if( strlen( md->composer ))
773 MP4TagsSetComposer( tags, md->composer );
774 if( strlen( md->comment ))
775 MP4TagsSetComments( tags, md->comment );
776 if( strlen( md->release_date ))
777 MP4TagsSetReleaseDate( tags, md->release_date );
778 if( strlen( md->album ))
779 MP4TagsSetAlbum( tags, md->album );
780 if( strlen( md->genre ))
781 MP4TagsSetGenre( tags, md->genre );
786 art.data = md->coverart;
787 art.size = md->coverart_size;
788 art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
789 MP4TagsAddArtwork( tags, &art );
792 /* push data to MP4 file */
793 MP4TagsStore( tags, m->file );
794 /* free memory associated with structure */
800 if ( job->mp4_optimize )
802 hb_log( "muxmp4: optimizing file" );
803 char filename[1024]; memset( filename, 0, 1024 );
804 snprintf( filename, 1024, "%s.tmp", job->file );
805 MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
807 rename( filename, job->file );
813 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
815 hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );