OSDN Git Service

LinGui: make Help->Guide work on windows/mingw
[handbrake-jp/handbrake-jp-git.git] / libhb / stream.c
index d2d2a4a..24093e6 100644 (file)
@@ -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;
 }
@@ -2823,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;
@@ -2857,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;
 
@@ -2872,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 );
@@ -3059,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
@@ -3116,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