[vlc-commits] avcodec: use reference counting for video decoder

Rémi Denis-Courmont git at videolan.org
Tue Jul 23 22:07:42 CEST 2013


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Tue Jul 23 23:03:37 2013 +0300| [b217f60be52bfb3b9f03d7a38f261f84c3218365] | committer: Rémi Denis-Courmont

avcodec: use reference counting for video decoder

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=b217f60be52bfb3b9f03d7a38f261f84c3218365
---

 modules/codec/avcodec/video.c |  209 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 202 insertions(+), 7 deletions(-)

diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
index 7ecf90c..30f9385 100644
--- a/modules/codec/avcodec/video.c
+++ b/modules/codec/avcodec/video.c
@@ -102,8 +102,12 @@ struct decoder_sys_t
  *****************************************************************************/
 static void ffmpeg_InitCodec      ( decoder_t * );
 static void ffmpeg_CopyPicture    ( decoder_t *, picture_t *, AVFrame * );
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+static int lavc_GetFrame(struct AVCodecContext *, AVFrame *, int);
+#else
 static int  ffmpeg_GetFrameBuf    ( struct AVCodecContext *, AVFrame * );
 static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *, AVFrame * );
+#endif
 static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *,
                                           const enum PixelFormat * );
 
@@ -314,12 +318,16 @@ int InitVideoDec( decoder_t *p_dec, AVCodecContext *p_context,
         msg_Dbg( p_dec, "direct rendering is disabled" );
     }
 
+    p_sys->p_context->get_format = ffmpeg_GetFormat;
     /* Always use our get_buffer wrapper so we can calculate the
      * PTS correctly */
-    p_sys->p_context->get_format = ffmpeg_GetFormat;
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+    p_sys->p_context->get_buffer2 = lavc_GetFrame;
+#else
     p_sys->p_context->get_buffer = ffmpeg_GetFrameBuf;
     p_sys->p_context->reget_buffer = avcodec_default_reget_buffer;
     p_sys->p_context->release_buffer = ffmpeg_ReleaseFrameBuf;
+#endif
     p_sys->p_context->opaque = p_dec;
 
 #ifdef HAVE_AVCODEC_MT
