OSDN Git Service

- bump to mp4v2-trunk-r349.
[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     /* Chapter state information for muxing */
27     MP4TrackId chapter_track;
28     int current_chapter;
29     uint64_t chapter_duration;
30 };
31
32 struct hb_mux_data_s
33 {
34     MP4TrackId  track;
35     uint8_t     subtitle;
36     int         sub_format;
37
38     uint64_t    sum_dur; // sum of the frame durations so far
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             uint64_t width, height = 60;
414             if( job->anamorphic.mode )
415                 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
416             else
417                 width = job->width;
418
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 );
424
425             MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
426
427             /* Tune track chunk duration */
428             MP4TuneTrackDurationPerChunk( m, mux_data->track );
429
430             const uint8_t textColor[4] = { 255,255,255,255 };
431
432             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
433
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);
437
438             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
439
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);
442
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);
445
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]);
450             
451             /* translate the track */
452             uint8_t* val;
453             uint8_t nval[36];
454             uint32_t *ptr32 = (uint32_t*) nval;
455             uint32_t size;
456
457             MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
458             memcpy(nval, val, size);
459
460             const uint32_t ytranslation = (job->height - height) * 0x10000;
461                 
462 #ifdef WORDS_BIGENDIAN
463             ptr32[7] = ytranslation;
464 #else
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);
468 #endif
469
470             MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);  
471         }
472     }
473
474     if (job->chapter_markers)
475     {
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);
482
483         m->chapter_track = textTrack;
484         m->chapter_duration = 0;
485         m->current_chapter = job->chapter_start;
486     }
487
488     /* Add encoded-by metadata listing version and build date */
489     char *tool_string;
490     tool_string = (char *)malloc(80);
491     snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
492
493     /* allocate,fetch,populate,store,free tags structure */
494     const MP4Tags* tags;
495     tags = MP4TagsAlloc();
496     MP4TagsFetch( tags, m->file );
497     MP4TagsSetEncodingTool( tags, tool_string );
498     MP4TagsStore( tags, m->file );
499     MP4TagsFree( tags );
500
501     free(tool_string);
502
503     return 0;
504 }
505
506 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
507                    hb_buffer_t * buf )
508 {
509     hb_job_t * job = m->job;
510     int64_t duration;
511     int64_t offset = 0;
512
513     if( mux_data == job->mux_data )
514     {
515         /* Video */
516
517         // if there are b-frames compute the render offset
518         // (we'll need it for both the video frame & the chapter track)
519         if ( m->init_delay )
520         {
521             offset = buf->start + m->init_delay - m->sum_dur;
522             if ( offset < 0 )
523             {
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 );
527                 offset = 0;
528             }
529         }
530
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 )
536         {    
537             hb_chapter_t *chapter = NULL;
538
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 )
548             {
549                 chapter = hb_list_item( m->job->title->list_chapter,
550                                         buf->new_chap - 2 );
551
552                 MP4AddChapter( m->file,
553                                m->chapter_track,
554                                duration,
555                                (chapter != NULL) ? chapter->title : NULL);
556
557                 m->current_chapter = buf->new_chap;
558                 m->chapter_duration += duration;
559             }
560         }
561
562         // We're getting the frames in decode order but the timestamps are
563         // for presentation so we have to use durations and effectively
564         // compute a DTS.
565         duration = buf->stop - buf->start;
566         if ( duration <= 0 )
567         {
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
578                the next frame. */
579             duration = 1000;
580         }
581         m->sum_dur += duration;
582     }
583     else
584     {
585         /* Audio */
586         duration = MP4_INVALID_DURATION;
587     }
588
589     /* Here's where the sample actually gets muxed. */
590     if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
591     {
592         /* Compute dependency flags.
593          *
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.
597          *
598          * Other bits are possible but no example media using such bits have been
599          * found.
600          *
601          * It is acceptable to supply 0-bits for any samples which characteristics
602          * cannot be positively guaranteed.
603          */
604         int sync = 0;
605         uint32_t dflags = 0;
606
607         /* encoding layer signals if frame is referenced by other frames */
608         if( buf->flags & HB_FRAME_REF )
609             dflags |= MP4_SDT_HAS_DEPENDENTS;
610         else
611             dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
612
613         switch( buf->frametype )
614         {
615             case HB_FRAME_IDR:
616                 sync = 1;
617                 break;
618             case HB_FRAME_I:
619                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
620                 break;
621             case HB_FRAME_P:
622                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
623                 break;
624             case HB_FRAME_BREF:
625             case HB_FRAME_B:
626             default:
627                 break; /* nothing to mark */
628         }
629
630         if( !MP4WriteSampleDependency( m->file,
631                                        mux_data->track,
632                                        buf->data,
633                                        buf->size,
634                                        duration,
635                                        offset,
636                                        sync,
637                                        dflags ))
638         {
639             hb_error("Failed to write to output file, disk full?");
640             *job->die = 1;
641         }
642     }
643     else if (mux_data->subtitle)
644     {
645         if( mux_data->sub_format == TEXTSUB )
646         {
647             /* Write an empty sample */
648             if ( mux_data->sum_dur < buf->start )
649             {
650                 uint8_t empty[2] = {0,0};
651                 if( !MP4WriteSample( m->file,
652                                     mux_data->track,
653                                     empty,
654                                     2,
655                                     buf->start - mux_data->sum_dur,
656                                     0,
657                                     1 ))
658                 {
659                     hb_error("Failed to write to output file, disk full?");
660                     *job->die = 1;
661                 } 
662                 mux_data->sum_dur += buf->start - mux_data->sum_dur;
663             }
664
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;
670
671             if( !MP4WriteSample( m->file,
672                                  mux_data->track,
673                                  buffer,
674                                  buf->size + 2,
675                                  buf->stop - buf->start,
676                                  0,
677                                  1 ))
678             {
679                 hb_error("Failed to write to output file, disk full?");
680                 *job->die = 1;
681             }
682
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);
687         }
688     }
689     else
690     {
691         /*
692          * Audio
693          */
694         if( !MP4WriteSample( m->file,
695                              mux_data->track,
696                              buf->data,
697                              buf->size,
698                              duration,
699                              offset,
700                              ( buf->frametype & HB_FRAME_KEY ) != 0 ))
701         {
702             hb_error("Failed to write to output file, disk full?");
703             *job->die = 1;
704         }
705     }
706
707
708     return 0;
709 }
710
711 static int MP4End( hb_mux_object_t * m )
712 {
713     hb_job_t   * job   = m->job;
714     hb_title_t * title = job->title;
715
716     /* Write our final chapter marker */
717     if( m->job->chapter_markers )
718     {
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 )
724         {
725
726             chapter = hb_list_item( m->job->title->list_chapter,
727                                     m->current_chapter - 1 );
728
729             MP4AddChapter( m->file,
730                            m->chapter_track,
731                            duration,
732                            (chapter != NULL) ? chapter->title : NULL);
733         }
734     }
735
736     if (job->areBframes)
737     {
738            // Insert track edit to get A/V back in sync.  The edit amount is
739            // the init_delay.
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 )
744             {
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,
747                                 edit_amt,
748                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
749             }
750      }
751
752     /*
753      * Write the MP4 iTunes metadata if we have any metadata
754      */
755     if( title->metadata )
756     {
757         hb_metadata_t *md = title->metadata;
758         const MP4Tags* tags;
759
760         hb_deep_log( 2, "Writing Metadata to output file...");
761
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 );
766
767         /* populate */
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 );
782
783         if( md->coverart )
784         {
785             MP4TagArtwork art;
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 );
790         }
791
792         /* push data to MP4 file */
793         MP4TagsStore( tags, m->file );
794         /* free memory associated with structure */
795         MP4TagsFree( tags );
796     }
797
798     MP4Close( m->file );
799
800     if ( job->mp4_optimize )
801     {
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 );
806         remove( job->file );
807         rename( filename, job->file );
808     }
809
810     return 0;
811 }
812
813 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
814 {
815     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
816     m->init      = MP4Init;
817     m->mux       = MP4Mux;
818     m->end       = MP4End;
819     m->job       = job;
820     return m;
821 }
822