avviddec: release buffers when not direct rendering
authorWim Taymans <wtaymans@redhat.com>
Fri, 10 Jan 2014 16:18:53 +0000 (17:18 +0100)
committerWim Taymans <wtaymans@redhat.com>
Fri, 10 Jan 2014 16:28:22 +0000 (17:28 +0100)
New libav will not call the release_buffer callback anymore when
avcodec_default_get_buffer() is called from get_buffer. Releasing of the
memory in a picture should now be done by registering a callback to the
avbuffer objects in the picture. There is some compatibility code to
wrap the memory we provide in get_buffer in avbuffer with a callback to
release_buffer but that is not done when avcodec_default_get_buffer()
is called.

Work around this by adding a dummy avbuffer object to the picture that
will release the frame.

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=721077

ext/libav/gstavviddec.c

index 63c742e..9f9fca5 100644 (file)
@@ -524,6 +524,7 @@ open_failed:
 
 typedef struct
 {
+  GstFFMpegVidDec *ffmpegdec;
   GstVideoCodecFrame *frame;
   gboolean mapped;
   GstVideoFrame vframe;
@@ -531,13 +532,17 @@ typedef struct
 } GstFFMpegVidDecVideoFrame;
 
 static GstFFMpegVidDecVideoFrame *
-gst_ffmpegviddec_video_frame_new (GstVideoCodecFrame * frame)
+gst_ffmpegviddec_video_frame_new (GstFFMpegVidDec * ffmpegdec,
+    GstVideoCodecFrame * frame)
 {
   GstFFMpegVidDecVideoFrame *dframe;
 
   dframe = g_slice_new0 (GstFFMpegVidDecVideoFrame);
+  dframe->ffmpegdec = ffmpegdec;
   dframe->frame = frame;
 
+  GST_DEBUG_OBJECT (ffmpegdec, "new video frame %p", dframe);
+
   return dframe;
 }
 
@@ -545,6 +550,8 @@ static void
 gst_ffmpegviddec_video_frame_free (GstFFMpegVidDec * ffmpegdec,
     GstFFMpegVidDecVideoFrame * frame)
 {
+  GST_DEBUG_OBJECT (ffmpegdec, "free video frame %p", frame);
+
   if (frame->mapped)
     gst_video_frame_unmap (&frame->vframe);
   gst_video_decoder_release_frame (GST_VIDEO_DECODER (ffmpegdec), frame->frame);
@@ -552,6 +559,14 @@ gst_ffmpegviddec_video_frame_free (GstFFMpegVidDec * ffmpegdec,
   g_slice_free (GstFFMpegVidDecVideoFrame, frame);
 }
 
+static void
+dummy_free_buffer (void *opaque, uint8_t * data)
+{
+  GstFFMpegVidDecVideoFrame *frame = opaque;
+
+  gst_ffmpegviddec_video_frame_free (frame->ffmpegdec, frame);
+}
+
 /* called when ffmpeg wants us to allocate a buffer to write the decoded frame
  * into. We try to give it memory from our pool */
 static int
@@ -590,7 +605,8 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture)
     goto duplicate_frame;
 
   /* GstFFMpegVidDecVideoFrame receives the frame ref */
-  picture->opaque = dframe = gst_ffmpegviddec_video_frame_new (frame);
+  picture->opaque = dframe =
+      gst_ffmpegviddec_video_frame_new (ffmpegdec, frame);
 
   GST_DEBUG_OBJECT (ffmpegdec, "storing opaque %p", dframe);
 
@@ -694,11 +710,19 @@ invalid_frame:
 fallback:
   {
     int c;
+    gboolean first = TRUE;
     int ret = avcodec_default_get_buffer (context, picture);
 
-    for (c = 0; c < AV_NUM_DATA_POINTERS; c++)
+    for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
       ffmpegdec->stride[c] = picture->linesize[c];
 
+      if (picture->buf[c] == NULL && first) {
+        picture->buf[c] =
+            av_buffer_create (NULL, 0, dummy_free_buffer, dframe, 0);
+        first = FALSE;
+      }
+    }
+
     return ret;
   }
 duplicate_frame:
@@ -1669,8 +1693,8 @@ gst_ffmpegviddec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query)
     avcodec_align_dimensions2 (ffmpegdec->context, &width, &height,
         linesize_align);
     edge =
-        ffmpegdec->context->
-        flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width ();
+        ffmpegdec->
+        context->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width ();
     /* increase the size for the padding */
     width += edge << 1;
     height += edge << 1;