avviddec: improve buffer handling and semantics
authorMark Nauwelaerts <mnauw@users.sourceforge.net>
Sat, 7 Dec 2013 10:35:09 +0000 (11:35 +0100)
committerMark Nauwelaerts <mnauw@users.sourceforge.net>
Sat, 7 Dec 2013 11:11:34 +0000 (12:11 +0100)
... so as to focus on providing *a* buffer rather than one (too) tied
to a frame, in particular allowing multiple allocations related to a frame.

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

ext/libav/gstavviddec.c

index b0ad2b6..863df81 100644 (file)
@@ -527,6 +527,7 @@ typedef struct
   GstVideoCodecFrame *frame;
   gboolean mapped;
   GstVideoFrame vframe;
+  GstBuffer *buffer;
 } GstFFMpegVidDecVideoFrame;
 
 static GstFFMpegVidDecVideoFrame *
@@ -547,6 +548,7 @@ gst_ffmpegviddec_video_frame_free (GstFFMpegVidDec * ffmpegdec,
   if (frame->mapped)
     gst_video_frame_unmap (&frame->vframe);
   gst_video_decoder_release_frame (GST_VIDEO_DECODER (ffmpegdec), frame->frame);
+  gst_buffer_replace (&frame->buffer, NULL);
   g_slice_free (GstFFMpegVidDecVideoFrame, frame);
 }
 
@@ -570,6 +572,8 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture)
    * picture back from ffmpeg we can use this to correctly timestamp the output
    * buffer */
   picture->reordered_opaque = context->reordered_opaque;
+  GST_DEBUG_OBJECT (ffmpegdec, "opaque value SN %d",
+      (gint32) picture->reordered_opaque);
 
   frame =
       gst_video_decoder_get_frame (GST_VIDEO_DECODER (ffmpegdec),
@@ -605,9 +609,16 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture)
   if (ret != GST_FLOW_OK)
     goto alloc_failed;
 
+  /* piggy-backed alloc'ed on the frame,
+   * and there was much rejoicing and we are grateful.
+   * Now take away buffer from frame, we will give it back later when decoded.
+   * This allows multiple request for a buffer per frame; unusual but possible. */
+  gst_buffer_replace (&dframe->buffer, frame->output_buffer);
+  gst_buffer_replace (&frame->output_buffer, NULL);
+
   /* Fill avpicture */
   info = &ffmpegdec->output_state->info;
-  if (!gst_video_frame_map (&dframe->vframe, info, dframe->frame->output_buffer,
+  if (!gst_video_frame_map (&dframe->vframe, info, dframe->buffer,
           GST_MAP_READWRITE))
     goto invalid_frame;
   dframe->mapped = TRUE;
@@ -635,7 +646,7 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture)
         }
         gst_video_frame_unmap (&dframe->vframe);
         dframe->mapped = FALSE;
-        gst_buffer_replace (&frame->output_buffer, NULL);
+        gst_buffer_replace (&dframe->buffer, NULL);
         ffmpegdec->current_dr = FALSE;
 
         goto no_dr;
@@ -652,7 +663,7 @@ gst_ffmpegviddec_get_buffer (AVCodecContext * context, AVFrame * picture)
    * the opaque data. */
   picture->type = FF_BUFFER_TYPE_USER;
 
-  GST_LOG_OBJECT (ffmpegdec, "returned frame %p", frame->output_buffer);
+  GST_LOG_OBJECT (ffmpegdec, "returned frame %p", dframe->buffer);
 
   return 0;
 
@@ -677,8 +688,7 @@ invalid_frame:
   {
     /* alloc default buffer when we can't get one from downstream */
     GST_LOG_OBJECT (ffmpegdec, "failed to map frame, fallback alloc");
-    gst_buffer_unref (frame->output_buffer);
-    frame->output_buffer = NULL;
+    gst_buffer_replace (&dframe->buffer, NULL);
     goto fallback;
   }
 fallback:
@@ -704,6 +714,8 @@ no_frame:
   }
 }
 
+/* this should havesame effect as _get_buffer wrt opaque metadata,
+ * but preserving current content, if any */
 static int
 gst_ffmpegviddec_reget_buffer (AVCodecContext * context, AVFrame * picture)
 {
@@ -764,7 +776,7 @@ gst_ffmpegviddec_release_buffer (AVCodecContext * context, AVFrame * picture)
 
   ffmpegdec = (GstFFMpegVidDec *) context->opaque;
   frame = (GstFFMpegVidDecVideoFrame *) picture->opaque;
-  GST_DEBUG_OBJECT (ffmpegdec, "release frame %d",
+  GST_DEBUG_OBJECT (ffmpegdec, "release frame SN %d",
       frame->frame->system_frame_number);
 
   /* check if it was our buffer */
@@ -1214,6 +1226,10 @@ gst_ffmpegviddec_video_frame (GstFFMpegVidDec * ffmpegdec,
   out_dframe = ffmpegdec->picture->opaque;
   out_frame = gst_video_codec_frame_ref (out_dframe->frame);
 
+  /* also give back a buffer allocated by the frame, if any */
+  gst_buffer_replace (&out_frame->output_buffer, out_dframe->buffer);
+  gst_buffer_replace (&out_dframe->buffer, NULL);
+
   GST_DEBUG_OBJECT (ffmpegdec,
       "pts %" G_GUINT64_FORMAT " duration %" G_GUINT64_FORMAT,
       out_frame->pts, out_frame->duration);