summaryrefslogtreecommitdiffstats
path: root/libhb/muxmp4.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhb/muxmp4.c')
-rw-r--r--libhb/muxmp4.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c
new file mode 100644
index 000000000..357551ec6
--- /dev/null
+++ b/libhb/muxmp4.c
@@ -0,0 +1,224 @@
+/* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 titer Exp $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.m0k.org/>.
+ It may be used under the terms of the GNU General Public License. */
+
+/* libmp4v2 header */
+#include "mp4.h"
+
+#include "hb.h"
+
+void AddIPodUUID(MP4FileHandle, MP4TrackId);
+
+
+struct hb_mux_object_s
+{
+ HB_MUX_COMMON;
+
+ hb_job_t * job;
+
+ /* libmp4v2 handle */
+ MP4FileHandle file;
+
+ /* Cumulated durations so far, in timescale units (see MP4Mux) */
+ uint64_t sum_dur;
+};
+
+struct hb_mux_data_s
+{
+ MP4TrackId track;
+};
+
+/**********************************************************************
+ * MP4Init
+ **********************************************************************
+ * Allocates hb_mux_data_t structures, create file and write headers
+ *********************************************************************/
+static int MP4Init( hb_mux_object_t * m )
+{
+ hb_job_t * job = m->job;
+ hb_title_t * title = job->title;
+
+ hb_audio_t * audio;
+ hb_mux_data_t * mux_data;
+ int i;
+
+ /* Create an empty mp4 file */
+ m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
+
+ /* Video track */
+ mux_data = malloc( sizeof( hb_mux_data_t ) );
+ job->mux_data = mux_data;
+
+ /* When using the standard 90000 timescale, QuickTime tends to have
+ synchronization issues (audio not playing at the correct speed).
+ To workaround this, we use the audio samplerate as the
+ timescale */
+ MP4SetTimeScale( m->file, job->arate );
+
+ if( job->vcodec == HB_VCODEC_X264 )
+ {
+ /* Stolen from mp4creator */
+ MP4SetVideoProfileLevel( m->file, 0x7F );
+
+ if (job->areBframes == 1)
+ {
+ hb_log("muxmp4: Adjusting duration for B-frames");
+ mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
+ MP4_INVALID_DURATION+1, job->width, job->height,
+ job->config.h264.sps[1], /* AVCProfileIndication */
+ job->config.h264.sps[2], /* profile_compat */
+ job->config.h264.sps[3], /* AVCLevelIndication */
+ 3 ); /* 4 bytes length before each NAL unit */
+ }
+ else
+ {
+ hb_log("muxmp4: Using default duration as there are no B-frames");
+ mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
+ MP4_INVALID_DURATION, job->width, job->height,
+ job->config.h264.sps[1], /* AVCProfileIndication */
+ job->config.h264.sps[2], /* profile_compat */
+ job->config.h264.sps[3], /* AVCLevelIndication */
+ 3 ); /* 4 bytes length before each NAL unit */
+ }
+
+ MP4AddH264SequenceParameterSet( m->file, mux_data->track,
+ job->config.h264.sps, job->config.h264.sps_length );
+ MP4AddH264PictureParameterSet( m->file, mux_data->track,
+ job->config.h264.pps, job->config.h264.pps_length );
+
+ if( job->h264_level == 30)
+ {
+ hb_log("About to add iPod atom");
+ AddIPodUUID(m->file, mux_data->track);
+ }
+
+ }
+ else /* FFmpeg or XviD */
+ {
+ MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
+ mux_data->track = MP4AddVideoTrack( m->file, job->arate,
+ MP4_INVALID_DURATION, job->width, job->height,
+ MP4_MPEG4_VIDEO_TYPE );
+
+ /* VOL from FFmpeg or XviD */
+ MP4SetTrackESConfiguration( m->file, mux_data->track,
+ job->config.mpeg4.bytes, job->config.mpeg4.length );
+ }
+
+ /* apply the anamorphic transformation matrix if needed */
+
+ if( job->pixel_ratio ) {
+
+ uint8_t* val;
+ uint8_t nval[38];
+ uint32_t *ptr32 = (uint32_t*) (nval + 2);
+ uint32_t size;
+
+ MP4GetBytesProperty(m->file, "moov.trak.tkhd.reserved3", &val, &size);
+
+ if (size == 38) {
+
+ memcpy(nval, val, size);
+
+ float width, height;
+ float widthRatio;
+ width = job->pixel_aspect_width;
+ height = job->pixel_aspect_height;
+ widthRatio = (width / height) * 0x10000;
+
+ uint32_t widthRatioInt;
+ widthRatioInt = (uint32_t)widthRatio;
+
+#ifdef WORDS_BIGENDIAN
+ ptr32[0] = widthRatioInt;
+#else
+ /* we need to switch the endianness, as the file format expects big endian */
+ ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
+#endif
+
+ if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
+ hb_log("Problem setting transform matrix");
+ }
+
+ }
+
+ }
+
+ /* end of transformation matrix */
+
+
+ for( i = 0; i < hb_list_count( title->list_audio ); i++ )
+ {
+ audio = hb_list_item( title->list_audio, i );
+ mux_data = malloc( sizeof( hb_mux_data_t ) );
+ audio->mux_data = mux_data;
+
+ mux_data->track = MP4AddAudioTrack( m->file,
+ job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
+ MP4SetAudioProfileLevel( m->file, 0x0F );
+ MP4SetTrackESConfiguration( m->file, mux_data->track,
+ audio->config.aac.bytes, audio->config.aac.length );
+ }
+
+ return 0;
+}
+
+static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
+ hb_buffer_t * buf )
+{
+ hb_job_t * job = m->job;
+
+ uint64_t duration;
+
+ if( mux_data == job->mux_data )
+ {
+ /* Video */
+ /* Because we use the audio samplerate as the timescale,
+ we have to use potentially variable durations so the video
+ doesn't go out of sync */
+ duration = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+ m->sum_dur += duration;
+ }
+ else
+ {
+ /* Audio */
+ duration = MP4_INVALID_DURATION;
+ }
+
+ MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
+ duration, 0, buf->key );
+ return 0;
+}
+
+static int MP4End( hb_mux_object_t * m )
+{
+#if 0
+ hb_job_t * job = m->job;
+#endif
+ char filename[1024]; memset( filename, 0, 1024 );
+
+ MP4Close( m->file );
+
+#if 0
+ hb_log( "muxmp4: optimizing file" );
+ snprintf( filename, 1024, "%s.tmp", job->file );
+ MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
+ remove( job->file );
+ rename( filename, job->file );
+#endif
+
+ return 0;
+}
+
+hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
+{
+ hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
+ m->init = MP4Init;
+ m->mux = MP4Mux;
+ m->end = MP4End;
+ m->job = job;
+ return m;
+}
+