OSDN Git Service

Found the ffmpeg initialization problem with VC-1 video.
authorjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Wed, 3 Dec 2008 16:18:51 +0000 (16:18 +0000)
committerjstebbins <jstebbins@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Wed, 3 Dec 2008 16:18:51 +0000 (16:18 +0000)
So now we can use our own demuxer for streams with VC-1.

git-svn-id: svn://localhost/HandBrake/trunk@1995 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/decavcodec.c
libhb/stream.c

index 8298468..e335ef9 100644 (file)
@@ -673,17 +673,91 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
     pv->context->opaque = pv;
     pv->context->get_buffer = get_frame_buf;
 
-    AVCodec *codec = avcodec_find_decoder( codec_id );
+    return 0;
+}
+
+static int next_hdr( hb_buffer_t *in, int offset )
+{
+    uint8_t *dat = in->data;
+    uint16_t last2 = 0xffff;
+    for ( ; in->size - offset > 1; ++offset )
+    {
+        if ( last2 == 0 && dat[offset] == 0x01 )
+            // found an mpeg start code
+            return offset - 2;
+
+        last2 = ( last2 << 8 ) | dat[offset];
+    }
+
+    return -1;
+}
+
+static int find_hdr( hb_buffer_t *in, int offset, uint8_t hdr_type )
+{
+    if ( in->size - offset < 4 )
+        // not enough room for an mpeg start code
+        return -1;
+
+    for ( ; ( offset = next_hdr( in, offset ) ) >= 0; ++offset )
+    {
+        if ( in->data[offset+3] == hdr_type )
+            // found it
+            break;
+    }
+    return offset;
+}
+
+static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in )
+{
+    hb_work_private_t *pv = w->private_data;
 
     // we can't call the avstream funcs but the read_header func in the
     // AVInputFormat may set up some state in the AVContext. In particular 
     // vc1t_read_header allocates 'extradata' to deal with header issues
     // related to Microsoft's bizarre engineering notions. We alloc a chunk
     // of space to make vc1 work then associate the codec with the context.
-    pv->context->extradata_size = 32;
-    pv->context->extradata = av_malloc(pv->context->extradata_size);
-    avcodec_open( pv->context, codec );
+    if ( w->codec_param != CODEC_ID_VC1 )
+    {
+        // we haven't been inflicted with M$ - allocate a little space as
+        // a marker and return success.
+        pv->context->extradata_size = 16;
+        pv->context->extradata = av_malloc(pv->context->extradata_size);
+        return 0;
+    }
 
+    // find the start and and of the sequence header
+    int shdr, shdr_end;
+    if ( ( shdr = find_hdr( in, 0, 0x0f ) ) < 0 )
+    {
+        // didn't find start of seq hdr
+        return 1;
+    }
+    if ( ( shdr_end = next_hdr( in, shdr + 4 ) ) < 0 )
+    {
+        shdr_end = in->size;
+    }
+    shdr_end -= shdr;
+
+    // find the start and and of the entry point header
+    int ehdr, ehdr_end;
+    if ( ( ehdr = find_hdr( in, 0, 0x0e ) ) < 0 )
+    {
+        // didn't find start of entry point hdr
+        return 1;
+    }
+    if ( ( ehdr_end = next_hdr( in, ehdr + 4 ) ) < 0 )
+    {
+        ehdr_end = in->size;
+    }
+    ehdr_end -= ehdr;
+
+    // found both headers - allocate an extradata big enough to hold both
+    // then copy them into it.
+    pv->context->extradata_size = shdr_end + ehdr_end;
+    pv->context->extradata = av_malloc(pv->context->extradata_size + 8);
+    memcpy( pv->context->extradata, in->data + shdr, shdr_end );
+    memcpy( pv->context->extradata + shdr_end, in->data + ehdr, ehdr_end );
+    memset( pv->context->extradata + shdr_end + ehdr_end, 0, 8);
     return 0;
 }
 
@@ -706,6 +780,27 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
         return HB_WORK_DONE;
     }
 
+    // if this is the first frame open the codec (we have to wait for the
+    // first frame because of M$ VC1 braindamage).
+    if ( pv->context->extradata_size == 0 )
+    {
+        if ( setup_extradata( w, in ) )
+        {
+            // we didn't find the headers needed to set up extradata.
+            // the codec will abort if we open it so just free the buf
+            // and hope we eventually get the info we need.
+            hb_buffer_close( &in );
+            return HB_WORK_OK;
+        }
+        AVCodec *codec = avcodec_find_decoder( w->codec_param );
+        // There's a mis-feature in ffmpeg that causes the context to be 
+        // incorrectly initialized the 1st time avcodec_open is called.
+        // If you close it and open a 2nd time, it finishes the job.
+        avcodec_open( pv->context, codec );
+        avcodec_close( pv->context );
+        avcodec_open( pv->context, codec );
+    }
+
     if( in->start >= 0 )
     {
         pts = in->start;
index d6079d8..1b03ae6 100755 (executable)
@@ -1941,14 +1941,6 @@ static void hb_ts_stream_find_pids(hb_stream_t *stream)
                if ((stream->ts_number_video_pids > 0) && (stream->ts_number_audio_pids > 0))
                  break;
        }
-    // XXX - until we figure out how to handle VC1 just bail when we find it so
-    // that ffmpeg will claim the input stream.
-    if ( stream->ts_stream_type[0] == 0xea )
-    {
-        stream->ts_number_video_pids = 0;
-        stream->ts_number_audio_pids = 0;
-        return;
-    }
 
        hb_log("hb_ts_stream_find_pids - found the following PIDS");
        hb_log("    Video PIDS : ");
@@ -2134,6 +2126,22 @@ static int isIframe( hb_stream_t *stream, const uint8_t *buf, int adapt_len )
         // didn't find an I-frame
         return 0;
     }
+    if ( stream->ts_stream_type[0] == 0xea )
+    {
+        // we have an vc1 stream 
+        for (i = 13 + adapt_len; i < 188; i++)
+        {
+            strid = (strid << 8) | buf[i];
+            if ( strid == 0x10f )
+            {
+                // the ffmpeg vc1 decoder requires a seq hdr code in the first
+                // frame.
+                return 1;
+            }
+        }
+        // didn't find an I-frame
+        return 0;
+    }
 
     // we don't understand the stream type so just say "yes" otherwise
     // we'll discard all the video.