]> granicus.if.org Git - handbrake/commitdiff
decavcodec: packetize data before searching for extradata
authorJohn Stebbins <jstebbins.hb@gmail.com>
Thu, 11 Oct 2018 18:24:06 +0000 (11:24 -0700)
committerJohn Stebbins <jstebbins.hb@gmail.com>
Tue, 6 Nov 2018 15:25:35 +0000 (07:25 -0800)
The necessary data for setting up extradata may be scattered across
multiple input buffers.  The buffers must be combined before extradata
can be extracted.

libhb/decavcodec.c

index 16c4e31b8d6713c3fa4ed41eb580b393a6a42f72..499de97315ab33719d5a1ffa206c5a2773d97f6c 100644 (file)
@@ -112,6 +112,7 @@ struct hb_work_private_s
 {
     hb_job_t             * job;
     hb_title_t           * title;
+    AVCodec              * codec;
     AVCodecContext       * context;
     AVCodecParserContext * parser;
     AVFrame              * frame;
@@ -1349,9 +1350,8 @@ static void filter_video(hb_work_private_t *pv)
  * The output of this function is stored in 'pv->list', which contains a list
  * of zero or more decoded packets.
  */
-static int decodeFrame( hb_work_object_t *w, packet_info_t * packet_info )
+static int decodeFrame( hb_work_private_t * pv, packet_info_t * packet_info )
 {
-    hb_work_private_t *pv = w->private_data;
     int got_picture = 0, oldlevel = 0, ret;
     AVPacket avp;
     reordered_data_t * reordered;
@@ -1449,128 +1449,6 @@ static int decodeFrame( hb_work_object_t *w, packet_info_t * packet_info )
     return got_picture;
 }
 
-static void decodeVideo( hb_work_object_t *w, hb_buffer_t * in)
-{
-    hb_work_private_t *pv = w->private_data;
-
-    /*
-     * The following loop is a do..while because we need to handle both
-     * data & the flush at the end (signaled by size=0). At the end there's
-     * generally a frame in the parser & one or more frames in the decoder
-     * (depending on the bframes setting).
-     */
-    int      pos, len;
-    int64_t  pts = in->s.start;
-    int64_t  dts = in->s.renderOffset;
-
-    if (in->s.new_chap > 0)
-    {
-        pv->new_chap = in->s.new_chap;
-        pv->chap_scr = in->s.scr_sequence;
-        if (in->s.start != AV_NOPTS_VALUE)
-        {
-            pv->chap_time = in->s.start;
-        }
-        else
-        {
-            pv->chap_time = pv->last_pts + 1;
-        }
-    }
-    if (in->s.start != AV_NOPTS_VALUE)
-    {
-        pv->last_pts = in->s.start;
-    }
-
-    // There are a 3 scenarios that can happen here.
-    // 1. The buffer contains exactly one frame of data
-    // 2. The buffer contains multiple frames of data
-    // 3. The buffer contains a partial frame of data
-    //
-    // In scenario 2, we want to be sure that the timestamps are only
-    // applied to the first frame in the buffer.  Additional frames
-    // in the buffer will have their timestamps computed in sync.
-    //
-    // In scenario 3, we need to save the ancillary buffer info of an
-    // unfinished frame so it can be applied when we receive the last
-    // buffer of that frame.
-    if (!pv->unfinished)
-    {
-        // New packet, and no previous data pending
-        pv->packet_info.scr_sequence = in->s.scr_sequence;
-        pv->packet_info.new_chap     = in->s.new_chap;
-        pv->packet_info.frametype    = in->s.frametype;
-    }
-    for (pos = 0; pos < in->size; pos += len)
-    {
-        uint8_t * pout;
-        int       pout_len;
-        int64_t   parser_pts, parser_dts;
-
-        if (pv->parser)
-        {
-            len = av_parser_parse2(pv->parser, pv->context, &pout, &pout_len,
-                                   in->data + pos, in->size - pos,
-                                   pts, dts, 0 );
-            parser_pts = pv->parser->pts;
-            parser_dts = pv->parser->dts;
-            pts = AV_NOPTS_VALUE;
-            dts = AV_NOPTS_VALUE;
-        }
-        else
-        {
-            pout = in->data;
-            len = pout_len = in->size;
-            parser_pts = pts;
-            parser_dts = dts;
-        }
-
-        if (pout != NULL && pout_len > 0)
-        {
-            pv->packet_info.data         = pout;
-            pv->packet_info.size         = pout_len;
-            pv->packet_info.pts          = parser_pts;
-            pv->packet_info.dts          = parser_dts;
-
-            decodeFrame(w, &pv->packet_info);
-            w->frame_count++;
-
-            // There could have been an unfinished packet when we entered
-            // decodeVideo that is now finished.  The next packet is associated
-            // with the input buffer, so set it's chapter and scr info.
-            pv->packet_info.scr_sequence = in->s.scr_sequence;
-            pv->packet_info.new_chap     = in->s.new_chap;
-            pv->packet_info.frametype    = in->s.frametype;
-            pv->unfinished               = 0;
-        }
-        if (len > 0 && pout_len <= 0)
-        {
-            pv->unfinished               = 1;
-        }
-    }
-
-    /* the stuff above flushed the parser, now flush the decoder */
-    if (in->s.flags & HB_BUF_FLAG_EOF)
-    {
-        while (decodeFrame(w, NULL))
-        {
-            continue;
-        }
-#ifdef USE_QSV
-        if (pv->qsv.decode &&
-            pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY)
-        {
-            // flush a second time
-            while (decodeFrame(w, NULL))
-            {
-                continue;
-            }
-        }
-#endif
-        if (pv->list_subtitle != NULL)
-            cc_send_to_decoder(pv, hb_buffer_eof_init());
-    }
-}
-
 static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
 {
 
@@ -1618,34 +1496,33 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
         pv->threads = HB_FFMPEG_THREADS_AUTO;
     }
 
-    AVCodec *codec = NULL;
-
 #ifdef USE_QSV
     if (pv->qsv.decode)
     {
-        codec = avcodec_find_decoder_by_name(pv->qsv.codec_name);
+        pv->codec = avcodec_find_decoder_by_name(pv->qsv.codec_name);
     }
     else
 #endif
     {
-        codec = avcodec_find_decoder(w->codec_param);
+        pv->codec = avcodec_find_decoder(w->codec_param);
     }
-    if ( codec == NULL )
+    if ( pv->codec == NULL )
     {
         hb_log( "decavcodecvInit: failed to find codec for id (%d)", w->codec_param );
         return 1;
     }
 
+    pv->context = avcodec_alloc_context3( pv->codec );
+    pv->context->workaround_bugs = FF_BUG_AUTODETECT;
+    pv->context->err_recognition = AV_EF_CRCCHECK;
+    pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
+
     if ( pv->title->opaque_priv )
     {
         AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv;
 
-        pv->context = avcodec_alloc_context3(codec);
         avcodec_parameters_to_context(pv->context,
                                   ic->streams[pv->title->video_id]->codecpar);
-        pv->context->workaround_bugs = FF_BUG_AUTODETECT;
-        pv->context->err_recognition = AV_EF_CRCCHECK;
-        pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
 
 #ifdef USE_QSV
         if (pv->qsv.decode &&
@@ -1671,7 +1548,7 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
         }
 #endif
 
-        if ( hb_avcodec_open( pv->context, codec, &av_opts, pv->threads ) )
+        if ( hb_avcodec_open( pv->context, pv->codec, &av_opts, pv->threads ) )
         {
             av_dict_free( &av_opts );
             hb_log( "decavcodecvInit: avcodec_open failed" );
@@ -1719,10 +1596,8 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
     return 0;
 }
 
-static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in )
+static int setup_extradata( hb_work_private_t * pv )
 {
-    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
@@ -1738,7 +1613,8 @@ static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in )
         else
         {
             int size;
-            size = pv->parser->parser->split(pv->context, in->data, in->size);
+            size = pv->parser->parser->split(pv->context, pv->packet_info.data,
+                                             pv->packet_info.size);
             if (size > 0)
             {
                 pv->context->extradata_size = size;
@@ -1746,7 +1622,7 @@ static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in )
                                 av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
                 if (pv->context->extradata == NULL)
                     return 1;
-                memcpy(pv->context->extradata, in->data, size);
+                memcpy(pv->context->extradata, pv->packet_info.data, size);
                 return 0;
             }
         }
