hb_mux_data_t * mux_data;
int i;
u_int16_t language_code;
+
+ /* Flags for enabling/disabling tracks in an MP4. */
+ typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags;
+
/* Create an empty mp4 file */
- m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
+ if (job->largeFileSize)
+ /* Use 64-bit MP4 file */
+ {
+ m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
+ hb_log("Using 64-bit MP4 formatting.");
+ }
+ else
+ /* Limit MP4s to less than 4 GB */
+ {
+ m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
+ }
+
if (m->file == MP4_INVALID_FILE_HANDLE)
{
- hb_log("muxmp4.c: MP4Create failed!");
+ hb_error("muxmp4.c: MP4Create failed!");
*job->die = 1;
return 0;
}
timescale */
if (!(MP4SetTimeScale( m->file, job->arate )))
{
- hb_log("muxmp4.c: MP4SetTimeScale failed!");
+ hb_error("muxmp4.c: MP4SetTimeScale failed!");
*job->die = 1;
return 0;
}
/* Stolen from mp4creator */
if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
{
- hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
+ hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
*job->die = 1;
return 0;
}
{
if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
{
- hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
+ hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
*job->die = 1;
return 0;
}
MP4_MPEG4_VIDEO_TYPE );
if (mux_data->track == MP4_INVALID_TRACK_ID)
{
- hb_log("muxmp4.c: MP4AddVideoTrack failed!");
+ hb_error("muxmp4.c: MP4AddVideoTrack failed!");
*job->die = 1;
return 0;
}
if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
job->config.mpeg4.bytes, job->config.mpeg4.length )))
{
- hb_log("muxmp4.c: MP4SetTrackESConfiguration failed!");
+ hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
*job->die = 1;
return 0;
}
/* Set the correct number of channels for this track */
reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
-
- /* store a reference to the first audio track,
- so we can use it to feed the chapter text track's sample rate */
- if (i == 0) {
- firstAudioTrack = mux_data->track;
- }
+
+ /* store a reference to the first audio track,
+ so we can use it to feed the chapter text track's sample rate */
+ if (i == 0) {
+ firstAudioTrack = mux_data->track;
+
+ /* Enable the first audio track */
+ MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
+ }
+
+ else
+ /* Disable the other audio tracks so QuickTime doesn't play
+ them all at once. */
+ {
+ MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
+ hb_log("Disabled extra audio track %i", mux_data->track-1);
+ }
}
if( job->chapter_markers && buf->new_chap )
{
struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
-
- MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
+
+ if( !MP4WriteSample(m->file,
+ m->chapter_track,
+ sample->sample,
+ sample->length,
+ sample->duration,
+ 0, true) )
+ {
+ hb_error("Failed to write to output file, disk full?");
+ *job->die = 1;
+ }
free(sample);
m->current_chapter++;
m->chapter_duration = m->sum_dur;
initial delay added to the frame order offset for b-frames.
Because of b-pyramid, double this duration when there are
b-pyramids, as denoted by job->areBframes equalling 2. */
- if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1) && (job->vcodec == HB_VCODEC_X264))
+ if ((mux_data->track == 1) && (thisSample == 0) && (buf->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
{
initDelay = buf->renderOffset;
thisSample++;
If there are b-frames, offset by the initDelay plus the
difference between the presentation time stamp x264 gives
and the decoding time stamp from the buffer data. */
- MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
- duration, ((mux_data->track != 1) || (job->areBframes==0) || (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
- (buf->key == 1) );
+ if( !MP4WriteSample( m->file,
+ mux_data->track,
+ buf->data,
+ buf->size,
+ duration,
+ ((mux_data->track != 1) ||
+ (job->areBframes==0) ||
+ (job->vcodec != HB_VCODEC_X264)) ? 0 : ( buf->renderOffset * job->arate / 90000),
+ ((buf->frametype & HB_FRAME_KEY) != 0) ) )
+ {
+ hb_error("Failed to write to output file, disk full?");
+ *job->die = 1;
+ }
return 0;
}
static int MP4End( hb_mux_object_t * m )
-{
+{
+ hb_job_t * job = m->job;
+
/* Write our final chapter marker */
if( m->job->chapter_markers )
{
struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
- MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
+ if( !MP4WriteSample(m->file,
+ m->chapter_track,
+ sample->sample,
+ sample->length,
+ sample->duration,
+ 0, true) )
+ {
+ hb_error("Failed to write to output file, disk full?");
+ *job->die = 1;
+ }
free(sample);
}
char filename[1024]; memset( filename, 0, 1024 );
#endif
- hb_job_t * job = m->job;
-
if (job->areBframes)
/* Walk the entire video sample table and find the minumum ctts value. */
{