X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fmuxmp4.c;h=1264f9b56b8824e58ba005916fd7004fa58a04a9;hb=033e32de9c380f54c7d1362a3979da205ebc3a29;hp=0f06deb154a483da241d964198e7b27a9d5741b6;hpb=ab3f631985a3436179492fc3d4908107c8c63197;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c index 0f06deb1..1264f9b5 100644 --- a/libhb/muxmp4.c +++ b/libhb/muxmp4.c @@ -23,8 +23,6 @@ struct hb_mux_object_s // bias to keep render offsets in ctts atom positive (set up by encx264) int64_t init_delay; - uint64_t sum_sub_duration; // sum of subtitle frame durations so far - /* Chapter state information for muxing */ MP4TrackId chapter_track; int current_chapter; @@ -33,9 +31,11 @@ struct hb_mux_object_s struct hb_mux_data_s { - MP4TrackId track; - uint8_t subtitle; - int sub_format; + MP4TrackId track; + uint8_t subtitle; + int sub_format; + + uint64_t sum_dur; // sum of the frame durations so far }; /* Tune video track chunk duration. @@ -60,7 +60,7 @@ static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId return 0; } - hb_deep_log( 2, "muxmp4: track %u, chunk duration %llu", MP4FindTrackIndex( m->file, trackId ), dur ); + hb_deep_log( 2, "muxmp4: track %u, chunk duration %"PRIu64, MP4FindTrackIndex( m->file, trackId ), dur ); return 1; } @@ -77,6 +77,7 @@ static int MP4Init( hb_mux_object_t * m ) hb_audio_t * audio; hb_mux_data_t * mux_data; int i; + int subtitle_default; /* Flags for enabling/disabling tracks in an MP4. */ typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8} track_header_flags; @@ -403,18 +404,41 @@ static int MP4Init( hb_mux_object_t * m ) } + // Quicktime requires that at least one subtitle is enabled, + // else it doesn't show any of the subtitles. + // So check to see if any of the subtitles are flagged to be + // the defualt. The default will the the enabled track, else + // enable the first track. + subtitle_default = 0; + for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) + { + hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i ); + + if( subtitle && subtitle->format == TEXTSUB && + subtitle->config.dest == PASSTHRUSUB ) + { + if ( subtitle->config.default_track ) + subtitle_default = 1; + } + } for( i = 0; i < hb_list_count( job->list_subtitle ); i++ ) { hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i ); if( subtitle && subtitle->format == TEXTSUB && - subtitle->dest == PASSTHRUSUB ) + subtitle->config.dest == PASSTHRUSUB ) { + uint64_t width, height = 60; + if( job->anamorphic.mode ) + width = job->width * ( (float) job->anamorphic.par_width / job->anamorphic.par_height ); + else + width = job->width; + mux_data = calloc(1, sizeof( hb_mux_data_t ) ); subtitle->mux_data = mux_data; mux_data->subtitle = 1; mux_data->sub_format = subtitle->format; - mux_data->track = MP4AddSubtitleTrack( m->file, 1 ); + mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height ); MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2); @@ -422,21 +446,17 @@ static int MP4Init( hb_mux_object_t * m ) MP4TuneTrackDurationPerChunk( m, mux_data->track ); const uint8_t textColor[4] = { 255,255,255,255 }; - uint64_t subHeight = 60; MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2); - MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width); - MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.height", subHeight); - MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1); MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1); - MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 0); + MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255); MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255); - MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", subHeight); - MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", job->width); + MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", height); + MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", width); MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1); MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24); @@ -455,7 +475,7 @@ static int MP4Init( hb_mux_object_t * m ) MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size); memcpy(nval, val, size); - const uint32_t ytranslation = (job->height - subHeight) * 0x10000; + const uint32_t ytranslation = (job->height - height) * 0x10000; #ifdef WORDS_BIGENDIAN ptr32[7] = ytranslation; @@ -466,6 +486,15 @@ static int MP4Init( hb_mux_object_t * m ) #endif MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size); + if ( !subtitle_default || subtitle->config.default_track ) { + /* Enable the default subtitle track */ + MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE)); + subtitle_default = 1; + } + else + { + MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE)); + } } } @@ -501,6 +530,244 @@ static int MP4Init( hb_mux_object_t * m ) return 0; } +typedef struct stylerecord_s { + enum style_s {ITALIC, BOLD, UNDERLINE} style; + uint16_t start; + uint16_t stop; + struct stylerecord_s *next; +} stylerecord; + +static void hb_makestylerecord( stylerecord **stack, + enum style_s style, int start ) +{ + stylerecord *record = calloc( sizeof( stylerecord ), 1 ); + + if( record ) + { + record->style = style; + record->start = start; + record->next = *stack; + *stack = record; + } +} + +static void hb_makestyleatom( stylerecord *record, uint8_t *style) +{ + uint8_t face = 1; + hb_deep_log(3, "Made style '%s' from %d to %d", + record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop); + + switch( record->style ) + { + case ITALIC: + face = 2; + break; + case BOLD: + face = 1; + break; + case UNDERLINE: + face = 4; + break; + default: + face = 2; + break; + } + + style[0] = (record->start >> 8) & 0xff; // startChar + style[1] = record->start & 0xff; + style[2] = (record->stop >> 8) & 0xff; // endChar + style[3] = record->stop & 0xff; + style[4] = (1 >> 8) & 0xff; // font-ID + style[5] = 1 & 0xff; + style[6] = face; // face-style-flags: 1 bold; 2 italic; 4 underline + style[7] = 24; // font-size + style[8] = 255; // r + style[9] = 255; // g + style[10] = 255; // b + style[11] = 255; // a + +} + +/* + * Copy the input to output removing markup and adding markup to the style + * atom where appropriate. + */ +static void hb_muxmp4_process_subtitle_style( uint8_t *input, + uint8_t *output, + uint8_t *style, uint16_t *stylesize ) +{ + uint8_t *reader = input; + uint8_t *writer = output; + uint8_t stylecount = 0; + uint16_t utf8_count = 0; // utf8 count from start of subtitle + stylerecord *stylestack = NULL; + stylerecord *oldrecord = NULL; + + while(*reader != '\0') { + if( ( *reader & 0xc0 ) == 0x80 ) + { + /* + * Track the utf8_count when doing markup so that we get the tx3g stops + * based on UTF8 chr counts rather than bytes. + */ + utf8_count++; + hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far", + utf8_count); + } + if (*reader == '<') { + /* + * possible markup, peek at the next chr + */ + switch(*(reader+1)) { + case 'i': + if (*(reader+2) == '>') { + reader += 3; + hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count)); + } else { + *writer++ = *reader++; + } + break; + case 'b': + if (*(reader+2) == '>') { + reader += 3; + hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count)); + } else { + *writer++ = *reader++; + } + break; + case 'u': + if (*(reader+2) == '>') { + reader += 3; + hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count)); + } else { + *writer++ = *reader++; + } + break; + case '/': + switch(*(reader+2)) { + case 'i': + if (*(reader+3) == '>') { + /* + * Check whether we then immediately start more markup of the same type, if so then + * lets not close it now and instead continue this markup. + */ + if ((*(reader+4) && *(reader+4) == '<') && + (*(reader+5) && *(reader+5) == 'i') && + (*(reader+6) && *(reader+6) == '>')) { + /* + * Opening italics right after, so don't close off these italics. + */ + hb_deep_log(3, "Joining two sets of italics"); + reader += (4 + 3); + continue; + } + + + if ((*(reader+4) && *(reader+4) == ' ') && + (*(reader+5) && *(reader+5) == '<') && + (*(reader+6) && *(reader+6) == 'i') && + (*(reader+7) && *(reader+7) == '>')) { + /* + * Opening italics right after, so don't close off these italics. + */ + hb_deep_log(3, "Joining two sets of italics (plus space)"); + reader += (4 + 4); + *writer++ = ' '; + continue; + } + if (stylestack && stylestack->style == ITALIC) { + uint8_t style_record[12]; + stylestack->stop = writer - output - utf8_count; + hb_makestyleatom(stylestack, style_record); + + memcpy(style + 10 + (12 * stylecount), style_record, 12); + stylecount++; + + oldrecord = stylestack; + stylestack = stylestack->next; + free(oldrecord); + } else { + hb_error("Mismatched Subtitle markup '%s'", input); + } + reader += 4; + } else { + *writer++ = *reader++; + } + break; + case 'b': + if (*(reader+3) == '>') { + if (stylestack && stylestack->style == BOLD) { + uint8_t style_record[12]; + stylestack->stop = writer - output - utf8_count; + hb_makestyleatom(stylestack, style_record); + + memcpy(style + 10 + (12 * stylecount), style_record, 12); + stylecount++; + oldrecord = stylestack; + stylestack = stylestack->next; + free(oldrecord); + } else { + hb_error("Mismatched Subtitle markup '%s'", input); + } + + reader += 4; + } else { + *writer++ = *reader++; + } + break; + case 'u': + if (*(reader+3) == '>') { + if (stylestack && stylestack->style == UNDERLINE) { + uint8_t style_record[12]; + stylestack->stop = writer - output - utf8_count; + hb_makestyleatom(stylestack, style_record); + + memcpy(style + 10 + (12 * stylecount), style_record, 12); + stylecount++; + + oldrecord = stylestack; + stylestack = stylestack->next; + free(oldrecord); + } else { + hb_error("Mismatched Subtitle markup '%s'", input); + } + reader += 4; + } else { + *writer++ = *reader++; + } + break; + default: + *writer++ = *reader++; + break; + } + break; + default: + *writer++ = *reader++; + break; + } + } else { + *writer++ = *reader++; + } + } + *writer = '\0'; + + if( stylecount ) + { + *stylesize = 10 + ( stylecount * 12 ); + + memcpy( style + 4, "styl", 4); + + style[0] = 0; + style[1] = 0; + style[2] = (*stylesize >> 8) & 0xff; + style[3] = *stylesize & 0xff; + style[8] = (stylecount >> 8) & 0xff; + style[9] = stylecount & 0xff; + + } + +} + static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, hb_buffer_t * buf ) { @@ -519,8 +786,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, offset = buf->start + m->init_delay - m->sum_dur; if ( offset < 0 ) { - hb_log("MP4Mux: illegal render offset %lld, start %lld," - "stop %lld, sum_dur %lld", + hb_log("MP4Mux: illegal render offset %"PRId64", start %"PRId64"," + "stop %"PRId64", sum_dur %"PRId64, offset, buf->start, buf->stop, m->sum_dur ); offset = 0; } @@ -567,8 +834,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, be possible and usually indicates a bug in the upstream code. Complain in the hope that someone will go find the bug but try to fix the error so that the file will still be playable. */ - hb_log("MP4Mux: illegal duration %lld, start %lld," - "stop %lld, sum_dur %lld", + hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64"," + "stop %"PRId64", sum_dur %"PRId64, duration, buf->start, buf->stop, m->sum_dur ); /* we don't know when the next frame starts so we can't pick a valid duration for this one. we pick something "short" @@ -643,34 +910,54 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, if( mux_data->sub_format == TEXTSUB ) { /* Write an empty sample */ - if ( m->sum_sub_duration < buf->start ) + if ( mux_data->sum_dur < buf->start ) { uint8_t empty[2] = {0,0}; if( !MP4WriteSample( m->file, mux_data->track, empty, 2, - buf->start - m->sum_sub_duration, + buf->start - mux_data->sum_dur, 0, 1 )) { hb_error("Failed to write to output file, disk full?"); *job->die = 1; } - hb_log("Subtitle not due yet, adding delay of %lld", buf->start - m->sum_sub_duration); - m->sum_sub_duration += buf->start - m->sum_sub_duration; + mux_data->sum_dur += buf->start - mux_data->sum_dur; } + uint8_t styleatom[2048];; + uint16_t stylesize = 0; + uint8_t buffer[2048]; + uint16_t buffersize = 0; + uint8_t output[2048]; + + *buffer = '\0'; + + /* + * Copy the subtitle into buffer stripping markup and creating + * style atoms for them. + */ + hb_muxmp4_process_subtitle_style( buf->data, + buffer, + styleatom, &stylesize ); + + buffersize = strlen((char*)buffer); + + hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s", + (float)buf->start / 90000, buf->start, buf->stop, + (buf->stop - buf->start), buffer); /* Write the subtitle sample */ - uint8_t buffer[2048]; - memcpy( buffer + 2, buf->data, buf->size ); - buffer[0] = ( buf->size >> 8 ) & 0xff; - buffer[1] = buf->size & 0xff; + memcpy( output + 2, buffer, buffersize ); + memcpy( output + 2 + buffersize, styleatom, stylesize); + output[0] = ( buffersize >> 8 ) & 0xff; + output[1] = buffersize & 0xff; if( !MP4WriteSample( m->file, mux_data->track, - buffer, - buf->size, + output, + buffersize + stylesize + 2, buf->stop - buf->start, 0, 1 )) @@ -679,10 +966,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data, *job->die = 1; } - m->sum_sub_duration += (buf->stop - buf->start); - hb_log("MuxMP4:Sub:%lld:%lld:%lld: %s", buf->start, buf->stop, - (buf->stop - buf->start), buf->data); - hb_log("MuxMP4:Total time elapsed:%lld", m->sum_sub_duration); + mux_data->sum_dur += (buf->stop - buf->start); } } else @@ -764,13 +1048,20 @@ static int MP4End( hb_mux_object_t * m ) MP4TagsFetch( tags, m->file ); /* populate */ - MP4TagsSetName( tags, md->name ); - MP4TagsSetArtist( tags, md->artist ); - MP4TagsSetComposer( tags, md->composer ); - MP4TagsSetComments( tags, md->comment ); - MP4TagsSetReleaseDate( tags, md->release_date ); - MP4TagsSetAlbum( tags, md->album ); - MP4TagsSetGenre( tags, md->genre ); + if( strlen( md->name )) + MP4TagsSetName( tags, md->name ); + if( strlen( md->artist )) + MP4TagsSetArtist( tags, md->artist ); + if( strlen( md->composer )) + MP4TagsSetComposer( tags, md->composer ); + if( strlen( md->comment )) + MP4TagsSetComments( tags, md->comment ); + if( strlen( md->release_date )) + MP4TagsSetReleaseDate( tags, md->release_date ); + if( strlen( md->album )) + MP4TagsSetAlbum( tags, md->album ); + if( strlen( md->genre )) + MP4TagsSetGenre( tags, md->genre ); if( md->coverart ) {