OSDN Git Service

b50795d8ce8c8df5a87770e5b991e10b880bc919
[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
39 /* Tune video track chunk duration.
40  * libmp4v2 default duration == dusamplerate == 1 second.
41  * Per van's suggestion we desire duration == 4 frames.
42  * Should be invoked immediately after track creation.
43  *
44  * return true on fail, false on success.
45  */
46 static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId )
47 {
48     uint32_t tscale;
49     MP4Duration dur;
50
51     tscale = MP4GetTrackTimeScale( m->file, trackId );
52     dur = (MP4Duration)ceil( (double)tscale * (double)m->job->vrate_base / (double)m->job->vrate * 4.0 );
53
54     if( !MP4SetTrackDurationPerChunk( m->file, trackId, dur ))
55     {
56         hb_error( "muxmp4.c: MP4SetTrackDurationPerChunk failed!" );
57         *m->job->die = 1;
58         return 0;
59     }
60
61     hb_deep_log( 2, "muxmp4: track %u, chunk duration %llu", MP4FindTrackIndex( m->file, trackId ), dur );
62     return 1;
63 }
64
65 /**********************************************************************
66  * MP4Init
67  **********************************************************************
68  * Allocates hb_mux_data_t structures, create file and write headers
69  *********************************************************************/
70 static int MP4Init( hb_mux_object_t * m )
71 {
72     hb_job_t   * job   = m->job;
73     hb_title_t * title = job->title;
74
75     hb_audio_t    * audio;
76     hb_mux_data_t * mux_data;
77     int i;
78
79     /* Flags for enabling/disabling tracks in an MP4. */
80     typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8}  track_header_flags;
81
82     /* Create an empty mp4 file */
83     if (job->largeFileSize)
84     /* Use 64-bit MP4 file */
85     {
86         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
87         hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
88     }
89     else
90     /* Limit MP4s to less than 4 GB */
91     {
92         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
93     }
94
95     if (m->file == MP4_INVALID_FILE_HANDLE)
96     {
97         hb_error("muxmp4.c: MP4Create failed!");
98         *job->die = 1;
99         return 0;
100     }
101
102     /* Video track */
103     mux_data      = calloc(1, sizeof( hb_mux_data_t ) );
104     job->mux_data = mux_data;
105
106     if (!(MP4SetTimeScale( m->file, 90000 )))
107     {
108         hb_error("muxmp4.c: MP4SetTimeScale failed!");
109         *job->die = 1;
110         return 0;
111     }
112
113     if( job->vcodec == HB_VCODEC_X264 )
114     {
115         /* Stolen from mp4creator */
116         MP4SetVideoProfileLevel( m->file, 0x7F );
117                 mux_data->track = MP4AddH264VideoTrack( m->file, 90000,
118                         MP4_INVALID_DURATION, job->width, job->height,
119                         job->config.h264.sps[1], /* AVCProfileIndication */
120                         job->config.h264.sps[2], /* profile_compat */
121                         job->config.h264.sps[3], /* AVCLevelIndication */
122                         3 );      /* 4 bytes length before each NAL unit */
123         if ( mux_data->track == MP4_INVALID_TRACK_ID )
124         {
125             hb_error( "muxmp4.c: MP4AddH264VideoTrack failed!" );
126             *job->die = 1;
127             return 0;
128         }
129
130         /* Tune track chunk duration */
131         if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
132         {
133             return 0;
134         }
135
136         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
137                 job->config.h264.sps, job->config.h264.sps_length );
138         MP4AddH264PictureParameterSet( m->file, mux_data->track,
139                 job->config.h264.pps, job->config.h264.pps_length );
140
141                 if( job->h264_level == 30 || job->ipod_atom)
142                 {
143                         hb_deep_log( 2, "muxmp4: adding iPod atom");
144                         MP4AddIPodUUID(m->file, mux_data->track);
145                 }
146
147         m->init_delay = job->config.h264.init_delay;
148     }
149     else /* FFmpeg or XviD */
150     {
151         MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
152         mux_data->track = MP4AddVideoTrack( m->file, 90000,
153                 MP4_INVALID_DURATION, job->width, job->height,
154                 MP4_MPEG4_VIDEO_TYPE );
155         if (mux_data->track == MP4_INVALID_TRACK_ID)
156         {
157             hb_error("muxmp4.c: MP4AddVideoTrack failed!");
158             *job->die = 1;
159             return 0;
160         }
161
162         /* Tune track chunk duration */
163         if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
164         {
165             return 0;
166         }
167
168         /* VOL from FFmpeg or XviD */
169         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
170                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
171         {
172             hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
173             *job->die = 1;
174             return 0;
175         }
176     }
177
178     // COLR atom for color and gamma correction.
179     // Per the notes at:
180     //   http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
181     //   http://forum.doom9.org/showthread.php?t=133982#post1090068
182     // the user can set it from job->color_matrix, otherwise by default
183     // we say anything that's likely to be HD content is ITU BT.709 and
184     // DVD, SD TV & other content is ITU BT.601.  We look at the title height
185     // rather than the job height here to get uncropped input dimensions.
186     if( job->color_matrix == 1 )
187     {
188         // ITU BT.601 DVD or SD TV content
189         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
190     }
191     else if( job->color_matrix == 2 )
192     {
193         // ITU BT.709 HD content
194         MP4AddColr(m->file, mux_data->track, 1, 1, 1);        
195     }
196     else if ( job->title->width >= 1280 || job->title->height >= 720 )
197     {
198         // we guess that 720p or above is ITU BT.709 HD content
199         MP4AddColr(m->file, mux_data->track, 1, 1, 1);
200     }
201     else
202     {
203         // ITU BT.601 DVD or SD TV content
204         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
205     }
206
207     if( job->anamorphic.mode )
208     {
209         /* PASP atom for anamorphic video */
210         float width, height;
211
212         width  = job->anamorphic.par_width;
213
214         height = job->anamorphic.par_height;
215
216         MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
217
218         MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
219     }
220
221         /* add the audio tracks */
222     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
223     {
224         audio = hb_list_item( title->list_audio, i );
225         mux_data = calloc(1, sizeof( hb_mux_data_t ) );
226         audio->priv.mux_data = mux_data;
227
228         if( audio->config.out.codec == HB_ACODEC_AC3 )
229         {
230             uint8_t fscod = 0;
231             uint8_t bsid = audio->config.in.version;
232             uint8_t bsmod = audio->config.in.mode;
233             uint8_t acmod = audio->config.flags.ac3 & 0x7;
234             uint8_t lfeon = (audio->config.flags.ac3 & A52_LFE) ? 1 : 0;
235             uint8_t bit_rate_code = 0;
236
237             /*
238              * Rewrite AC3 information into correct format for dac3 atom
239              */
240             switch( audio->config.in.samplerate )
241             {
242             case 48000:
243                 fscod = 0;
244                 break;
245             case 44100:
246                 fscod = 1;
247                 break;
248             case 32000:
249                 fscod = 2;
250                 break;
251             default:
252                 /*
253                  * Error value, tells decoder to not decode this audio.
254                  */
255                 fscod = 3;
256                 break;
257             }
258
259             switch( audio->config.in.bitrate )
260             {
261             case 32000:
262                 bit_rate_code = 0;
263                 break;
264             case 40000:
265                 bit_rate_code = 1;
266                 break;
267             case 48000:
268                 bit_rate_code = 2;
269                 break;
270             case 56000:
271                 bit_rate_code = 3;
272                 break;
273             case 64000:
274                 bit_rate_code = 4;
275                 break;
276             case 80000:
277                 bit_rate_code = 5;
278                 break;
279             case 96000:
280                 bit_rate_code = 6;
281                 break;
282             case 112000:
283                 bit_rate_code = 7;
284                 break;
285             case 128000:
286                 bit_rate_code = 8;
287                 break;
288             case 160000:
289                 bit_rate_code = 9;
290                 break;
291             case 192000:
292                 bit_rate_code = 10;
293                 break;
294             case 224000:
295                 bit_rate_code = 11;
296                 break;
297             case 256000:
298                 bit_rate_code = 12;
299                 break;
300             case 320000:
301                 bit_rate_code = 13;
302                 break;
303             case 384000:
304                 bit_rate_code = 14;
305                 break;
306             case 448000:
307                 bit_rate_code = 15;
308                 break;
309             case 512000:
310                 bit_rate_code = 16;
311                 break;
312             case 576000:
313                 bit_rate_code = 17;
314                 break;
315             case 640000:
316                 bit_rate_code = 18;
317                 break;
318             default:
319                 hb_error("Unknown AC3 bitrate");
320                 bit_rate_code = 0;
321                 break;
322             }
323
324             mux_data->track = MP4AddAC3AudioTrack(
325                 m->file,
326                 audio->config.out.samplerate, 
327                 fscod,
328                 bsid,
329                 bsmod,
330                 acmod,
331                 lfeon,
332                 bit_rate_code);
333
334             /* Tune track chunk duration */
335             MP4TuneTrackDurationPerChunk( m, mux_data->track );
336
337             if (audio->config.out.name == NULL) {
338                 MP4SetTrackBytesProperty(
339                     m->file, mux_data->track,
340                     "udta.name.value",
341                     (const uint8_t*)"Surround", strlen("Surround"));
342             }
343             else {
344                 MP4SetTrackBytesProperty(
345                     m->file, mux_data->track,
346                     "udta.name.value",
347                     (const uint8_t*)(audio->config.out.name),
348                     strlen(audio->config.out.name));
349             }
350         } else {
351             mux_data->track = MP4AddAudioTrack(
352                 m->file,
353                 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
354
355             /* Tune track chunk duration */
356             MP4TuneTrackDurationPerChunk( m, mux_data->track );
357
358             if (audio->config.out.name == NULL) {
359                 MP4SetTrackBytesProperty(
360                     m->file, mux_data->track,
361                     "udta.name.value",
362                     (const uint8_t*)"Stereo", strlen("Stereo"));
363             }
364             else {
365                 MP4SetTrackBytesProperty(
366                     m->file, mux_data->track,
367                     "udta.name.value",
368                     (const uint8_t*)(audio->config.out.name),
369                     strlen(audio->config.out.name));
370             }
371
372             MP4SetAudioProfileLevel( m->file, 0x0F );
373             MP4SetTrackESConfiguration(
374                 m->file, mux_data->track,
375                 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
376
377             /* Set the correct number of channels for this track */
378              MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
379         }
380
381         /* Set the language for this track */
382         MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
383
384         if( hb_list_count( title->list_audio ) > 1 )
385         {
386             /* Set the audio track alternate group */
387             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
388         }
389
390         if (i == 0) {
391             /* Enable the first audio track */
392             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
393         }
394         else
395             /* Disable the other audio tracks so QuickTime doesn't play
396                them all at once. */
397         {
398             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
399             hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
400         }
401
402     }
403
404     for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
405     {
406         hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
407         
408         if( subtitle && subtitle->format == TEXTSUB && 
409             subtitle->dest == PASSTHRUSUB )
410         {
411             mux_data = calloc(1, sizeof( hb_mux_data_t ) );
412             subtitle->mux_data = mux_data;
413             mux_data->subtitle = 1;
414             mux_data->sub_format = subtitle->format;
415             // TODO: add subtitle track
416             // mux_data->track = MP4AddSubtitleTrack(....);
417         }
418     }
419
420     if (job->chapter_markers)
421     {
422         /* add a text track for the chapters. We add the 'chap' atom to track
423            one which is usually the video track & should never be disabled.
424            The Quicktime spec says it doesn't matter which media track the
425            chap atom is on but it has to be an enabled track. */
426         MP4TrackId textTrack;
427         textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
428
429         m->chapter_track = textTrack;
430         m->chapter_duration = 0;
431         m->current_chapter = job->chapter_start;
432     }
433
434     /* Add encoded-by metadata listing version and build date */
435     char *tool_string;
436     tool_string = (char *)malloc(80);
437     snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
438
439     /* allocate,fetch,populate,store,free tags structure */
440     const MP4Tags* tags;
441     tags = MP4TagsAlloc();
442     MP4TagsFetch( tags, m->file );
443     MP4TagsSetEncodingTool( tags, tool_string );
444     MP4TagsStore( tags, m->file );
445     MP4TagsFree( tags );
446
447     free(tool_string);
448
449     return 0;
450 }
451
452 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
453                    hb_buffer_t * buf )
454 {
455     hb_job_t * job = m->job;
456     int64_t duration;
457     int64_t offset = 0;
458
459     if( mux_data == job->mux_data )
460     {
461         /* Video */
462
463         // if there are b-frames compute the render offset
464         // (we'll need it for both the video frame & the chapter track)
465         if ( m->init_delay )
466         {
467             offset = buf->start + m->init_delay - m->sum_dur;
468             if ( offset < 0 )
469             {
470                 hb_log("MP4Mux: illegal render offset %lld, start %lld,"
471                        "stop %lld, sum_dur %lld",
472                        offset, buf->start, buf->stop, m->sum_dur );
473                 offset = 0;
474             }
475         }
476
477         /* Add the sample before the new frame.
478            It is important that this be calculated prior to the duration
479            of the new video sample, as we want to sync to right after it.
480            (This is because of how durations for text tracks work in QT) */
481         if( job->chapter_markers && buf->new_chap )
482         {    
483             hb_chapter_t *chapter = NULL;
484
485             // this chapter is postioned by writing out the previous chapter.
486             // the duration of the previous chapter is the duration up to but
487             // not including the current frame minus the duration of all
488             // chapters up to the previous.
489             // The initial and final chapters can be very short (a second or
490             // less) since they're not really chapters but just a placeholder to
491             // insert a cell command. We don't write chapters shorter than 1.5 sec.
492             duration = m->sum_dur - m->chapter_duration + offset;
493             if ( duration >= (90000*3)/2 )
494             {
495                 chapter = hb_list_item( m->job->title->list_chapter,
496                                         buf->new_chap - 2 );
497
498                 MP4AddChapter( m->file,
499                                m->chapter_track,
500                                duration,
501                                (chapter != NULL) ? chapter->title : NULL);
502
503                 m->current_chapter = buf->new_chap;
504                 m->chapter_duration += duration;
505             }
506         }
507
508         // We're getting the frames in decode order but the timestamps are
509         // for presentation so we have to use durations and effectively
510         // compute a DTS.
511         duration = buf->stop - buf->start;
512         if ( duration <= 0 )
513         {
514             /* We got an illegal mp4/h264 duration. This shouldn't
515                be possible and usually indicates a bug in the upstream code.
516                Complain in the hope that someone will go find the bug but
517                try to fix the error so that the file will still be playable. */
518             hb_log("MP4Mux: illegal duration %lld, start %lld,"
519                    "stop %lld, sum_dur %lld",
520                    duration, buf->start, buf->stop, m->sum_dur );
521             /* we don't know when the next frame starts so we can't pick a
522                valid duration for this one. we pick something "short"
523                (roughly 1/3 of an NTSC frame time) to take time from
524                the next frame. */
525             duration = 1000;
526         }
527         m->sum_dur += duration;
528     }
529     else
530     {
531         /* Audio */
532         duration = MP4_INVALID_DURATION;
533     }
534
535     /* Here's where the sample actually gets muxed. */
536     if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
537     {
538         /* Compute dependency flags.
539          *
540          * This mechanism is (optionally) used by media players such as QuickTime
541          * to offer better scrubbing performance. The most influential bits are
542          * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
543          *
544          * Other bits are possible but no example media using such bits have been
545          * found.
546          *
547          * It is acceptable to supply 0-bits for any samples which characteristics
548          * cannot be positively guaranteed.
549          */
550         int sync = 0;
551         uint32_t dflags = 0;
552
553         /* encoding layer signals if frame is referenced by other frames */
554         if( buf->flags & HB_FRAME_REF )
555             dflags |= MP4_SDT_HAS_DEPENDENTS;
556         else
557             dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
558
559         switch( buf->frametype )
560         {
561             case HB_FRAME_IDR:
562                 sync = 1;
563                 break;
564             case HB_FRAME_I:
565                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
566                 break;
567             case HB_FRAME_P:
568                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
569                 break;
570             case HB_FRAME_BREF:
571             case HB_FRAME_B:
572             default:
573                 break; /* nothing to mark */
574         }
575
576         if( !MP4WriteSampleDependency( m->file,
577                                        mux_data->track,
578                                        buf->data,
579                                        buf->size,
580                                        duration,
581                                        offset,
582                                        sync,
583                                        dflags ))
584         {
585             hb_error("Failed to write to output file, disk full?");
586             *job->die = 1;
587         }
588     }
589     else if (mux_data->subtitle)
590     {
591         if( mux_data->sub_format == TEXTSUB )
592         {
593             hb_log("MuxMP4: Text Sub:%lld: %s", buf->start, buf->data);
594         }
595     }
596     else
597     {
598         if( !MP4WriteSample( m->file,
599                              mux_data->track,
600                              buf->data,
601                              buf->size,
602                              duration,
603                              offset,
604                              ( buf->frametype & HB_FRAME_KEY ) != 0 ))
605         {
606             hb_error("Failed to write to output file, disk full?");
607             *job->die = 1;
608         }
609     }
610
611
612     return 0;
613 }
614
615 static int MP4End( hb_mux_object_t * m )
616 {
617     hb_job_t   * job   = m->job;
618     hb_title_t * title = job->title;
619
620     /* Write our final chapter marker */
621     if( m->job->chapter_markers )
622     {
623         hb_chapter_t *chapter = NULL;
624         int64_t duration = m->sum_dur - m->chapter_duration;
625         /* The final chapter can have a very short duration - if it's less
626          * than 1.5 seconds just skip it. */
627         if ( duration >= (90000*3)/2 )
628         {
629
630             chapter = hb_list_item( m->job->title->list_chapter,
631                                     m->current_chapter - 1 );
632
633             MP4AddChapter( m->file,
634                            m->chapter_track,
635                            duration,
636                            (chapter != NULL) ? chapter->title : NULL);
637         }
638     }
639
640     if (job->areBframes)
641     {
642            // Insert track edit to get A/V back in sync.  The edit amount is
643            // the init_delay.
644            int64_t edit_amt = m->init_delay;
645            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
646                            MP4GetTrackDuration(m->file, 1), 0);
647             if ( m->job->chapter_markers )
648             {
649                 // apply same edit to chapter track to keep it in sync with video
650                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
651                                 edit_amt,
652                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
653             }
654      }
655
656     /*
657      * Write the MP4 iTunes metadata if we have any metadata
658      */
659     if( title->metadata )
660     {
661         hb_metadata_t *md = title->metadata;
662         const MP4Tags* tags;
663
664         hb_deep_log( 2, "Writing Metadata to output file...");
665
666         /* allocate tags structure */
667         tags = MP4TagsAlloc();
668         /* fetch data from MP4 file (in case it already has some data) */
669         MP4TagsFetch( tags, m->file );
670
671         /* populate */
672         MP4TagsSetName( tags, md->name );
673         MP4TagsSetArtist( tags, md->artist );
674         MP4TagsSetComposer( tags, md->composer );
675         MP4TagsSetComments( tags, md->comment );
676         MP4TagsSetReleaseDate( tags, md->release_date );
677         MP4TagsSetAlbum( tags, md->album );
678         MP4TagsSetGenre( tags, md->genre );
679
680         if( md->coverart )
681         {
682             MP4TagArtwork art;
683             art.data = md->coverart;
684             art.size = md->coverart_size;
685             art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
686             MP4TagsAddArtwork( tags, &art );
687         }
688
689         /* push data to MP4 file */
690         MP4TagsStore( tags, m->file );
691         /* free memory associated with structure */
692         MP4TagsFree( tags );
693     }
694
695     MP4Close( m->file );
696
697     if ( job->mp4_optimize )
698     {
699         hb_log( "muxmp4: optimizing file" );
700         char filename[1024]; memset( filename, 0, 1024 );
701         snprintf( filename, 1024, "%s.tmp", job->file );
702         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
703         remove( job->file );
704         rename( filename, job->file );
705     }
706
707     return 0;
708 }
709
710 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
711 {
712     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
713     m->init      = MP4Init;
714     m->mux       = MP4Mux;
715     m->end       = MP4End;
716     m->job       = job;
717     return m;
718 }
719