X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fstream.c;h=24093e62a74d7bd072e99782454323683c70b007;hb=55b0015a8c50106e553bc2f48336cc2a1c495459;hp=9d0ce80713ee36d7cb16a0b299427dea6ac2b734;hpb=d9f6a569f43fe4cade4236817d4c86e5aa43d733;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/stream.c b/libhb/stream.c index 9d0ce807..24093e62 100644 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -1259,6 +1259,14 @@ int hb_stream_read( hb_stream_t * src_stream, hb_buffer_t * b ) return hb_ts_stream_decode( src_stream, b ); } +int64_t ffmpeg_initial_timestamp( hb_stream_t * stream ) +{ + AVStream *s = stream->ffmpeg_ic->streams[stream->ffmpeg_video_id]; + if ( s->nb_index_entries < 1 ) + return 0; + + return s->index_entries[0].timestamp; +} int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) { @@ -1284,7 +1292,7 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) stream->chapter = chapter_num - 1; stream->chapter_end = sum_dur; - int64_t pos = ( ( ( sum_dur - chapter->duration ) * AV_TIME_BASE ) / 90000 ); + int64_t pos = ( ( ( sum_dur - chapter->duration ) * AV_TIME_BASE ) / 90000 ) + ffmpeg_initial_timestamp( stream ); hb_deep_log( 2, "Seeking to chapter %d: starts %"PRId64", ends %"PRId64", AV pos %"PRId64, chapter_num, sum_dur - chapter->duration, sum_dur, pos); @@ -1302,7 +1310,7 @@ int hb_stream_seek_chapter( hb_stream_t * stream, int chapter_num ) // that causes the problem. since hb_stream_seek_chapter // is called before we start reading, make sure // we do a seek here. - av_seek_frame( stream->ffmpeg_ic, -1, 0LL, AVSEEK_FLAG_BACKWARD ); + av_seek_frame( stream->ffmpeg_ic, -1, ffmpeg_initial_timestamp( stream ), AVSEEK_FLAG_BACKWARD ); } return 1; } @@ -1435,6 +1443,18 @@ static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang ) sizeof( audio->config.lang.description ), "%s (%s)", strlen(lang->native_name) ? lang->native_name : lang->eng_name, codec_name ); + + if (audio->config.in.codec == HB_ACODEC_FFMPEG) + { + int layout = audio->config.in.channel_layout; + char *desc = audio->config.lang.description + + strlen( audio->config.lang.description ); + sprintf( desc, " (%d.%d ch)", + HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(layout) + + HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(layout), + HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(layout) ); + } + snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name ); snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), @@ -2771,24 +2791,19 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) { AVStream *st = stream->ffmpeg_ic->streams[id]; AVCodecContext *codec = st->codec; + int layout; // scan will ignore any audio without a bitrate. Since we've already // typed the audio in order to determine its codec we set up the audio // paramters here. + layout = hb_ff_layout_xlat( codec->channel_layout, codec->channels ); + if ( !layout ) + { + // Unsupported layout + return; + } if ( codec->bit_rate || codec->sample_rate ) { - static const int chan2layout[] = { - HB_INPUT_CH_LAYOUT_MONO, // We should allow no audio really. - HB_INPUT_CH_LAYOUT_MONO, - HB_INPUT_CH_LAYOUT_STEREO, - HB_INPUT_CH_LAYOUT_2F1R, - HB_INPUT_CH_LAYOUT_2F2R, - HB_INPUT_CH_LAYOUT_3F2R, - HB_INPUT_CH_LAYOUT_4F2R, - HB_INPUT_CH_LAYOUT_STEREO, - HB_INPUT_CH_LAYOUT_STEREO, - }; - hb_audio_t *audio = calloc( 1, sizeof(*audio) );; audio->id = id; @@ -2807,7 +2822,7 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) audio->config.in.bitrate = codec->bit_rate? codec->bit_rate : 1; audio->config.in.samplerate = codec->sample_rate; - audio->config.in.channel_layout = chan2layout[codec->channels & 7]; + audio->config.in.channel_layout = layout; } set_audio_description( audio, lang_for_code2( st->language ) ); @@ -2816,6 +2831,175 @@ static void add_ffmpeg_audio( hb_title_t *title, hb_stream_t *stream, int id ) } } +/* + * Format: + * MkvVobSubtitlePrivateData = ( Line )* + * Line = FieldName ':' ' ' FieldValue '\n' + * FieldName = [^:]+ + * FieldValue = [^\n]+ + * + * The line of interest is: + * PaletteLine = "palette" ':' ' ' RRGGBB ( ',' ' ' RRGGBB )* + * + * More information on the format at: + * http://www.matroska.org/technical/specs/subtitles/images.html + */ +static int ffmpeg_parse_vobsub_extradata_mkv( AVCodecContext *codec, hb_subtitle_t *subtitle ) +{ + // lines = (string) codec->extradata; + char *lines = malloc( codec->extradata_size + 1 ); + if ( lines == NULL ) + return 1; + memcpy( lines, codec->extradata, codec->extradata_size ); + lines[codec->extradata_size] = '\0'; + + uint32_t rgb[16]; + int gotPalette = 0; + int gotDimensions = 0; + + char *curLine, *curLine_parserData; + for ( curLine = strtok_r( lines, "\n", &curLine_parserData ); + curLine; + curLine = strtok_r( NULL, "\n", &curLine_parserData ) ) + { + if (!gotPalette) + { + int numElementsRead = sscanf(curLine, "palette: " + "%06x, %06x, %06x, %06x, " + "%06x, %06x, %06x, %06x, " + "%06x, %06x, %06x, %06x, " + "%06x, %06x, %06x, %06x", + &rgb[0], &rgb[1], &rgb[2], &rgb[3], + &rgb[4], &rgb[5], &rgb[6], &rgb[7], + &rgb[8], &rgb[9], &rgb[10], &rgb[11], + &rgb[12], &rgb[13], &rgb[14], &rgb[15]); + + if (numElementsRead == 16) { + gotPalette = 1; + } + } + if (!gotDimensions) + { + int numElementsRead = sscanf(curLine, "size: %dx%d", + &subtitle->width, &subtitle->height); + + if (numElementsRead == 2) { + gotDimensions = 1; + } + } + if (gotPalette && gotDimensions) + break; + } + + if (subtitle->width == 0 || subtitle->height == 0) + { + subtitle->width = 720; + subtitle->height = 480; + } + + free( lines ); + + if ( gotPalette ) + { + int i; + for (i=0; i<16; i++) + subtitle->palette[i] = hb_rgb2yuv(rgb[i]); + return 0; + } + else + { + return 1; + } +} + +/* + * Format: 8-bit {0,Y,Cb,Cr} x 16 + */ +static int ffmpeg_parse_vobsub_extradata_mp4( AVCodecContext *codec, hb_subtitle_t *subtitle ) +{ + if ( codec->extradata_size != 4*16 ) + return 1; + + int i, j; + for ( i=0, j=0; i<16; i++, j+=4 ) + { + subtitle->palette[i] = + codec->extradata[j+1] << 16 | // Y + codec->extradata[j+2] << 8 | // Cb + codec->extradata[j+3] << 0; // Cr + } + if (codec->width <= 0 || codec->height <= 0) + { + subtitle->width = 720; + subtitle->height = 480; + } + else + { + subtitle->width = codec->width; + subtitle->height = codec->height; + } + return 0; +} + +/* + * Parses the 'subtitle->palette' information from the specific VOB subtitle track's private data. + * Returns 0 if successful or 1 if parsing failed or was incomplete. + */ +static int ffmpeg_parse_vobsub_extradata( AVCodecContext *codec, hb_subtitle_t *subtitle ) +{ + // XXX: Better if we actually chose the correct parser based on the input container + return + ffmpeg_parse_vobsub_extradata_mkv( codec, subtitle ) && + ffmpeg_parse_vobsub_extradata_mp4( codec, subtitle ); +} + +static void add_ffmpeg_subtitle( hb_title_t *title, hb_stream_t *stream, int id ) +{ + AVStream *st = stream->ffmpeg_ic->streams[id]; + AVCodecContext *codec = st->codec; + + hb_subtitle_t *subtitle = calloc( 1, sizeof(*subtitle) ); + + subtitle->id = id; + + switch ( codec->codec_id ) + { + case CODEC_ID_DVD_SUBTITLE: + subtitle->format = PICTURESUB; + subtitle->source = VOBSUB; + subtitle->config.dest = RENDERSUB; // By default render (burn-in) the VOBSUB. + if ( ffmpeg_parse_vobsub_extradata( codec, subtitle ) ) + hb_log( "add_ffmpeg_subtitle: malformed extradata for VOB subtitle track; " + "subtitle colors likely to be wrong" ); + break; + case CODEC_ID_TEXT: + subtitle->format = TEXTSUB; + subtitle->source = UTF8SUB; + subtitle->config.dest = PASSTHRUSUB; + break; + case CODEC_ID_MOV_TEXT: // TX3G + subtitle->format = TEXTSUB; + subtitle->source = TX3GSUB; + subtitle->config.dest = PASSTHRUSUB; + break; + case CODEC_ID_SSA: + subtitle->format = TEXTSUB; + subtitle->source = SSASUB; + subtitle->config.dest = PASSTHRUSUB; + break; + default: + hb_log( "add_ffmpeg_subtitle: unknown subtitle stream type: 0x%x", (int) codec->codec_id ); + free(subtitle); + return; + } + + iso639_lang_t *language = lang_for_code2( st->language ); + strcpy( subtitle->lang, language->eng_name ); + strncpy( subtitle->iso639_2, language->iso639_2, 4 ); + + hb_list_add(title->list_subtitle, subtitle); +} + static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream ) { AVFormatContext *ic = stream->ffmpeg_ic; @@ -2850,6 +3034,13 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream ) avcodec_find_decoder( ic->streams[i]->codec->codec_id ) && title->video_codec == 0 ) { + AVCodecContext *context = ic->streams[i]->codec; + if ( context->pix_fmt != PIX_FMT_YUV420P && + !sws_isSupportedInput( context->pix_fmt ) ) + { + hb_log( "ffmpeg_title_scan: Unsupported color space" ); + continue; + } title->video_id = i; stream->ffmpeg_video_id = i; @@ -2865,6 +3056,10 @@ static hb_title_t *ffmpeg_title_scan( hb_stream_t *stream ) { add_ffmpeg_audio( title, stream, i ); } + else if ( ic->streams[i]->codec->codec_type == CODEC_TYPE_SUBTITLE ) + { + add_ffmpeg_subtitle( title, stream, i ); + } } title->container_name = strdup( ic->iformat->name ); @@ -3052,6 +3247,34 @@ static int ffmpeg_read( hb_stream_t *stream, hb_buffer_t *buf ) { buf->renderOffset = buf->start; } + + /* + * Fill out buf->stop for subtitle packets + * + * libavcodec's MKV demuxer stores the duration of UTF-8 subtitles (CODEC_ID_TEXT) + * in the 'convergence_duration' field for some reason. + * + * Other subtitles' durations are stored in the 'duration' field. + * + * VOB subtitles (CODEC_ID_DVD_SUBTITLE) do not have their duration stored in + * either field. This is not a problem because the VOB decoder can extract this + * information from the packet payload itself. + * + * SSA subtitles (CODEC_ID_SSA) do not have their duration stored in + * either field. This is not a problem because the SSA decoder can extract this + * information from the packet payload itself. + */ + enum CodecID ffmpeg_pkt_codec = stream->ffmpeg_ic->streams[stream->ffmpeg_pkt->stream_index]->codec->codec_id; + if ( ffmpeg_pkt_codec == CODEC_ID_TEXT ) { + int64_t ffmpeg_pkt_duration = stream->ffmpeg_pkt->convergence_duration; + int64_t buf_duration = av_to_hb_pts( ffmpeg_pkt_duration, tsconv ); + buf->stop = buf->start + buf_duration; + } + if ( ffmpeg_pkt_codec == CODEC_ID_MOV_TEXT ) { + int64_t ffmpeg_pkt_duration = stream->ffmpeg_pkt->duration; + int64_t buf_duration = av_to_hb_pts( ffmpeg_pkt_duration, tsconv ); + buf->stop = buf->start + buf_duration; + } /* * Check to see whether this video buffer is on a chapter @@ -3109,7 +3332,7 @@ static int ffmpeg_seek_ts( hb_stream_t *stream, int64_t ts ) AVFormatContext *ic = stream->ffmpeg_ic; int64_t pos; - pos = ts * AV_TIME_BASE / 90000; + pos = ts * AV_TIME_BASE / 90000 + ffmpeg_initial_timestamp( stream ); stream->need_keyframe = 1; // Seek to the nearest timestamp before that requested where // there is an I-frame