@@ -885,6 +893,198 @@ static void ffmpeg_CopyPicture( decoder_t *p_dec,
     }
 }
 
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+typedef struct
+{
+    vlc_va_t *va;
+    AVFrame *frame;
+} lavc_hw_ref_t;
+
+static void lavc_va_ReleaseFrame(void *opaque, uint8_t *data)
+{
+    lavc_hw_ref_t *ref = opaque;
+
+    vlc_va_Release(ref->va, ref->frame);
+    free(ref);
+    (void) data;
+}
+
+static int lavc_va_GetFrame(struct AVCodecContext *ctx, AVFrame *frame)
+{
+    decoder_t *dec = ctx->opaque;
+    decoder_sys_t *sys = dec->p_sys;
+    vlc_va_t *va = sys->p_va;
+
+    if (vlc_va_Setup(va, &ctx->hwaccel_context, &dec->fmt_out.video.i_chroma,
+                     ctx->coded_width, ctx->coded_height))
+    {
+        msg_Err(dec, "hardware acceleration setup failed");
+        return -1;
+    }
+    if (vlc_va_Get(va, frame))
+    {
+        msg_Err(dec, "hardware acceleration picture allocation failed");
+        return -1;
+    }
+
+    lavc_hw_ref_t *ref = malloc(sizeof (*ref));
+    if (unlikely(ref == NULL))
+    {
+        vlc_va_Release(va, frame);
+        return -1;
+    }
+    ref->va = va;
+    ref->frame = frame;
+
+    frame->buf[0] = av_buffer_create(frame->data[0], 0, lavc_va_ReleaseFrame,
+                                     ref, 0);
+    if (unlikely(frame->buf[0] == NULL))
+    {
+        lavc_va_ReleaseFrame(ref, frame->data[0]);
+        return -1;
+    }
+    return 0;
+}
+
+typedef struct
+{
+    decoder_t *decoder;
+    picture_t *picture;
+} lavc_pic_ref_t;
+
+static void lavc_dr_ReleaseFrame(void *opaque, uint8_t *data)
+{
+    lavc_pic_ref_t *ref = opaque;
+
+    decoder_UnlinkPicture(ref->decoder, ref->picture);
+    free(ref);
+    (void) data;
+}
+
+static picture_t *lavc_dr_GetFrame(struct AVCodecContext *ctx,
+                                   AVFrame *frame, unsigned flags)
+{
+    decoder_t *dec = (decoder_t *)ctx->opaque;
+
+    if (GetVlcChroma(&dec->fmt_out.video, ctx->pix_fmt) != VLC_SUCCESS)
+        return NULL;
+    dec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
+    if (ctx->pix_fmt == PIX_FMT_PAL8)
+        return NULL;
+
+    int width = frame->width;
+    int height = frame->height;
+    int aligns[AV_NUM_DATA_POINTERS];
+
+    avcodec_align_dimensions2(ctx, &width, &height, aligns);
+
+    picture_t *pic = ffmpeg_NewPictBuf(dec, ctx);
+    if (pic == NULL)
+        return NULL;
+
+    /* Check that the picture is suitable for libavcodec */
+    if (pic->p[0].i_pitch < width * pic->p[0].i_pixel_pitch
+     || pic->p[0].i_lines < height)
+        goto no_dr;
+
+    for (int i = 0; i < pic->i_planes; i++)
+    {
+        /*if (pic->p[i].i_pitch % aligns[i])
+            goto no_dr;*/
+        if (((uintptr_t)pic->p[i].p_pixels) % aligns[i])
+            goto no_dr;
+    }
+
+    /* Allocate buffer references */
+    for (unsigned i = 0; i < AV_NUM_DATA_POINTERS; i++)
+        frame->buf[i] = NULL;
+    for (int i = 0; i < pic->i_planes; i++)
+    {
+        lavc_pic_ref_t *ref = malloc(sizeof (*ref));
+        if (ref == NULL)
+            goto error;
+        ref->decoder = dec;
+        ref->picture = pic;
+        decoder_LinkPicture(dec, pic);
+
+        uint8_t *data = pic->p[i].p_pixels;
+        int size = pic->p[i].i_pitch * pic->p[i].i_lines;
+
+        frame->buf[i] = av_buffer_create(data, size, lavc_dr_ReleaseFrame,
+                                         ref, 0);
+        if (unlikely(frame->buf[i] == NULL))
+        {
+            lavc_dr_ReleaseFrame(ref, data);
+            goto error;
+        }
+    }
+    decoder_UnlinkPicture(dec, pic);
+    (void) flags;
+    return pic;
+error:
+    for (unsigned i = 0; frame->buf[i] != NULL; i++)
+        av_buffer_unref(&frame->buf[i]);
+no_dr:
+    decoder_DeletePicture(dec, pic);
+    return NULL;
+}
+
+/**
+ * Callback used by libavcodec to get a frame buffer.
+ *
+ * It is used for direct rendering as well as to get the right PTS for each
+ * decoded picture (even in indirect rendering mode).
+ */
+static int lavc_GetFrame(struct AVCodecContext *ctx, AVFrame *frame, int flags)
+{
+    decoder_t *dec = ctx->opaque;
+    decoder_sys_t *sys = dec->p_sys;
+    picture_t *pic;
+
+    if (sys->p_va != NULL)
+        return lavc_va_GetFrame(ctx, frame);
+
+    frame->opaque = NULL;
+    if (!sys->b_direct_rendering)
+        return avcodec_default_get_buffer2(ctx, frame, flags);
+
+    /* Some codecs set pix_fmt only after the 1st frame has been decoded,
+     * so we need to check for direct rendering again. */
+    wait_mt(sys);
+    pic = lavc_dr_GetFrame(ctx, frame, flags);
+    if (pic == NULL)
+    {
+        if (sys->i_direct_rendering_used != 0)
+        {
+            msg_Warn(dec, "disabling direct rendering");
+            sys->i_direct_rendering_used = 0;
+        }
+        post_mt(sys);
+        return avcodec_default_get_buffer2(ctx, frame, flags);
+    }
+
+    if (sys->i_direct_rendering_used != 1)
+    {
+        msg_Dbg(dec, "enabling direct rendering");
+        sys->i_direct_rendering_used = 1;
+    }
+    post_mt(sys);
+
+    frame->opaque = pic;
+    static_assert(PICTURE_PLANE_MAX <= AV_NUM_DATA_POINTERS, "Oops!");
+    for (unsigned i = 0; i < PICTURE_PLANE_MAX; i++)
+    {
+        frame->data[i] = pic->p[i].p_pixels;
+        frame->linesize[i] = pic->p[i].i_pitch;
+    }
+    for (unsigned i = PICTURE_PLANE_MAX; i < AV_NUM_DATA_POINTERS; i++)
+    {
+        frame->data[i] = NULL;
+        frame->linesize[i] = 0;
+    }
+    return 0;
+}
+#else
 static int ffmpeg_va_GetFrameBuf( struct AVCodecContext *p_context, AVFrame *p_ff_pic )
 {
     decoder_t *p_dec = (decoder_t *)p_context->opaque;
@@ -974,12 +1174,6 @@ no_dr:
     return NULL;
 }
 
-/*****************************************************************************
- * ffmpeg_GetFrameBuf: callback used by ffmpeg to get a frame buffer.
- *****************************************************************************
- * It is used for direct rendering as well as to get the right PTS for each
- * decoded picture (even in indirect rendering mode).
- *****************************************************************************/
 static int ffmpeg_GetFrameBuf( struct AVCodecContext *p_context,
                                AVFrame *p_ff_pic )
 {
@@ -1060,6 +1254,7 @@ static void ffmpeg_ReleaseFrameBuf( struct AVCodecContext *p_context,
     for( int i = 0; i < 4; i++ )
         p_ff_pic->data[i] = NULL;
 }
+#endif
 
 static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
                                           const enum PixelFormat *pi_fmt )



More information about the vlc-commits mailing list