@@ -1756,73 +1632,23 @@ static int setup_extradata( hb_work_object_t *w, hb_buffer_t *in )
     return 0;
 }
 
-static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
-                            hb_buffer_t ** buf_out )
+static int decodePacket( hb_work_object_t * w )
 {
-    hb_work_private_t *pv = w->private_data;
-    hb_buffer_t *in = *buf_in;
-
-    *buf_in = NULL;
-    *buf_out = NULL;
-
-    // libavcodec/mpeg12dec.c requires buffers to be zero padded.
-    // If not zero padded, it can get stuck in an infinite loop.
-    // It's likely there are other decoders that expect the same.
-    if (in->data != NULL)
-    {
-        memset(in->data + in->size, 0, in->alloc - in->size);
-    }
-
-    /* if we got an empty buffer signaling end-of-stream send it downstream */
-    if (in->s.flags & HB_BUF_FLAG_EOF)
-    {
-        if (pv->context != NULL && pv->context->codec != NULL)
-        {
-            decodeVideo(w, in);
-        }
-        hb_buffer_list_append(&pv->list, in);
-        *buf_out = hb_buffer_list_clear(&pv->list);
-        return HB_WORK_DONE;
-    }
+    hb_work_private_t * pv     = w->private_data;
 
     // if this is the first frame open the codec (we have to wait for the
     // first frame because of M$ VC1 braindamage).
     if ( !pv->video_codec_opened )
     {
-
-        AVCodec *codec = NULL;
-#ifdef USE_QSV
-        if (pv->qsv.decode)
-        {
-            codec = avcodec_find_decoder_by_name(pv->qsv.codec_name);
-        }
-        else
-#endif
-        {
-            codec = avcodec_find_decoder(w->codec_param);
-        }
-
-        if ( codec == NULL )
-        {
-            hb_log( "decavcodecvWork: failed to find codec for id (%d)", w->codec_param );
-            *buf_out = hb_buffer_eof_init();
-            return HB_WORK_DONE;
-        }
-
-        if (pv->context == NULL)
-        {
-            pv->context = avcodec_alloc_context3( codec );
-        }
         pv->context->workaround_bugs = FF_BUG_AUTODETECT;
         pv->context->err_recognition = AV_EF_CRCCHECK;
         pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
 
-        if ( setup_extradata( w, in ) )
+        if (setup_extradata(pv))
         {
             // 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;
         }
 
@@ -1843,7 +1669,7 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
         }
 
         // disable threaded decoding for scan, can cause crashes
-        if ( hb_avcodec_open( pv->context, codec, &av_opts, pv->threads ) )
+        if ( hb_avcodec_open( pv->context, pv->codec, &av_opts, pv->threads ) )
         {
             av_dict_free( &av_opts );
             hb_log( "decavcodecvWork: avcodec_open failed" );
@@ -1851,7 +1677,6 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
             // so try again when this fails
             av_freep( &pv->context->extradata );
             pv->context->extradata_size = 0;
-            hb_buffer_close( &in );
             return HB_WORK_OK;
         }
         pv->context->pkt_timebase.num = pv->title->video_timebase.num;
@@ -1860,17 +1685,152 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
         pv->video_codec_opened = 1;
     }
 
