OSDN Git Service

f6c462298cffbd72f8b8c55e6680a2dbed1b40bb
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
1 /* $Id: muxmp4.c,v 1.24 2005/11/04 13:09:41 titer Exp $
2
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. */
6
7 #include "mp4v2/mp4v2.h"
8 #include "a52dec/a52.h"
9
10 #include "hb.h"
11
12 struct hb_mux_object_s
13 {
14     HB_MUX_COMMON;
15
16     hb_job_t * job;
17
18     /* libmp4v2 handle */
19     MP4FileHandle file;
20
21     int64_t sum_dur;    // sum of video frame durations so far
22
23     // bias to keep render offsets in ctts atom positive (set up by encx264)
24     int64_t init_delay;
25
26     uint64_t sum_sub_duration; // sum of subtitle frame durations so far
27
28     /* Chapter state information for muxing */
29     MP4TrackId chapter_track;
30     int current_chapter;
31     uint64_t chapter_duration;
32 };
33
34 struct hb_mux_data_s
35 {
36     MP4TrackId track;
37     uint8_t    subtitle;
38     int        sub_format;
39 };
40
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.
45  *
46  * return true on fail, false on success.
47  */
48 static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId )
49 {
50     uint32_t tscale;
51     MP4Duration dur;
52
53     tscale = MP4GetTrackTimeScale( m->file, trackId );
54     dur = (MP4Duration)ceil( (double)tscale * (double)m->job->vrate_base / (double)m->job->vrate * 4.0 );
55
56     if( !MP4SetTrackDurationPerChunk( m->file, trackId, dur ))
57     {
58         hb_error( "muxmp4.c: MP4SetTrackDurationPerChunk failed!" );
59         *m->job->die = 1;
60         return 0;
61     }
62
63     hb_deep_log( 2, "muxmp4: track %u, chunk duration %llu", MP4FindTrackIndex( m->file, trackId ), dur );
64     return 1;
65 }
66
67 /**********************************************************************
68  * MP4Init
69  **********************************************************************
70  * Allocates hb_mux_data_t structures, create file and write headers
71  *********************************************************************/
72 static int MP4Init( hb_mux_object_t * m )
73 {
74     hb_job_t   * job   = m->job;
75     hb_title_t * title = job->title;
76
77     hb_audio_t    * audio;
78     hb_mux_data_t * mux_data;
79     int i;
80
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;
83
84     /* Create an empty mp4 file */
85     if (job->largeFileSize)
86     /* Use 64-bit MP4 file */
87     {
88         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
89         hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
90     }
91     else
92     /* Limit MP4s to less than 4 GB */
93     {
94         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
95     }
96
97     if (m->file == MP4_INVALID_FILE_HANDLE)
98     {
99         hb_error("muxmp4.c: MP4Create failed!");
100         *job->die = 1;
101         return 0;
102     }
103
104     /* Video track */
105     mux_data      = calloc(1, sizeof( hb_mux_data_t ) );
106     job->mux_data = mux_data;
107
108     if (!(MP4SetTimeScale( m->file, 90000 )))
109     {
110         hb_error("muxmp4.c: MP4SetTimeScale failed!");
111         *job->die = 1;
112         return 0;
113     }
114
115     if( job->vcodec == HB_VCODEC_X264 )
116     {
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 )
126         {
127             hb_error( "muxmp4.c: MP4AddH264VideoTrack failed!" );
128             *job->die = 1;
129             return 0;
130         }
131
132         /* Tune track chunk duration */
133         if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
134         {
135             return 0;
136         }
137
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 );
142
143                 if( job->h264_level == 30 || job->ipod_atom)
144                 {
145                         hb_deep_log( 2, "muxmp4: adding iPod atom");
146                         MP4AddIPodUUID(m->file, mux_data->track);
147                 }
148
149         m->init_delay = job->config.h264.init_delay;
150     }
151     else /* FFmpeg or XviD */
152     {
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)
158         {
159             hb_error("muxmp4.c: MP4AddVideoTrack failed!");
160             *job->die = 1;
161             return 0;
162         }
163
164         /* Tune track chunk duration */
165         if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
166         {
167             return 0;
168         }
169
170         /* VOL from FFmpeg or XviD */
171         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
172                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
173         {
174             hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
175             *job->die = 1;
176             return 0;
177         }
178     }
179
180     // COLR atom for color and gamma correction.
181     // Per the notes at:
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 )
189     {
190         // ITU BT.601 DVD or SD TV content
191         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
192     }
193     else if( job->color_matrix == 2 )
194     {
195         // ITU BT.709 HD content
196         MP4AddColr(m->file, mux_data->track, 1, 1, 1);        
197     }
198     else if ( job->title->width >= 1280 || job->title->height >= 720 )
199     {
200         // we guess that 720p or above is ITU BT.709 HD content
201         MP4AddColr(m->file, mux_data->track, 1, 1, 1);
202     }
203     else
204     {
205         // ITU BT.601 DVD or SD TV content
206         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
207     }
208
209     if( job->anamorphic.mode )
210     {
211         /* PASP atom for anamorphic video */
212         float width, height;
213
214         width  = job->anamorphic.par_width;
215
216         height = job->anamorphic.par_height;
217
218         MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
219
220         MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
221     }
222
223         /* add the audio tracks */
224     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
225     {
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;
229
230         if( audio->config.out.codec == HB_ACODEC_AC3 )
231         {
232             uint8_t fscod = 0;
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;
238
239             /*
240              * Rewrite AC3 information into correct format for dac3 atom
241              */
242             switch( audio->config.in.samplerate )
243             {
244             case 48000:
245                 fscod = 0;
246                 break;
247             case 44100:
248                 fscod = 1;
249                 break;
250             case 32000:
251                 fscod = 2;
252                 break;
253             default:
254                 /*
255                  * Error value, tells decoder to not decode this audio.
256                  */
257                 fscod = 3;
258                 break;
259             }
260
261             switch( audio->config.in.bitrate )
262             {
263             case 32000:
264                 bit_rate_code = 0;
265                 break;
266             case 40000:
267                 bit_rate_code = 1;
268                 break;
269             case 48000:
270                 bit_rate_code = 2;
271                 break;
272             case 56000:
273                 bit_rate_code = 3;
274                 break;
275             case 64000:
276                 bit_rate_code = 4;
277                 break;
278             case 80000:
279                 bit_rate_code = 5;
280                 break;
281             case 96000:
282                 bit_rate_code = 6;
283                 break;
284             case 112000:
285                 bit_rate_code = 7;
286                 break;
287             case 128000:
288                 bit_rate_code = 8;
289                 break;
290             case 160000:
291                 bit_rate_code = 9;
292                 break;
293             case 192000:
294                 bit_rate_code = 10;
295                 break;
296             case 224000:
297                 bit_rate_code = 11;
298                 break;
299             case 256000:
300                 bit_rate_code = 12;
301                 break;
302             case 320000:
303                 bit_rate_code = 13;
304                 break;
305             case 384000:
306                 bit_rate_code = 14;
307                 break;
308             case 448000:
309                 bit_rate_code = 15;
310                 break;
311             case 512000:
312                 bit_rate_code = 16;
313                 break;
314             case 576000:
315                 bit_rate_code = 17;
316                 break;
317             case 640000:
318                 bit_rate_code = 18;
319                 break;
320             default:
321                 hb_error("Unknown AC3 bitrate");
322                 bit_rate_code = 0;
323                 break;
324             }
325
326             mux_data->track = MP4AddAC3AudioTrack(
327                 m->file,
328                 audio->config.out.samplerate, 
329                 fscod,
330                 bsid,
331                 bsmod,
332                 acmod,
333                 lfeon,
334                 bit_rate_code);
335
336             /* Tune track chunk duration */
337             MP4TuneTrackDurationPerChunk( m, mux_data->track );
338
339             if (audio->config.out.name == NULL) {
340                 MP4SetTrackBytesProperty(
341                     m->file, mux_data->track,
342                     "udta.name.value",
343                     (const uint8_t*)"Surround", strlen("Surround"));
344             }
345             else {
346                 MP4SetTrackBytesProperty(
347                     m->file, mux_data->track,
348                     "udta.name.value",
349                     (const uint8_t*)(audio->config.out.name),
350                     strlen(audio->config.out.name));
351             }
352         } else {
353             mux_data->track = MP4AddAudioTrack(
354                 m->file,
355                 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
356
357             /* Tune track chunk duration */
358             MP4TuneTrackDurationPerChunk( m, mux_data->track );
359
360             if (audio->config.out.name == NULL) {
361                 MP4SetTrackBytesProperty(
362                     m->file, mux_data->track,
363                     "udta.name.value",
364                     (const uint8_t*)"Stereo", strlen("Stereo"));
365             }
366             else {
367                 MP4SetTrackBytesProperty(
368                     m->file, mux_data->track,
369                     "udta.name.value",
370                     (const uint8_t*)(audio->config.out.name),
371                     strlen(audio->config.out.name));
372             }
373
374             MP4SetAudioProfileLevel( m->file, 0x0F );
375             MP4SetTrackESConfiguration(
376                 m->file, mux_data->track,
377                 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
378
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));
381         }
382
383         /* Set the language for this track */
384         MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
385
386         if( hb_list_count( title->list_audio ) > 1 )
387         {
388             /* Set the audio track alternate group */
389             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
390         }
391
392         if (i == 0) {
393             /* Enable the first audio track */
394             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
395         }
396         else
397             /* Disable the other audio tracks so QuickTime doesn't play
398                them all at once. */
399         {
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 ));
402         }
403
404     }
405
406     for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
407     {
408         hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
409
410         if( subtitle && subtitle->format == TEXTSUB && 
411             subtitle->dest == PASSTHRUSUB )
412         {
413             mux_data = calloc(1, sizeof( hb_mux_data_t ) );
414             subtitle->mux_data = mux_data;
415             mux_data->subtitle = 1;
416             mux_data->sub_format = subtitle->format;
417             mux_data->track = MP4AddSubtitleTrack( m->file, 1 );
418
419             MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
420
421             /* Tune track chunk duration */
422             MP4TuneTrackDurationPerChunk( m, mux_data->track );
423
424             const uint8_t textColor[4] = { 255,255,255,255 };
425             uint64_t subHeight = 60;
426
427             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
428
429             MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width);
430             MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.height", subHeight);
431
432             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
433             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
434             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 0);
435
436             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
437
438             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", subHeight);
439             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", job->width);
440
441             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
442             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
443
444             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
445             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
446             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
447             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
448             
449             /* translate the track */
450             uint8_t* val;
451             uint8_t nval[36];
452             uint32_t *ptr32 = (uint32_t*) nval;
453             uint32_t size;
454
455             MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
456             memcpy(nval, val, size);
457
458             const uint32_t ytranslation = (job->height - subHeight) * 0x10000;
459                 
460 #ifdef WORDS_BIGENDIAN
461             ptr32[7] = ytranslation;
462 #else
463             /* we need to switch the endianness, as the file format expects big endian */
464             ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) + 
465                             ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
466 #endif
467
468             MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);  
469         }
470     }
471
472     if (job->chapter_markers)
473     {
474         /* add a text track for the chapters. We add the 'chap' atom to track
475            one which is usually the video track & should never be disabled.
476            The Quicktime spec says it doesn't matter which media track the
477            chap atom is on but it has to be an enabled track. */
478         MP4TrackId textTrack;
479         textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
480
481         m->chapter_track = textTrack;
482         m->chapter_duration = 0;
483         m->current_chapter = job->chapter_start;
484     }
485
486     /* Add encoded-by metadata listing version and build date */
487     char *tool_string;
488     tool_string = (char *)malloc(80);
489     snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
490
491     /* allocate,fetch,populate,store,free tags structure */
492     const MP4Tags* tags;
493     tags = MP4TagsAlloc();
494     MP4TagsFetch( tags, m->file );
495     MP4TagsSetEncodingTool( tags, tool_string );
496     MP4TagsStore( tags, m->file );
497     MP4TagsFree( tags );
498
499     free(tool_string);
500
501     return 0;
502 }
503
504 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
505                    hb_buffer_t * buf )
506 {
507     hb_job_t * job = m->job;
508     int64_t duration;
509     int64_t offset = 0;
510
511     if( mux_data == job->mux_data )
512     {
513         /* Video */
514
515         // if there are b-frames compute the render offset
516         // (we'll need it for both the video frame & the chapter track)
517         if ( m->init_delay )
518         {
519             offset = buf->start + m->init_delay - m->sum_dur;
520             if ( offset < 0 )
521             {
522                 hb_log("MP4Mux: illegal render offset %lld, start %lld,"
523                        "stop %lld, sum_dur %lld",
524                        offset, buf->start, buf->stop, m->sum_dur );
525                 offset = 0;
526             }
527         }
528
529         /* Add the sample before the new frame.
530            It is important that this be calculated prior to the duration
531            of the new video sample, as we want to sync to right after it.
532            (This is because of how durations for text tracks work in QT) */
533         if( job->chapter_markers && buf->new_chap )
534         {    
535             hb_chapter_t *chapter = NULL;
536
537             // this chapter is postioned by writing out the previous chapter.
538             // the duration of the previous chapter is the duration up to but
539             // not including the current frame minus the duration of all
540             // chapters up to the previous.
541             // The initial and final chapters can be very short (a second or
542             // less) since they're not really chapters but just a placeholder to
543             // insert a cell command. We don't write chapters shorter than 1.5 sec.
544             duration = m->sum_dur - m->chapter_duration + offset;
545             if ( duration >= (90000*3)/2 )
546             {
547                 chapter = hb_list_item( m->job->title->list_chapter,
548                                         buf->new_chap - 2 );
549
550                 MP4AddChapter( m->file,
551                                m->chapter_track,
552                                duration,
553                                (chapter != NULL) ? chapter->title : NULL);
554
555                 m->current_chapter = buf->new_chap;
556                 m->chapter_duration += duration;
557             }
558         }
559
560         // We're getting the frames in decode order but the timestamps are
561         // for presentation so we have to use durations and effectively
562         // compute a DTS.
563         duration = buf->stop - buf->start;
564         if ( duration <= 0 )
565         {
566             /* We got an illegal mp4/h264 duration. This shouldn't
567                be possible and usually indicates a bug in the upstream code.
568                Complain in the hope that someone will go find the bug but
569                try to fix the error so that the file will still be playable. */
570             hb_log("MP4Mux: illegal duration %lld, start %lld,"
571                    "stop %lld, sum_dur %lld",
572                    duration, buf->start, buf->stop, m->sum_dur );
573             /* we don't know when the next frame starts so we can't pick a
574                valid duration for this one. we pick something "short"
575                (roughly 1/3 of an NTSC frame time) to take time from
576                the next frame. */
577             duration = 1000;
578         }
579         m->sum_dur += duration;
580     }
581     else
582     {
583         /* Audio */
584         duration = MP4_INVALID_DURATION;
585     }
586
587     /* Here's where the sample actually gets muxed. */
588     if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
589     {
590         /* Compute dependency flags.
591          *
592          * This mechanism is (optionally) used by media players such as QuickTime
593          * to offer better scrubbing performance. The most influential bits are
594          * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
595          *
596          * Other bits are possible but no example media using such bits have been
597          * found.
598          *
599          * It is acceptable to supply 0-bits for any samples which characteristics
600          * cannot be positively guaranteed.
601          */
602         int sync = 0;
603         uint32_t dflags = 0;
604
605         /* encoding layer signals if frame is referenced by other frames */
606         if( buf->flags & HB_FRAME_REF )
607             dflags |= MP4_SDT_HAS_DEPENDENTS;
608         else
609             dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
610
611         switch( buf->frametype )
612         {
613             case HB_FRAME_IDR:
614                 sync = 1;
615                 break;
616             case HB_FRAME_I:
617                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
618                 break;
619             case HB_FRAME_P:
620                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
621                 break;
622             case HB_FRAME_BREF:
623             case HB_FRAME_B:
624             default:
625                 break; /* nothing to mark */
626         }
627
628         if( !MP4WriteSampleDependency( m->file,
629                                        mux_data->track,
630                                        buf->data,
631                                        buf->size,
632                                        duration,
633                                        offset,
634                                        sync,
635                                        dflags ))
636         {
637             hb_error("Failed to write to output file, disk full?");
638             *job->die = 1;
639         }
640     }
641     else if (mux_data->subtitle)
642     {
643         if( mux_data->sub_format == TEXTSUB )
644         {
645             /* Write an empty sample */
646             if ( m->sum_sub_duration < buf->start )
647             {
648                 uint8_t empty[2] = {0,0};
649                 if( !MP4WriteSample( m->file,
650                                     mux_data->track,
651                                     empty,
652                                     2,
653                                     buf->start - m->sum_sub_duration,
654                                     0,
655                                     1 ))
656                 {
657                     hb_error("Failed to write to output file, disk full?");
658                     *job->die = 1;
659                 } 
660                 m->sum_sub_duration += buf->start - m->sum_sub_duration;
661             }
662
663             /* Write the subtitle sample */
664             uint8_t buffer[2048];
665             memcpy( buffer + 2, buf->data, buf->size );
666             buffer[0] = ( buf->size >> 8 ) & 0xff;
667             buffer[1] = buf->size & 0xff;
668
669             if( !MP4WriteSample( m->file,
670                                  mux_data->track,
671                                  buffer,
672                                  buf->size,
673                                  buf->stop - buf->start,
674                                  0,
675                                  1 ))
676             {
677                 hb_error("Failed to write to output file, disk full?");
678                 *job->die = 1;
679             }
680
681             m->sum_sub_duration += (buf->stop - buf->start);
682             hb_deep_log(3, "MuxMP4:Sub:%fs:%lld:%lld:%lld: %s", (float)buf->start / 90000, buf->start, buf->stop, 
683                    (buf->stop - buf->start), buf->data);
684             hb_deep_log(3, "MuxMP4:Total time elapsed:%lld", m->sum_sub_duration);
685         }
686     }
687     else
688     {
689         /*
690          * Audio
691          */
692         if( !MP4WriteSample( m->file,
693                              mux_data->track,
694                              buf->data,
695                              buf->size,
696                              duration,
697                              offset,
698                              ( buf->frametype & HB_FRAME_KEY ) != 0 ))
699         {
700             hb_error("Failed to write to output file, disk full?");
701             *job->die = 1;
702         }
703     }
704
705
706     return 0;
707 }
708
709 static int MP4End( hb_mux_object_t * m )
710 {
711     hb_job_t   * job   = m->job;
712     hb_title_t * title = job->title;
713
714     /* Write our final chapter marker */
715     if( m->job->chapter_markers )
716     {
717         hb_chapter_t *chapter = NULL;
718         int64_t duration = m->sum_dur - m->chapter_duration;
719         /* The final chapter can have a very short duration - if it's less
720          * than 1.5 seconds just skip it. */
721         if ( duration >= (90000*3)/2 )
722         {
723
724             chapter = hb_list_item( m->job->title->list_chapter,
725                                     m->current_chapter - 1 );
726
727             MP4AddChapter( m->file,
728                            m->chapter_track,
729                            duration,
730                            (chapter != NULL) ? chapter->title : NULL);
731         }
732     }
733
734     if (job->areBframes)
735     {
736            // Insert track edit to get A/V back in sync.  The edit amount is
737            // the init_delay.
738            int64_t edit_amt = m->init_delay;
739            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
740                            MP4GetTrackDuration(m->file, 1), 0);
741             if ( m->job->chapter_markers )
742             {
743                 // apply same edit to chapter track to keep it in sync with video
744                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
745                                 edit_amt,
746                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
747             }
748      }
749
750     /*
751      * Write the MP4 iTunes metadata if we have any metadata
752      */
753     if( title->metadata )
754     {
755         hb_metadata_t *md = title->metadata;
756         const MP4Tags* tags;
757
758         hb_deep_log( 2, "Writing Metadata to output file...");
759
760         /* allocate tags structure */
761         tags = MP4TagsAlloc();
762         /* fetch data from MP4 file (in case it already has some data) */
763         MP4TagsFetch( tags, m->file );
764
765         /* populate */
766         MP4TagsSetName( tags, md->name );
767         MP4TagsSetArtist( tags, md->artist );
768         MP4TagsSetComposer( tags, md->composer );
769         MP4TagsSetComments( tags, md->comment );
770         MP4TagsSetReleaseDate( tags, md->release_date );
771         MP4TagsSetAlbum( tags, md->album );
772         MP4TagsSetGenre( tags, md->genre );
773
774         if( md->coverart )
775         {
776             MP4TagArtwork art;
777             art.data = md->coverart;
778             art.size = md->coverart_size;
779             art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
780             MP4TagsAddArtwork( tags, &art );
781         }
782
783         /* push data to MP4 file */
784         MP4TagsStore( tags, m->file );
785         /* free memory associated with structure */
786         MP4TagsFree( tags );
787     }
788
789     MP4Close( m->file );
790
791     if ( job->mp4_optimize )
792     {
793         hb_log( "muxmp4: optimizing file" );
794         char filename[1024]; memset( filename, 0, 1024 );
795         snprintf( filename, 1024, "%s.tmp", job->file );
796         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
797         remove( job->file );
798         rename( filename, job->file );
799     }
800
801     return 0;
802 }
803
804 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
805 {
806     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
807     m->init      = MP4Init;
808     m->mux       = MP4Mux;
809     m->end       = MP4End;
810     m->job       = job;
811     return m;
812 }
813