OSDN Git Service

Fix the release/developer detection in configure.py
[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 %"PRIu64, 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     int subtitle_default;
81
82     /* Flags for enabling/disabling tracks in an MP4. */
83     typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8}  track_header_flags;
84
85     /* Create an empty mp4 file */
86     if (job->largeFileSize)
87     /* Use 64-bit MP4 file */
88     {
89         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, MP4_CREATE_64BIT_DATA );
90         hb_deep_log( 2, "muxmp4: using 64-bit MP4 formatting.");
91     }
92     else
93     /* Limit MP4s to less than 4 GB */
94     {
95         m->file = MP4Create( job->file, MP4_DETAILS_ERROR, 0 );
96     }
97
98     if (m->file == MP4_INVALID_FILE_HANDLE)
99     {
100         hb_error("muxmp4.c: MP4Create failed!");
101         *job->die = 1;
102         return 0;
103     }
104
105     /* Video track */
106     mux_data      = calloc(1, sizeof( hb_mux_data_t ) );
107     job->mux_data = mux_data;
108
109     if (!(MP4SetTimeScale( m->file, 90000 )))
110     {
111         hb_error("muxmp4.c: MP4SetTimeScale failed!");
112         *job->die = 1;
113         return 0;
114     }
115
116     if( job->vcodec == HB_VCODEC_X264 )
117     {
118         /* Stolen from mp4creator */
119         MP4SetVideoProfileLevel( m->file, 0x7F );
120                 mux_data->track = MP4AddH264VideoTrack( m->file, 90000,
121                         MP4_INVALID_DURATION, job->width, job->height,
122                         job->config.h264.sps[1], /* AVCProfileIndication */
123                         job->config.h264.sps[2], /* profile_compat */
124                         job->config.h264.sps[3], /* AVCLevelIndication */
125                         3 );      /* 4 bytes length before each NAL unit */
126         if ( mux_data->track == MP4_INVALID_TRACK_ID )
127         {
128             hb_error( "muxmp4.c: MP4AddH264VideoTrack failed!" );
129             *job->die = 1;
130             return 0;
131         }
132
133         /* Tune track chunk duration */
134         if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
135         {
136             return 0;
137         }
138
139         MP4AddH264SequenceParameterSet( m->file, mux_data->track,
140                 job->config.h264.sps, job->config.h264.sps_length );
141         MP4AddH264PictureParameterSet( m->file, mux_data->track,
142                 job->config.h264.pps, job->config.h264.pps_length );
143
144                 if( job->h264_level == 30 || job->ipod_atom)
145                 {
146                         hb_deep_log( 2, "muxmp4: adding iPod atom");
147                         MP4AddIPodUUID(m->file, mux_data->track);
148                 }
149
150         m->init_delay = job->config.h264.init_delay;
151     }
152     else /* FFmpeg or XviD */
153     {
154         MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 );
155         mux_data->track = MP4AddVideoTrack( m->file, 90000,
156                 MP4_INVALID_DURATION, job->width, job->height,
157                 MP4_MPEG4_VIDEO_TYPE );
158         if (mux_data->track == MP4_INVALID_TRACK_ID)
159         {
160             hb_error("muxmp4.c: MP4AddVideoTrack failed!");
161             *job->die = 1;
162             return 0;
163         }
164
165         /* Tune track chunk duration */
166         if( !MP4TuneTrackDurationPerChunk( m, mux_data->track ))
167         {
168             return 0;
169         }
170
171         /* VOL from FFmpeg or XviD */
172         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
173                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
174         {
175             hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
176             *job->die = 1;
177             return 0;
178         }
179     }
180
181     // COLR atom for color and gamma correction.
182     // Per the notes at:
183     //   http://developer.apple.com/quicktime/icefloe/dispatch019.html#colr
184     //   http://forum.doom9.org/showthread.php?t=133982#post1090068
185     // the user can set it from job->color_matrix, otherwise by default
186     // we say anything that's likely to be HD content is ITU BT.709 and
187     // DVD, SD TV & other content is ITU BT.601.  We look at the title height
188     // rather than the job height here to get uncropped input dimensions.
189     if( job->color_matrix == 1 )
190     {
191         // ITU BT.601 DVD or SD TV content
192         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
193     }
194     else if( job->color_matrix == 2 )
195     {
196         // ITU BT.709 HD content
197         MP4AddColr(m->file, mux_data->track, 1, 1, 1);        
198     }
199     else if ( job->title->width >= 1280 || job->title->height >= 720 )
200     {
201         // we guess that 720p or above is ITU BT.709 HD content
202         MP4AddColr(m->file, mux_data->track, 1, 1, 1);
203     }
204     else
205     {
206         // ITU BT.601 DVD or SD TV content
207         MP4AddColr(m->file, mux_data->track, 6, 1, 6);
208     }
209
210     if( job->anamorphic.mode )
211     {
212         /* PASP atom for anamorphic video */
213         float width, height;
214
215         width  = job->anamorphic.par_width;
216
217         height = job->anamorphic.par_height;
218
219         MP4AddPixelAspectRatio(m->file, mux_data->track, (uint32_t)width, (uint32_t)height);
220
221         MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width * (width / height));
222     }
223
224         /* add the audio tracks */
225     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
226     {
227         audio = hb_list_item( title->list_audio, i );
228         mux_data = calloc(1, sizeof( hb_mux_data_t ) );
229         audio->priv.mux_data = mux_data;
230
231         if( audio->config.out.codec == HB_ACODEC_AC3 )
232         {
233             uint8_t fscod = 0;
234             uint8_t bsid = audio->config.in.version;
235             uint8_t bsmod = audio->config.in.mode;
236             uint8_t acmod = audio->config.flags.ac3 & 0x7;
237             uint8_t lfeon = (audio->config.flags.ac3 & A52_LFE) ? 1 : 0;
238             uint8_t bit_rate_code = 0;
239
240             /*
241              * Rewrite AC3 information into correct format for dac3 atom
242              */
243             switch( audio->config.in.samplerate )
244             {
245             case 48000:
246                 fscod = 0;
247                 break;
248             case 44100:
249                 fscod = 1;
250                 break;
251             case 32000:
252                 fscod = 2;
253                 break;
254             default:
255                 /*
256                  * Error value, tells decoder to not decode this audio.
257                  */
258                 fscod = 3;
259                 break;
260             }
261
262             switch( audio->config.in.bitrate )
263             {
264             case 32000:
265                 bit_rate_code = 0;
266                 break;
267             case 40000:
268                 bit_rate_code = 1;
269                 break;
270             case 48000:
271                 bit_rate_code = 2;
272                 break;
273             case 56000:
274                 bit_rate_code = 3;
275                 break;
276             case 64000:
277                 bit_rate_code = 4;
278                 break;
279             case 80000:
280                 bit_rate_code = 5;
281                 break;
282             case 96000:
283                 bit_rate_code = 6;
284                 break;
285             case 112000:
286                 bit_rate_code = 7;
287                 break;
288             case 128000:
289                 bit_rate_code = 8;
290                 break;
291             case 160000:
292                 bit_rate_code = 9;
293                 break;
294             case 192000:
295                 bit_rate_code = 10;
296                 break;
297             case 224000:
298                 bit_rate_code = 11;
299                 break;
300             case 256000:
301                 bit_rate_code = 12;
302                 break;
303             case 320000:
304                 bit_rate_code = 13;
305                 break;
306             case 384000:
307                 bit_rate_code = 14;
308                 break;
309             case 448000:
310                 bit_rate_code = 15;
311                 break;
312             case 512000:
313                 bit_rate_code = 16;
314                 break;
315             case 576000:
316                 bit_rate_code = 17;
317                 break;
318             case 640000:
319                 bit_rate_code = 18;
320                 break;
321             default:
322                 hb_error("Unknown AC3 bitrate");
323                 bit_rate_code = 0;
324                 break;
325             }
326
327             mux_data->track = MP4AddAC3AudioTrack(
328                 m->file,
329                 audio->config.out.samplerate, 
330                 fscod,
331                 bsid,
332                 bsmod,
333                 acmod,
334                 lfeon,
335                 bit_rate_code);
336
337             /* Tune track chunk duration */
338             MP4TuneTrackDurationPerChunk( m, mux_data->track );
339
340             if (audio->config.out.name == NULL) {
341                 MP4SetTrackBytesProperty(
342                     m->file, mux_data->track,
343                     "udta.name.value",
344                     (const uint8_t*)"Surround", strlen("Surround"));
345             }
346             else {
347                 MP4SetTrackBytesProperty(
348                     m->file, mux_data->track,
349                     "udta.name.value",
350                     (const uint8_t*)(audio->config.out.name),
351                     strlen(audio->config.out.name));
352             }
353         } else {
354             mux_data->track = MP4AddAudioTrack(
355                 m->file,
356                 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
357
358             /* Tune track chunk duration */
359             MP4TuneTrackDurationPerChunk( m, mux_data->track );
360
361             if (audio->config.out.name == NULL) {
362                 MP4SetTrackBytesProperty(
363                     m->file, mux_data->track,
364                     "udta.name.value",
365                     (const uint8_t*)"Stereo", strlen("Stereo"));
366             }
367             else {
368                 MP4SetTrackBytesProperty(
369                     m->file, mux_data->track,
370                     "udta.name.value",
371                     (const uint8_t*)(audio->config.out.name),
372                     strlen(audio->config.out.name));
373             }
374
375             MP4SetAudioProfileLevel( m->file, 0x0F );
376             MP4SetTrackESConfiguration(
377                 m->file, mux_data->track,
378                 audio->priv.config.aac.bytes, audio->priv.config.aac.length );
379
380             /* Set the correct number of channels for this track */
381              MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
382         }
383
384         /* Set the language for this track */
385         MP4SetTrackLanguage(m->file, mux_data->track, audio->config.lang.iso639_2);
386
387         if( hb_list_count( title->list_audio ) > 1 )
388         {
389             /* Set the audio track alternate group */
390             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
391         }
392
393         if (i == 0) {
394             /* Enable the first audio track */
395             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
396         }
397         else
398             /* Disable the other audio tracks so QuickTime doesn't play
399                them all at once. */
400         {
401             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
402             hb_deep_log( 2, "muxmp4: disabled extra audio track %u", MP4FindTrackIndex( m->file, mux_data->track ));
403         }
404
405     }
406
407     // Quicktime requires that at least one subtitle is enabled,
408     // else it doesn't show any of the subtitles.
409     // So check to see if any of the subtitles are flagged to be
410     // the defualt.  The default will the the enabled track, else
411     // enable the first track.
412     subtitle_default = 0;
413     for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
414     {
415         hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
416
417         if( subtitle && subtitle->format == TEXTSUB && 
418             subtitle->config.dest == PASSTHRUSUB )
419         {
420             if ( subtitle->config.default_track )
421                 subtitle_default = 1;
422         }
423     }
424     for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
425     {
426         hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
427
428         if( subtitle && subtitle->format == TEXTSUB && 
429             subtitle->config.dest == PASSTHRUSUB )
430         {
431             uint64_t width, height = 60;
432             if( job->anamorphic.mode )
433                 width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height );
434             else
435                 width = job->width;
436
437             mux_data = calloc(1, sizeof( hb_mux_data_t ) );
438             subtitle->mux_data = mux_data;
439             mux_data->subtitle = 1;
440             mux_data->sub_format = subtitle->format;
441             mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
442
443             MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
444
445             /* Tune track chunk duration */
446             MP4TuneTrackDurationPerChunk( m, mux_data->track );
447
448             const uint8_t textColor[4] = { 255,255,255,255 };
449
450             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
451
452             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
453             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
454             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
455
456             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
457
458             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height);
459             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width);
460
461             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
462             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
463
464             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
465             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
466             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
467             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
468             
469             /* translate the track */
470             uint8_t* val;
471             uint8_t nval[36];
472             uint32_t *ptr32 = (uint32_t*) nval;
473             uint32_t size;
474
475             MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
476             memcpy(nval, val, size);
477
478             const uint32_t ytranslation = (job->height - height) * 0x10000;
479                 
480 #ifdef WORDS_BIGENDIAN
481             ptr32[7] = ytranslation;
482 #else
483             /* we need to switch the endianness, as the file format expects big endian */
484             ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) + 
485                             ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
486 #endif
487
488             MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);  
489             if ( !subtitle_default || subtitle->config.default_track ) {
490                 /* Enable the default subtitle track */
491                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
492                 subtitle_default = 1;
493             }
494             else
495             {
496                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
497             }
498         }
499     }
500
501     if (job->chapter_markers)
502     {
503         /* add a text track for the chapters. We add the 'chap' atom to track
504            one which is usually the video track & should never be disabled.
505            The Quicktime spec says it doesn't matter which media track the
506            chap atom is on but it has to be an enabled track. */
507         MP4TrackId textTrack;
508         textTrack = MP4AddChapterTextTrack(m->file, 1, 0);
509
510         m->chapter_track = textTrack;
511         m->chapter_duration = 0;
512         m->current_chapter = job->chapter_start;
513     }
514
515     /* Add encoded-by metadata listing version and build date */
516     char *tool_string;
517     tool_string = (char *)malloc(80);
518     snprintf( tool_string, 80, "HandBrake %s %i", HB_PROJECT_VERSION, HB_PROJECT_BUILD);
519
520     /* allocate,fetch,populate,store,free tags structure */
521     const MP4Tags* tags;
522     tags = MP4TagsAlloc();
523     MP4TagsFetch( tags, m->file );
524     MP4TagsSetEncodingTool( tags, tool_string );
525     MP4TagsStore( tags, m->file );
526     MP4TagsFree( tags );
527
528     free(tool_string);
529
530     return 0;
531 }
532
533 typedef struct stylerecord_s {
534     enum style_s {ITALIC, BOLD, UNDERLINE} style;
535     uint16_t start;
536     uint16_t stop;
537     struct stylerecord_s *next;
538 } stylerecord;
539
540 static void hb_makestylerecord( stylerecord **stack, 
541                                 enum style_s style, int start )
542 {
543     stylerecord *record = calloc( sizeof( stylerecord ), 1 );
544
545     if( record ) 
546     {
547         record->style = style;
548         record->start = start;
549         record->next = *stack;
550         *stack = record;
551     }
552 }
553
554 static void hb_makestyleatom( stylerecord *record, uint8_t *style)
555 {
556     uint8_t face = 1;
557     hb_deep_log(3, "Made style '%s' from %d to %d", 
558            record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
559     
560     switch( record->style )
561     {
562     case ITALIC:
563         face = 2;
564         break;
565     case BOLD:
566         face = 1;
567         break;
568     case UNDERLINE:
569         face = 4;
570         break;
571     default:
572         face = 2;
573         break;
574     }
575
576     style[0] = (record->start >> 8) & 0xff; // startChar
577     style[1] = record->start & 0xff;
578     style[2] = (record->stop >> 8) & 0xff;   // endChar
579     style[3] = record->stop & 0xff;
580     style[4] = (1 >> 8) & 0xff;    // font-ID
581     style[5] = 1 & 0xff;
582     style[6] = face;   // face-style-flags: 1 bold; 2 italic; 4 underline
583     style[7] = 24;      // font-size
584     style[8] = 255;     // r
585     style[9] = 255;     // g
586     style[10] = 255;    // b
587     style[11] = 255;    // a
588  
589 }
590
591 /*
592  * Copy the input to output removing markup and adding markup to the style
593  * atom where appropriate.
594  */
595 static void hb_muxmp4_process_subtitle_style( uint8_t *input,
596                                               uint8_t *output,
597                                               uint8_t *style, uint16_t *stylesize )
598 {
599     uint8_t *reader = input;
600     uint8_t *writer = output;
601     uint8_t stylecount = 0;
602     uint16_t utf8_count = 0;         // utf8 count from start of subtitle
603     stylerecord *stylestack = NULL;
604     stylerecord *oldrecord = NULL;
605     
606     while(*reader != '\0') {
607         if( ( *reader & 0xc0 ) == 0x80 ) 
608         {
609             /*
610              * Track the utf8_count when doing markup so that we get the tx3g stops
611              * based on UTF8 chr counts rather than bytes.
612              */
613             utf8_count++;
614             hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far", 
615                              utf8_count);
616         }
617         if (*reader == '<') {
618             /*
619              * possible markup, peek at the next chr
620              */
621             switch(*(reader+1)) {
622             case 'i':
623                 if (*(reader+2) == '>') {
624                     reader += 3;
625                     hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
626                 } else {
627                     *writer++ = *reader++;
628                 }
629                 break;
630             case 'b':
631                 if (*(reader+2) == '>') {
632                     reader += 3; 
633                     hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
634                 } else {
635                     *writer++ = *reader++;  
636                 }
637                 break;
638             case 'u': 
639                 if (*(reader+2) == '>') {
640                     reader += 3;
641                     hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
642                 } else {
643                     *writer++ = *reader++;
644                 }
645                 break;
646             case '/':
647                 switch(*(reader+2)) {
648                 case 'i':
649                     if (*(reader+3) == '>') {
650                         /*
651                          * Check whether we then immediately start more markup of the same type, if so then
652                          * lets not close it now and instead continue this markup.
653                          */
654                         if ((*(reader+4) && *(reader+4) == '<') &&
655                             (*(reader+5) && *(reader+5) == 'i') &&
656                             (*(reader+6) && *(reader+6) == '>')) {
657                             /*
658                              * Opening italics right after, so don't close off these italics.
659                              */
660                             hb_deep_log(3, "Joining two sets of italics");
661                             reader += (4 + 3);
662                             continue;
663                         }
664
665
666                         if ((*(reader+4) && *(reader+4) == ' ') && 
667                             (*(reader+5) && *(reader+5) == '<') &&
668                             (*(reader+6) && *(reader+6) == 'i') &&
669                             (*(reader+7) && *(reader+7) == '>')) {
670                             /*
671                              * Opening italics right after, so don't close off these italics.
672                              */
673                             hb_deep_log(3, "Joining two sets of italics (plus space)");
674                             reader += (4 + 4);
675                             *writer++ = ' ';
676                             continue;
677                         }
678                         if (stylestack && stylestack->style == ITALIC) {
679                             uint8_t style_record[12];
680                             stylestack->stop = writer - output - utf8_count;
681                             hb_makestyleatom(stylestack, style_record);
682
683                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
684                             stylecount++;
685
686                             oldrecord = stylestack;
687                             stylestack = stylestack->next;
688                             free(oldrecord);
689                         } else {
690                             hb_error("Mismatched Subtitle markup '%s'", input);
691                         }
692                         reader += 4;
693                     } else {
694                         *writer++ = *reader++;
695                     }
696                     break;
697                 case 'b':
698                     if (*(reader+3) == '>') {
699                         if (stylestack && stylestack->style == BOLD) {
700                             uint8_t style_record[12];
701                             stylestack->stop = writer - output - utf8_count;
702                             hb_makestyleatom(stylestack, style_record);
703
704                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
705                             stylecount++;
706                             oldrecord = stylestack;
707                             stylestack = stylestack->next;
708                             free(oldrecord);
709                         } else {
710                             hb_error("Mismatched Subtitle markup '%s'", input);
711                         }
712
713                         reader += 4;
714                     } else {
715                         *writer++ = *reader++;
716                     }
717                     break;
718                 case 'u': 
719                     if (*(reader+3) == '>') {
720                         if (stylestack && stylestack->style == UNDERLINE) {
721                             uint8_t style_record[12];
722                             stylestack->stop = writer - output - utf8_count;
723                             hb_makestyleatom(stylestack, style_record);
724
725                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
726                             stylecount++;
727
728                             oldrecord = stylestack;
729                             stylestack = stylestack->next;
730                             free(oldrecord);
731                         } else {
732                             hb_error("Mismatched Subtitle markup '%s'", input);
733                         }
734                         reader += 4;
735                     } else {
736                         *writer++ = *reader++;
737                     }
738                     break;
739                 default:
740                     *writer++ = *reader++;
741                     break;
742                 }
743                 break;
744             default:
745                 *writer++ = *reader++;
746                 break;
747             }
748         } else {
749             *writer++ = *reader++;
750         }
751     }
752     *writer = '\0';
753
754     if( stylecount )
755     {
756         *stylesize = 10 + ( stylecount * 12 );
757
758         memcpy( style + 4, "styl", 4);
759
760         style[0] = 0;
761         style[1] = 0;
762         style[2] = (*stylesize >> 8) & 0xff;
763         style[3] = *stylesize & 0xff;
764         style[8] = (stylecount >> 8) & 0xff;
765         style[9] = stylecount & 0xff;
766
767     }
768
769 }
770
771 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
772                    hb_buffer_t * buf )
773 {
774     hb_job_t * job = m->job;
775     int64_t duration;
776     int64_t offset = 0;
777
778     if( mux_data == job->mux_data )
779     {
780         /* Video */
781
782         // if there are b-frames compute the render offset
783         // (we'll need it for both the video frame & the chapter track)
784         if ( m->init_delay )
785         {
786             offset = buf->start + m->init_delay - m->sum_dur;
787             if ( offset < 0 )
788             {
789                 hb_log("MP4Mux: illegal render offset %"PRId64", start %"PRId64","
790                        "stop %"PRId64", sum_dur %"PRId64,
791                        offset, buf->start, buf->stop, m->sum_dur );
792                 offset = 0;
793             }
794         }
795
796         /* Add the sample before the new frame.
797            It is important that this be calculated prior to the duration
798            of the new video sample, as we want to sync to right after it.
799            (This is because of how durations for text tracks work in QT) */
800         if( job->chapter_markers && buf->new_chap )
801         {    
802             hb_chapter_t *chapter = NULL;
803
804             // this chapter is postioned by writing out the previous chapter.
805             // the duration of the previous chapter is the duration up to but
806             // not including the current frame minus the duration of all
807             // chapters up to the previous.
808             // The initial and final chapters can be very short (a second or
809             // less) since they're not really chapters but just a placeholder to
810             // insert a cell command. We don't write chapters shorter than 1.5 sec.
811             duration = m->sum_dur - m->chapter_duration + offset;
812             if ( duration >= (90000*3)/2 )
813             {
814                 chapter = hb_list_item( m->job->title->list_chapter,
815                                         buf->new_chap - 2 );
816
817                 MP4AddChapter( m->file,
818                                m->chapter_track,
819                                duration,
820                                (chapter != NULL) ? chapter->title : NULL);
821
822                 m->current_chapter = buf->new_chap;
823                 m->chapter_duration += duration;
824             }
825         }
826
827         // We're getting the frames in decode order but the timestamps are
828         // for presentation so we have to use durations and effectively
829         // compute a DTS.
830         duration = buf->stop - buf->start;
831         if ( duration <= 0 )
832         {
833             /* We got an illegal mp4/h264 duration. This shouldn't
834                be possible and usually indicates a bug in the upstream code.
835                Complain in the hope that someone will go find the bug but
836                try to fix the error so that the file will still be playable. */
837             hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
838                    "stop %"PRId64", sum_dur %"PRId64,
839                    duration, buf->start, buf->stop, m->sum_dur );
840             /* we don't know when the next frame starts so we can't pick a
841                valid duration for this one. we pick something "short"
842                (roughly 1/3 of an NTSC frame time) to take time from
843                the next frame. */
844             duration = 1000;
845         }
846         m->sum_dur += duration;
847     }
848     else
849     {
850         /* Audio */
851         duration = MP4_INVALID_DURATION;
852     }
853
854     /* Here's where the sample actually gets muxed. */
855     if( job->vcodec == HB_VCODEC_X264 && mux_data == job->mux_data )
856     {
857         /* Compute dependency flags.
858          *
859          * This mechanism is (optionally) used by media players such as QuickTime
860          * to offer better scrubbing performance. The most influential bits are
861          * MP4_SDT_HAS_NO_DEPENDENTS and MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED.
862          *
863          * Other bits are possible but no example media using such bits have been
864          * found.
865          *
866          * It is acceptable to supply 0-bits for any samples which characteristics
867          * cannot be positively guaranteed.
868          */
869         int sync = 0;
870         uint32_t dflags = 0;
871
872         /* encoding layer signals if frame is referenced by other frames */
873         if( buf->flags & HB_FRAME_REF )
874             dflags |= MP4_SDT_HAS_DEPENDENTS;
875         else
876             dflags |= MP4_SDT_HAS_NO_DEPENDENTS; /* disposable */
877
878         switch( buf->frametype )
879         {
880             case HB_FRAME_IDR:
881                 sync = 1;
882                 break;
883             case HB_FRAME_I:
884                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
885                 break;
886             case HB_FRAME_P:
887                 dflags |= MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED;
888                 break;
889             case HB_FRAME_BREF:
890             case HB_FRAME_B:
891             default:
892                 break; /* nothing to mark */
893         }
894
895         if( !MP4WriteSampleDependency( m->file,
896                                        mux_data->track,
897                                        buf->data,
898                                        buf->size,
899                                        duration,
900                                        offset,
901                                        sync,
902                                        dflags ))
903         {
904             hb_error("Failed to write to output file, disk full?");
905             *job->die = 1;
906         }
907     }
908     else if (mux_data->subtitle)
909     {
910         if( mux_data->sub_format == TEXTSUB )
911         {
912             /* Write an empty sample */
913             if ( mux_data->sum_dur < buf->start )
914             {
915                 uint8_t empty[2] = {0,0};
916                 if( !MP4WriteSample( m->file,
917                                     mux_data->track,
918                                     empty,
919                                     2,
920                                     buf->start - mux_data->sum_dur,
921                                     0,
922                                     1 ))
923                 {
924                     hb_error("Failed to write to output file, disk full?");
925                     *job->die = 1;
926                 } 
927                 mux_data->sum_dur += buf->start - mux_data->sum_dur;
928             }
929             uint8_t styleatom[2048];;
930             uint16_t stylesize = 0;
931             uint8_t buffer[2048];
932             uint16_t buffersize = 0;
933             uint8_t output[2048];
934
935             *buffer = '\0';
936
937             /*
938              * Copy the subtitle into buffer stripping markup and creating
939              * style atoms for them.
940              */
941             hb_muxmp4_process_subtitle_style( buf->data,
942                                               buffer,
943                                               styleatom, &stylesize );
944
945             buffersize = strlen((char*)buffer);
946
947             hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
948                         (float)buf->start / 90000, buf->start, buf->stop, 
949                         (buf->stop - buf->start), buffer);
950
951             /* Write the subtitle sample */
952             memcpy( output + 2, buffer, buffersize );
953             memcpy( output + 2 + buffersize, styleatom, stylesize);
954             output[0] = ( buffersize >> 8 ) & 0xff;
955             output[1] = buffersize & 0xff;
956
957             if( !MP4WriteSample( m->file,
958                                  mux_data->track,
959                                  output,
960                                  buffersize + stylesize + 2,
961                                  buf->stop - buf->start,
962                                  0,
963                                  1 ))
964             {
965                 hb_error("Failed to write to output file, disk full?");
966                 *job->die = 1;
967             }
968
969             mux_data->sum_dur += (buf->stop - buf->start);
970         }
971     }
972     else
973     {
974         /*
975          * Audio
976          */
977         if( !MP4WriteSample( m->file,
978                              mux_data->track,
979                              buf->data,
980                              buf->size,
981                              duration,
982                              offset,
983                              ( buf->frametype & HB_FRAME_KEY ) != 0 ))
984         {
985             hb_error("Failed to write to output file, disk full?");
986             *job->die = 1;
987         }
988     }
989
990
991     return 0;
992 }
993
994 static int MP4End( hb_mux_object_t * m )
995 {
996     hb_job_t   * job   = m->job;
997     hb_title_t * title = job->title;
998
999     /* Write our final chapter marker */
1000     if( m->job->chapter_markers )
1001     {
1002         hb_chapter_t *chapter = NULL;
1003         int64_t duration = m->sum_dur - m->chapter_duration;
1004         /* The final chapter can have a very short duration - if it's less
1005          * than 1.5 seconds just skip it. */
1006         if ( duration >= (90000*3)/2 )
1007         {
1008
1009             chapter = hb_list_item( m->job->title->list_chapter,
1010                                     m->current_chapter - 1 );
1011
1012             MP4AddChapter( m->file,
1013                            m->chapter_track,
1014                            duration,
1015                            (chapter != NULL) ? chapter->title : NULL);
1016         }
1017     }
1018
1019     if (job->areBframes)
1020     {
1021            // Insert track edit to get A/V back in sync.  The edit amount is
1022            // the init_delay.
1023            int64_t edit_amt = m->init_delay;
1024            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
1025                            MP4GetTrackDuration(m->file, 1), 0);
1026             if ( m->job->chapter_markers )
1027             {
1028                 // apply same edit to chapter track to keep it in sync with video
1029                 MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
1030                                 edit_amt,
1031                                 MP4GetTrackDuration(m->file, m->chapter_track), 0);
1032             }
1033      }
1034
1035     /*
1036      * Write the MP4 iTunes metadata if we have any metadata
1037      */
1038     if( title->metadata )
1039     {
1040         hb_metadata_t *md = title->metadata;
1041         const MP4Tags* tags;
1042
1043         hb_deep_log( 2, "Writing Metadata to output file...");
1044
1045         /* allocate tags structure */
1046         tags = MP4TagsAlloc();
1047         /* fetch data from MP4 file (in case it already has some data) */
1048         MP4TagsFetch( tags, m->file );
1049
1050         /* populate */
1051         if( strlen( md->name ))
1052             MP4TagsSetName( tags, md->name );
1053         if( strlen( md->artist ))
1054             MP4TagsSetArtist( tags, md->artist );
1055         if( strlen( md->composer ))
1056             MP4TagsSetComposer( tags, md->composer );
1057         if( strlen( md->comment ))
1058             MP4TagsSetComments( tags, md->comment );
1059         if( strlen( md->release_date ))
1060             MP4TagsSetReleaseDate( tags, md->release_date );
1061         if( strlen( md->album ))
1062             MP4TagsSetAlbum( tags, md->album );
1063         if( strlen( md->genre ))
1064             MP4TagsSetGenre( tags, md->genre );
1065
1066         if( md->coverart )
1067         {
1068             MP4TagArtwork art;
1069             art.data = md->coverart;
1070             art.size = md->coverart_size;
1071             art.type = MP4_ART_UNDEFINED; // delegate typing to libmp4v2
1072             MP4TagsAddArtwork( tags, &art );
1073         }
1074
1075         /* push data to MP4 file */
1076         MP4TagsStore( tags, m->file );
1077         /* free memory associated with structure */
1078         MP4TagsFree( tags );
1079     }
1080
1081     MP4Close( m->file );
1082
1083     if ( job->mp4_optimize )
1084     {
1085         hb_log( "muxmp4: optimizing file" );
1086         char filename[1024]; memset( filename, 0, 1024 );
1087         snprintf( filename, 1024, "%s.tmp", job->file );
1088         MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
1089         remove( job->file );
1090         rename( filename, job->file );
1091     }
1092
1093     return 0;
1094 }
1095
1096 hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
1097 {
1098     hb_mux_object_t * m = calloc( sizeof( hb_mux_object_t ), 1 );
1099     m->init      = MP4Init;
1100     m->mux       = MP4Mux;
1101     m->end       = MP4End;
1102     m->job       = job;
1103     return m;
1104 }
1105