+    decodeFrame(pv, &pv->packet_info);
+
+    return HB_WORK_OK;
+}
+
+static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
+                            hb_buffer_t ** buf_out )
+{
+    hb_work_private_t * pv     = w->private_data;
+    hb_buffer_t       * in     = *buf_in;
+    int                 result = HB_WORK_OK;
+
+    *buf_out = NULL;
+
+    // libavcodec/mpeg12dec.c requires buffers to be zero padded.
+    // If not zero padded, it can get stuck in an infinite loop.
+    // It's likely there are other decoders that expect the same.
+    if (in->data != NULL)
+    {
+        memset(in->data + in->size, 0, in->alloc - in->size);
+    }
     if (in->palette != NULL)
     {
         pv->palette = in->palette;
         in->palette = NULL;
     }
-    decodeVideo(w, in);
-    hb_buffer_close( &in );
+
+    /* if we got an empty buffer signaling end-of-stream send it downstream */
+    if (in->s.flags & HB_BUF_FLAG_EOF)
+    {
+        if (pv->context != NULL && pv->context->codec != NULL)
+        {
+            while (decodeFrame(pv, NULL))
+            {
+                continue;
+            }
+        }
+        hb_buffer_list_append(&pv->list, in);
+        *buf_out = hb_buffer_list_clear(&pv->list);
+        return HB_WORK_DONE;
+    }
+
+    /*
+     * The following loop is a do..while because we need to handle both
+     * data & the flush at the end (signaled by size=0). At the end there's
+     * generally a frame in the parser & one or more frames in the decoder
+     * (depending on the bframes setting).
+     */
+    int      pos, len;
+    int64_t  pts = in->s.start;
+    int64_t  dts = in->s.renderOffset;
+
+    if (in->s.new_chap > 0)
+    {
+        pv->new_chap = in->s.new_chap;
+        pv->chap_scr = in->s.scr_sequence;
+        if (in->s.start != AV_NOPTS_VALUE)
+        {
+            pv->chap_time = in->s.start;
+        }
+        else
+        {
+            pv->chap_time = pv->last_pts + 1;
+        }
+    }
+    if (in->s.start != AV_NOPTS_VALUE)
+    {
+        pv->last_pts = in->s.start;
+    }
+
+    // There are a 3 scenarios that can happen here.
+    // 1. The buffer contains exactly one frame of data
+    // 2. The buffer contains multiple frames of data
+    // 3. The buffer contains a partial frame of data
+    //
+    // In scenario 2, we want to be sure that the timestamps are only
+    // applied to the first frame in the buffer.  Additional frames
+    // in the buffer will have their timestamps computed in sync.
+    //
+    // In scenario 3, we need to save the ancillary buffer info of an
+    // unfinished frame so it can be applied when we receive the last
+    // buffer of that frame.
+    if (!pv->unfinished)
+    {
+        // New packet, and no previous data pending
+        pv->packet_info.scr_sequence = in->s.scr_sequence;
+        pv->packet_info.new_chap     = in->s.new_chap;
+        pv->packet_info.frametype    = in->s.frametype;
+    }
+    for (pos = 0; pos < in->size; pos += len)
+    {
+        uint8_t * pout;
+        int       pout_len;
+        int64_t   parser_pts, parser_dts;
+
+        if (pv->parser)
+        {
+            len = av_parser_parse2(pv->parser, pv->context, &pout, &pout_len,
+                                   in->data + pos, in->size - pos,
+                                   pts, dts, 0 );
+            parser_pts = pv->parser->pts;
+            parser_dts = pv->parser->dts;
+            pts = AV_NOPTS_VALUE;
+            dts = AV_NOPTS_VALUE;
+        }
+        else
+        {
+            pout = in->data;
+            len = pout_len = in->size;
+            parser_pts = pts;
+            parser_dts = dts;
+        }
+
+        if (pout != NULL && pout_len > 0)
+        {
+            pv->packet_info.data         = pout;
+            pv->packet_info.size         = pout_len;
+            pv->packet_info.pts          = parser_pts;
+            pv->packet_info.dts          = parser_dts;
+
+            result = decodePacket(w);
+            if (result != HB_WORK_OK)
+            {
+                break;
+            }
+            w->frame_count++;
+
+            // There could have been an unfinished packet that is now finished.
+            // The next packet is associated with the input buffer, so set
+            // it's chapter and scr info.
+            pv->packet_info.scr_sequence = in->s.scr_sequence;
+            pv->packet_info.new_chap     = in->s.new_chap;
+            pv->packet_info.frametype    = in->s.frametype;
+            pv->unfinished               = 0;
+        }
+        if (len > 0 && pout_len <= 0)
+        {
+            pv->unfinished = 1;
+        }
+    }
     *buf_out = hb_buffer_list_clear(&pv->list);
-    return HB_WORK_OK;
+
+    return result;
 }
 
+
 static void compute_frame_duration( hb_work_private_t *pv )
 {
     double duration = 0.;
@@ -2138,6 +2098,7 @@ static void decavcodecvFlush( hb_work_object_t *w )
                 av_parser_close(pv->parser);
             }
             pv->parser = av_parser_init( w->codec_param );
+            pv->context = avcodec_alloc_context3( pv->codec );
         }
         else
         {