OSDN Git Service

LinGui: make Help->Guide work on windows/mingw
[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     hb_buffer_t *delay_buf;
24
25     /* Chapter state information for muxing */
26     MP4TrackId chapter_track;
27     int current_chapter;
28     uint64_t chapter_duration;
29 };
30
31 struct hb_mux_data_s
32 {
33     MP4TrackId  track;
34     uint8_t     subtitle;
35     int         sub_format;
36
37     uint64_t    sum_dur; // sum of the frame durations so far
38 };
39
40 /* Tune video track chunk duration.
41  * libmp4v2 default duration == dusamplerate == 1 second.
42  * Per van's suggestion we desire duration == 4 frames.
43  * Should be invoked immediately after track creation.
44  *
45  * return true on fail, false on success.
46  */
47 static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId )
48 {
49     uint32_t tscale;
50     MP4Duration dur;
51
52     tscale = MP4GetTrackTimeScale( m->file, trackId );
53     dur = (MP4Duration)ceil( (double)tscale * (double)m->job->vrate_base / (double)m->job->vrate * 4.0 );
54
55     if( !MP4SetTrackDurationPerChunk( m->file, trackId, dur ))
56     {
57         hb_error( "muxmp4.c: MP4SetTrackDurationPerChunk failed!" );
58         *m->job->die = 1;
59         return 0;
60     }
61
62     hb_deep_log( 2, "muxmp4: track %u, chunk duration %"PRIu64, MP4FindTrackIndex( m->file, trackId ), dur );
63     return 1;
64 }
65
66 /**********************************************************************
67  * MP4Init
68  **********************************************************************
69  * Allocates hb_mux_data_t structures, create file and write headers
70  *********************************************************************/
71 static int MP4Init( hb_mux_object_t * m )
72 {
73     hb_job_t   * job   = m->job;
74     hb_title_t * title = job->title;
75
76     hb_audio_t    * audio;
77     hb_mux_data_t * mux_data;
78     int i;
79     int subtitle_default;
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     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 if( audio->config.out.codec == HB_ACODEC_FAAC ||
351                    audio->config.out.codec == HB_ACODEC_CA_AAC ) {
352             mux_data->track = MP4AddAudioTrack(
353                 m->file,
354                 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
355
356             /* Tune track chunk duration */
357             MP4TuneTrackDurationPerChunk( m, mux_data->track );
358
359             if (audio->config.out.name == NULL) {
360                 MP4SetTrackBytesProperty(
361                     m->file, mux_data->track,
362                     "udta.name.value",
363                     (const uint8_t*)"Stereo", strlen("Stereo"));
364             }
365             else {
366                 MP4SetTrackBytesProperty(
367                     m->file, mux_data->track,
368                     "udta.name.value",
369                     (const uint8_t*)(audio->config.out.name),
370                     strlen(audio->config.out.name));
371             }
372
373             MP4SetAudioProfileLevel( m->file, 0x0F );
374             MP4SetTrackESConfiguration(
375                 m->file, mux_data->track,
376                 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
377
378             /* Set the correct number of channels for this track */
379              MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
380         } else if( audio->config.out.codec == HB_ACODEC_LAME ) {
381             mux_data->track = MP4AddAudioTrack(
382                 m->file,
383                 audio->config.out.samplerate, 1152, MP4_MPEG2_AUDIO_TYPE );
384
385             /* Tune track chunk duration */
386             MP4TuneTrackDurationPerChunk( m, mux_data->track );
387
388             if (audio->config.out.name == NULL) {
389                 MP4SetTrackBytesProperty(
390                     m->file, mux_data->track,
391                     "udta.name.value",
392                     (const uint8_t*)"Stereo", strlen("Stereo"));
393             }
394             else {
395                 MP4SetTrackBytesProperty(
396                     m->file, mux_data->track,
397                     "udta.name.value",
398                     (const uint8_t*)(audio->config.out.name),
399                     strlen(audio->config.out.name));
400             }
401
402             MP4SetAudioProfileLevel( m->file, 0x0F );
403
404             /* Set the correct number of channels for this track */
405              MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
406         }
407
408         /* Set the language for this track */
409         MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
410
411         if( hb_list_count( title->list_audio ) > 1 )
412         {
413             /* Set the audio track alternate group */
414             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
415         }
416
417         if (i == 0) {
418             /* Enable the first audio track */
419             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
420         }
421         else
422             /* Disable the other audio tracks so QuickTime doesn't play
423                them all at once. */
424         {
425             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
426             hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
427         }
428
429     }
430
431     // Quicktime requires that at least one subtitle is enabled,
432     // else it doesn't show any of the subtitles.
433     // So check to see if any of the subtitles are flagged to be
434     // the defualt.  The default will the the enabled track, else
435     // enable the first track.
436     subtitle_default = 0;
437     for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
438     {
439         hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
440
441         if( subtitle && subtitle->format == TEXTSUB && 
442             subtitle->config.dest == PASSTHRUSUB )
443         {
444             if ( subtitle->config.default_track )
445                 subtitle_default = 1;
446         }
447     }
448     for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
449     {
450         hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
451
452         if( subtitle && subtitle->format == TEXTSUB && 
453             subtitle->config.dest == PASSTHRUSUB )
454         {
455             uint64_t width, height = 60;
456             if( job->anamorphic.mode )
457                 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
458             else
459                 width = job->width;
460
461             mux_data = calloc(1, sizeof( hb_mux_data_t ) );
462             subtitle->mux_data = mux_data;
463             mux_data->subtitle = 1;
464             mux_data->sub_format = subtitle->format;
465             mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
466
467             MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
468
469             /* Tune track chunk duration */
470             MP4TuneTrackDurationPerChunk( m, mux_data->track );
471
472             const uint8_t textColor[4] = { 255,255,255,255 };
473
474             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
475
476             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
477             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
478             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
479
480             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
481
482             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
483             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
484
485             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
486             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
487
488             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
489             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
490             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
491             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
492             
493             /* translate the track */
494             uint8_t* val;
495             uint8_t nval[36];
496             uint32_t *ptr32 = (uint32_t*) nval;
497             uint32_t size;
498
499             MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
500             memcpy(nval, val, size);
501
502             const uint32_t ytranslation = (job->height - height) * 0x10000;
503                 
504 #ifdef WORDS_BIGENDIAN
505             ptr32[7] = ytranslation;
506 #else
507             /* we need to switch the endianness, as the file format expects big endian */
508             ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) + 
509                             ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
510 #endif
511
512             MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);  
513             if ( !subtitle_default || subtitle->config.default_track ) {
514                 /* Enable the default subtitle track */
515                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
516                 subtitle_default = 1;
517             }
518             else
519             {
520                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
521             }
522         }
523         else if( subtitle && subtitle->format == PICTURESUB && 
524             subtitle->config.dest == PASSTHRUSUB )
525         {
526             mux_data = calloc(1, sizeof( hb_mux_data_t ) );
527             subtitle->mux_data = mux_data;
528             mux_data->subtitle = 1;
529             mux_data->sub_format = subtitle->format;
530
531             mux_data->track = MP4AddSubpicTrack( m->file, 90000, subtitle->width, subtitle->height );
532
533             MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
534
535             /* Tune track chunk duration */
536             MP4TuneTrackDurationPerChunk( m, mux_data->track );
537             uint8_t palette[16][4];
538             int ii;
539             for ( ii = 0; ii < 16; ii++ )
540             {
541                 palette[ii][0] = 0;
542                 palette[ii][1] = (subtitle->palette[ii] >> 16) & 0xff;
543                 palette[ii][2] = (subtitle->palette[ii] >> 8) & 0xff;
544                 palette[ii][3] = (subtitle->palette[ii]) & 0xff;
545             }
546             if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
547                     (uint8_t*)palette, 16 * 4 )))
548             {
549                 hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
550                 *job->die = 1;
551                 return 0;
552             }
553             if ( !subtitle_default || subtitle->config.default_track ) {
554                 /* Enable the default subtitle track */
555                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
556                 subtitle_default = 1;
557             }
558             else
559             {
560                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
561             }
562         }
563     }
564
565     if (job->chapter_markers)
566     {
567         /* add a text track for the chapters. We add the 'chap' atom to track
568            one which is usually the video track & should never be disabled.
569            The Quicktime spec says it doesn't matter which media track the
570            chap atom is on but it has to be an enabled track. */
571         MP4TrackId textTrack;
572         textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
573
574         m->chapter_track = textTrack;
575         m->chapter_duration = 0;
576         m->current_chapter = job->chapter_start;
577     }
578
579     /* Add encoded-by metadata listing version and build date */
580     char *tool_string;
581     tool_string = (char *)malloc(80);
582     snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
583
584     /* allocate,fetch,populate,store,free tags structure */
585     const MP4Tags* tags;
586     tags = MP4TagsAlloc();
587     MP4TagsFetch( tags, m->file );
588     MP4TagsSetEncodingTool( tags, tool_string );
589     MP4TagsStore( tags, m->file );
590     MP4TagsFree( tags );
591
592     free(tool_string);
593
594     return 0;
595 }
596
597 typedef struct stylerecord_s {
598     enum style_s {ITALIC, BOLD, UNDERLINE} style;
599     uint16_t start;
600     uint16_t stop;
601     struct stylerecord_s *next;
602 } stylerecord;
603
604 static void hb_makestylerecord( stylerecord **stack, 
605                                 enum style_s style, int start )
606 {
607     stylerecord *record = calloc( sizeof( stylerecord ), 1 );
608
609     if( record ) 
610     {
611         record->style = style;
612         record->start = start;
613         record->next = *stack;
614         *stack = record;
615     }
616 }
617
618 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
619 {
620     uint8_t face = 1;
621     hb_deep_log(3, "Made style '%s' from %d to %d", 
622            record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
623     
624     switch( record->style )
625     {
626     case ITALIC:
627         face = 2;
628         break;
629     case BOLD:
630         face = 1;
631         break;
632     case UNDERLINE:
633         face = 4;
634         break;
635     default:
636         face = 2;
637         break;
638     }
639
640     style[0] = (record->start >> 8) & 0xff; // startChar
641     style[1] = record->start & 0xff;
642     style[2] = (record->stop >> 8) & 0xff;   // endChar
643     style[3] = record->stop & 0xff;
644     style[4] = (1 >> 8) & 0xff;    // font-ID
645     style[5] = 1 & 0xff;
646     style[6] = face;   // face-style-flags: 1 bold; 2 italic; 4 underline
647     style[7] = 24;      // font-size
648     style[8] = 255;     // r
649     style[9] = 255;     // g
650     style[10] = 255;    // b
651     style[11] = 255;    // a
652  
653 }
654
655 /*
656  * Copy the input to output removing markup and adding markup to the style
657  * atom where appropriate.
658  */
659 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
660                                               uint8_t *output,
661                                               uint8_t *style, uint16_t *stylesize )
662 {
663     uint8_t *reader = input;
664     uint8_t *writer = output;
665     uint8_t stylecount = 0;
666     uint16_t utf8_count = 0;         // utf8 count from start of subtitle
667     stylerecord *stylestack = NULL;
668     stylerecord *oldrecord = NULL;
669     
670     while(*reader != '\0') {
671         if( ( *reader & 0xc0 ) == 0x80 ) 
672         {
673             /*
674              * Track the utf8_count when doing markup so that we get the tx3g stops
675              * based on UTF8 chr counts rather than bytes.
676              */
677             utf8_count++;
678             hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far", 
679                              utf8_count);
680         }
681         if (*reader == '<') {
682             /*
683              * possible markup, peek at the next chr
684              */
685             switch(*(reader+1)) {
686             case 'i':
687                 if (*(reader+2) == '>') {
688                     reader += 3;
689                     hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
690                 } else {
691                     *writer++ = *reader++;
692                 }
693                 break;
694             case 'b':
695                 if (*(reader+2) == '>') {
696                     reader += 3; 
697                     hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
698                 } else {
699                     *writer++ = *reader++;  
700                 }
701                 break;
702             case 'u': 
703                 if (*(reader+2) == '>') {
704                     reader += 3;
705                     hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
706                 } else {
707                     *writer++ = *reader++;
708                 }
709                 break;
710             case '/':
711                 switch(*(reader+2)) {
712                 case 'i':
713                     if (*(reader+3) == '>') {
714                         /*
715                          * Check whether we then immediately start more markup of the same type, if so then
716                          * lets not close it now and instead continue this markup.
717                          */
718                         if ((*(reader+4) && *(reader+4) == '<') &&
719                             (*(reader+5) && *(reader+5) == 'i') &&
720                             (*(reader+6) && *(reader+6) == '>')) {
721                             /*
722                              * Opening italics right after, so don't close off these italics.
723                              */
724                             hb_deep_log(3, "Joining two sets of italics");
725                             reader += (4 + 3);
726                             continue;
727                         }
728
729
730                         if ((*(reader+4) && *(reader+4) == ' ') && 
731                             (*(reader+5) && *(reader+5) == '<') &&
732                             (*(reader+6) && *(reader+6) == 'i') &&
733                             (*(reader+7) && *(reader+7) == '>')) {
734                             /*
735                              * Opening italics right after, so don't close off these italics.
736                              */
737                             hb_deep_log(3, "Joining two sets of italics (plus space)");
738                             reader += (4 + 4);
739                             *writer++ = ' ';
740                             continue;
741                         }
742                         if (stylestack && stylestack->style == ITALIC) {
743                             uint8_t style_record[12];
744                             stylestack->stop = writer - output - utf8_count;
745                             hb_makestyleatom(stylestack, style_record);
746
747                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
748                             stylecount++;
749
750                             oldrecord = stylestack;
751                             stylestack = stylestack->next;
752                             free(oldrecord);
753                         } else {
754                             hb_error("Mismatched Subtitle markup '%s'", input);
755                         }
756                         reader += 4;
757                     } else {
758                         *writer++ = *reader++;
759                     }
760                     break;
761                 case 'b':
762                     if (*(reader+3) == '>') {
763                         if (stylestack && stylestack->style == BOLD) {
764                             uint8_t style_record[12];
765                             stylestack->stop = writer - output - utf8_count;
766                             hb_makestyleatom(stylestack, style_record);
767
768                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
769                             stylecount++;
770                             oldrecord = stylestack;
771                             stylestack = stylestack->next;
772                             free(oldrecord);
773                         } else {
774                             hb_error("Mismatched Subtitle markup '%s'", input);
775                         }
776
777                         reader += 4;
778                     } else {
779                         *writer++ = *reader++;
780                     }
781                     break;
782                 case 'u': 
783                     if (*(reader+3) == '>') {
784                         if (stylestack && stylestack->style == UNDERLINE) {
785                             uint8_t style_record[12];
786                             stylestack->stop = writer - output - utf8_count;
787                             hb_makestyleatom(stylestack, style_record);
788
789                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
790                             stylecount++;
791
792                             oldrecord = stylestack;
793                             stylestack = stylestack->next;
794                             free(oldrecord);
795                         } else {
796                             hb_error("Mismatched Subtitle markup '%s'", input);
797                         }
798                         reader += 4;
799                     } else {
800                         *writer++ = *reader++;
801                     }
802                     break;
803                 default:
804                     *writer++ = *reader++;
805                     break;
806                 }
807                 break;
808             default:
809                 *writer++ = *reader++;
810                 break;
811             }
812         } else {
813             *writer++ = *reader++;
814         }
815     }
816     *writer = '\0';
817
818     if( stylecount )
819     {
820         *stylesize = 10 + ( stylecount * 12 );
821
822         memcpy( style + 4, "styl", 4);
823
824         style[0] = 0;
825         style[1] = 0;
826         style[2] = (*stylesize >> 8) & 0xff;
827         style[3] = *stylesize & 0xff;
828         style[8] = (stylecount >> 8) & 0xff;
829         style[9] = stylecount & 0xff;
830
831     }
832
833 }
834
835 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
836                    hb_buffer_t * buf )
837 {
838     hb_job_t * job = m->job;
839     int64_t duration;
840     int64_t offset = 0;
841     hb_buffer_t *tmp;
842
843     if( mux_data == job->mux_data )
844     {
845         /* Video */
846
847         if( job->vcodec == HB_VCODEC_X264 )
848         {
849             if ( buf && buf->start < buf->renderOffset )
850             {
851                 hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
852                        buf->start, buf->renderOffset );
853                 buf->renderOffset = buf->start;
854             }
855         }
856
857         // We delay muxing video by one frame so that we can calculate
858         // the dts to dts duration of the frames.
859         tmp = buf;
860         buf = m->delay_buf;
861         m->delay_buf = tmp;
862
863         if ( !buf )
864             return 0;
865
866         if( job->vcodec == HB_VCODEC_X264 )
867         {
868             // x264 supplies us with DTS, so offset is PTS - DTS
869             offset = buf->start - buf->renderOffset;
870         }
871
872         /* Add the sample before the new frame.
873            It is important that this be calculated prior to the duration
874            of the new video sample, as we want to sync to right after it.
875            (This is because of how durations for text tracks work in QT) */
876         if( job->chapter_markers && buf->new_chap )
877         {    
878             hb_chapter_t *chapter = NULL;
879
880             // this chapter is postioned by writing out the previous chapter.
881             // the duration of the previous chapter is the duration up to but
882             // not including the current frame minus the duration of all
883             // chapters up to the previous.
884             // The initial and final chapters can be very short (a second or
885             // less) since they're not really chapters but just a placeholder to
886             // insert a cell command. We don't write chapters shorter than 1.5 sec.
887             duration = m->sum_dur - m->chapter_duration + offset;
888             if ( duration >= (90000*3)/2 )
889             {
890                 chapter = hb_list_item( m->job->title->list_chapter,
891                                         buf->new_chap - 2 );
892
893                 MP4AddChapter( m->file,
894                                m->chapter_track,
895                                duration,
896                                (chapter != NULL) ? chapter->title : NULL);
897
898                 m->current_chapter = buf->new_chap;
899                 m->chapter_duration += duration;
900             }
901         }
902
903         if( job->vcodec == HB_VCODEC_X264 )
904         {
905             // x264 supplies us with DTS
906             if ( m->delay_buf )
907             {
908                 duration = m->delay_buf->renderOffset - buf->renderOffset;
909             }
910             else
911             {
912                 duration = buf->stop - m->sum_dur;
913                 // Due to how libx264 generates DTS, it's possible for the
914                 // above calculation to be negative. 
915                 //
916                 // x264 generates DTS by rearranging PTS in this sequence:
917                 // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
918                 //
919                 // where delay == pts2.  This guarantees that DTS <= PTS for
920                 // any frame, but also generates this sequence of durations:
921                 // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
922                 // 
923                 // so the sum up to the last frame is:
924                 // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
925                 //
926                 // while the original total duration of the video was:
927                 // duration = d0 + d1 + d2 + d3 ... + d(N)
928                 //
929                 // Note that if d0 + d1 != d(N-1) + d(N), the total
930                 // length of the video changes since d(N-1) and d(N) are
931                 // replaced by d0 and d1 in the final duration sum.
932                 //
933                 // To keep the total length of the video the same as the source
934                 // we try to make 
935                 // d(N-2) = duration - sum_dur
936                 //
937                 // But if d0 + d1 >= d(N-1) + d(N), the above calculation
938                 // results in a nagative value and we need to fix it.
939                 if ( duration <= 0 )
940                     duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
941             }
942         }
943         else
944         {
945             // We're getting the frames in decode order but the timestamps are
946             // for presentation so we have to use durations and effectively
947             // compute a DTS.
948             duration = buf->stop - buf->start;
949         }
950
951         if ( duration <= 0 )
952         {
953             /* We got an illegal mp4/h264 duration. This shouldn't
954                be possible and usually indicates a bug in the upstream code.
955                Complain in the hope that someone will go find the bug but
956                try to fix the error so that the file will still be playable. */
957             hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
958                    "stop %"PRId64", sum_dur %"PRId64,
959                    duration, buf->start, buf->stop, m->sum_dur );
960             /* we don't know when the next frame starts so we can't pick a
961                valid duration for this one. we pick something "short"
962                (roughly 1/3 of an NTSC frame time) to take time from
963                the next frame. */
964             duration = 1000;
965         }
966         m->sum_dur += duration;
967     }
968     else
969     {
970         /* Audio */
971         duration = MP4_INVALID_DURATION;
972     }
973
974     /* Here's where the sample actually gets muxed. */
975     if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
976     {
977         /* Compute dependency flags.
978          *
979          * This mechanism is (optionally) used by media players such as QuickTime
980          * to offer better scrubbing performance. The most influential bits are
981          * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
982          *
983          * Other bits are possible but no example media using such bits have been
984          * found.
985          *
986          * It is acceptable to supply 0-bits for any samples which characteristics
987          * cannot be positively guaranteed.
988          */
989         int sync = 0;
990         uint32_t dflags = 0;
991
992         /* encoding layer signals if frame is referenced by other frames */
993         if( buf->flags & HB_FRAME_REF )
994             dflags |= MP4_SDT_HAS_DEPENDENTS;
995         else
996             dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
997
998         switch( buf->frametype )
999         {
1000             case HB_FRAME_IDR:
1001                 sync = 1;
1002                 break;
1003             case HB_FRAME_I:
1004                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
1005                 break;
1006             case HB_FRAME_P:
1007                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
1008                 break;
1009             case HB_FRAME_BREF:
1010             case HB_FRAME_B:
1011             default:
1012                 break; /* nothing to mark */
1013         }
1014
1015         if( !MP4WriteSampleDependency( m->file,
1016                                        mux_data->track,
1017                                        buf->data,
1018                                        buf->size,
1019                                        duration,
1020                                        offset,
1021                                        sync,
1022                                        dflags ))
1023         {
1024             hb_error("Failed to write to output file, disk full?");
1025             *job->die = 1;
1026         }
1027     }
1028     else if (mux_data->subtitle)
1029     {
1030         if( mux_data->sub_format == TEXTSUB )
1031         {
1032             /* Write an empty sample */
1033             if ( mux_data->sum_dur < buf->start )
1034             {
1035                 uint8_t empty[2] = {0,0};
1036                 if( !MP4WriteSample( m->file,
1037                                     mux_data->track,
1038                                     empty,
1039                                     2,
1040                                     buf->start - mux_data->sum_dur,
1041                                     0,
1042                                     1 ))
1043                 {
1044                     hb_error("Failed to write to output file, disk full?");
1045                     *job->die = 1;
1046                 } 
1047                 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1048             }
1049             uint8_t styleatom[2048];;
1050             uint16_t stylesize = 0;
1051             uint8_t buffer[2048];
1052             uint16_t buffersize = 0;
1053             uint8_t output[2048];
1054
1055             *buffer = '\0';
1056
1057             /*
1058              * Copy the subtitle into buffer stripping markup and creating
1059              * style atoms for them.
1060              */
1061             hb_muxmp4_process_subtitle_style( buf->data,
1062                                               buffer,
1063                                               styleatom, &stylesize );
1064
1065             buffersize = strlen((char*)buffer);
1066
1067             hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
1068                         (float)buf->start / 90000, buf->start, buf->stop, 
1069                         (buf->stop - buf->start), buffer);
1070
1071             /* Write the subtitle sample */
1072             memcpy( output + 2, buffer, buffersize );
1073             memcpy( output + 2 + buffersize, styleatom, stylesize);
1074             output[0] = ( buffersize >> 8 ) & 0xff;
1075             output[1] = buffersize & 0xff;
1076
1077             if( !MP4WriteSample( m->file,
1078                                  mux_data->track,
1079                                  output,
1080                                  buffersize + stylesize + 2,
1081                                  buf->stop - buf->start,
1082                                  0,
1083                                  1 ))
1084             {
1085                 hb_error("Failed to write to output file, disk full?");
1086                 *job->die = 1;
1087             }
1088
1089             mux_data->sum_dur += (buf->stop - buf->start);
1090         }
1091         else if( mux_data->sub_format == PICTURESUB )
1092         {
1093             /* Write an empty sample */
1094             if ( mux_data->sum_dur < buf->start )
1095             {
1096                 uint8_t empty[2] = {0,0};
1097                 if( !MP4WriteSample( m->file,
1098                                     mux_data->track,
1099                                     empty,
1100                                     2,
1101                                     buf->start - mux_data->sum_dur,
1102                                     0,
1103                                     1 ))
1104                 {
1105                     hb_error("Failed to write to output file, disk full?");
1106                     *job->die = 1;
1107                 } 
1108                 mux_data->sum_dur += buf->start - mux_data->sum_dur;
1109             }
1110             if( !MP4WriteSample( m->file,
1111                                  mux_data->track,
1112                                  buf->data,
1113                                  buf->size,
1114                                  buf->stop - buf->start,
1115                                  0,
1116                                  1 ))
1117             {
1118                 hb_error("Failed to write to output file, disk full?");
1119                 *job->die = 1;
1120             }
1121
1122             mux_data->sum_dur += (buf->stop - buf->start);
1123         }
1124     }
1125     else
1126     {
1127         /*
1128          * Audio
1129          */
1130         if( !MP4WriteSample( m->file,
1131                              mux_data->track,
1132                              buf->data,
1133                              buf->size,
1134                              duration,
1135                              offset,
1136                              ( buf->frametype & HB_FRAME_KEY ) != 0 ))
1137         {
1138             hb_error("Failed to write to output file, disk full?");
1139             *job->die = 1;
1140         }
1141     }
1142     hb_buffer_close( &buf );
1143
1144     return 0;
1145 }
1146
1147 static int MP4End( hb_mux_object_t * m )
1148 {
1149     hb_job_t   * job   = m->job;
1150     hb_title_t * title = job->title;
1151
1152     // Flush the delayed frame
1153     if ( m->delay_buf )
1154         MP4Mux( m, job->mux_data, NULL );
1155
1156     /* Write our final chapter marker */
1157     if( m->job->chapter_markers )
1158     {
1159         hb_chapter_t *chapter = NULL;
1160         int64_t duration = m->sum_dur - m->chapter_duration;
1161         /* The final chapter can have a very short duration - if it's less
1162          * than 1.5 seconds just skip it. */
1163         if ( duration >= (90000*3)/2 )
1164         {
1165
1166             chapter = hb_list_item( m->job->title->list_chapter,
1167                                     m->current_chapter - 1 );
1168
1169             MP4AddChapter( m->file,
1170                            m->chapter_track,
1171                            duration,
1172                            (chapter != NULL) ? chapter->title : NULL);
1173         }
1174     }
1175
1176     if ( job->config.h264.init_delay )
1177     {
1178            // Insert track edit to get A/V back in sync.  The edit amount is
1179            // the init_delay.
1180            int64_t edit_amt = job->config.h264.init_delay;
1181            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1182                            MP4GetTrackDuration(m->file, 1), 0);
1183             if ( m->job->chapter_markers )
1184             {
1185                 // apply same edit to chapter track to keep it in sync with video
1186                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1187                                 edit_amt,
1188                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1189             }
1190      }
1191
1192     /*
1193      * Write the MP4 iTunes metadata if we have any metadata
1194      */
1195     if( title->metadata )
1196     {
1197         hb_metadata_t *md = title->metadata;
1198         const MP4Tags* tags;
1199
1200         hb_deep_log( 2, "Writing Metadata to output file...");
1201
1202         /* allocate tags structure */
1203         tags = MP4TagsAlloc();
1204         /* fetch data from MP4 file (in case it already has some data) */
1205         MP4TagsFetch( tags, m->file );
1206
1207         /* populate */
1208         if( strlen( md->name ))
1209             MP4TagsSetName( tags, md->name );
1210         if( strlen( md->artist ))
1211             MP4TagsSetArtist( tags, md->artist );
1212         if( strlen( md->composer ))
1213             MP4TagsSetComposer( tags, md->composer );
1214         if( strlen( md->comment ))
1215             MP4TagsSetComments( tags, md->comment );
1216         if( strlen( md->release_date ))
1217             MP4TagsSetReleaseDate( tags, md->release_date );
1218         if( strlen( md->album ))
1219             MP4TagsSetAlbum( tags, md->album );
1220         if( strlen( md->genre ))
1221             MP4TagsSetGenre( tags, md->genre );
1222
1223         if( md->coverart )
1224         {
1225             MP4TagArtwork art;
1226             art.data = md->coverart;
1227             art.size = md->coverart_size;
1228             art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1229             MP4TagsAddArtwork( tags, &art );
1230         }
1231
1232         /* push data to MP4 file */
1233         MP4TagsStore( tags, m->file );
1234         /* free memory associated with structure */
1235         MP4TagsFree( tags );
1236     }
1237
1238     MP4Close( m->file );
1239
1240     if ( job->mp4_optimize )
1241     {
1242         hb_log( "muxmp4: optimizing file" );
1243         char filename[1024]; memset( filename, 0, 1024 );
1244         snprintf( filename, 1024, "%s.tmp", job->file );
1245         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1246         remove( job->file );
1247         rename( filename, job->file );
1248     }
1249
1250     return 0;
1251 }
1252
1253 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1254 {
1255     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
1256     m->init      = MP4Init;
1257     m->mux       = MP4Mux;
1258     m->end       = MP4End;
1259     m->job       = job;
1260     return m;
1261 }
1262