d3dvideosink: Implement a buffer pool that shares D3D surfaces with upstream
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Sat, 22 Dec 2012 20:59:03 +0000 (21:59 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 26 Mar 2013 12:39:46 +0000 (13:39 +0100)
sys/d3dvideosink/d3dhelpers.c
sys/d3dvideosink/d3dhelpers.h
sys/d3dvideosink/d3dvideosink.c
sys/d3dvideosink/d3dvideosink.h

index 081d402..6d0f70d 100644 (file)
@@ -37,8 +37,8 @@ static gboolean d3d_init_swap_chain (GstD3DVideoSink * sink, HWND hWnd);
 static gboolean d3d_release_swap_chain (GstD3DVideoSink * sink);
 static gboolean d3d_resize_swap_chain (GstD3DVideoSink * sink);
 static gboolean d3d_present_swap_chain (GstD3DVideoSink * sink);
-static gboolean d3d_copy_buffer_to_surface (GstD3DVideoSink * sink,
-    LPDIRECT3DSURFACE9 surface, GstBuffer * buffer);
+static gboolean d3d_copy_buffer (GstD3DVideoSink * sink,
+    GstBuffer * from, GstBuffer * to);
 static gboolean d3d_stretch_and_copy (GstD3DVideoSink * sink,
     LPDIRECT3DSURFACE9 back_buffer);
 static HWND d3d_create_internal_window (GstD3DVideoSink * sink);
@@ -315,6 +315,392 @@ d3d_format_comp_compare (gconstpointer a, gconstpointer b)
     return 1;
 }
 
+#define GST_D3D_SURFACE_MEMORY_NAME "D3DSurface"
+
+typedef struct
+{
+  GstMemory mem;
+
+  GstD3DVideoSink *sink;
+
+  GMutex lock;
+  gint map_count;
+
+  LPDIRECT3DSURFACE9 surface;
+  D3DLOCKED_RECT lr;
+  gint x, y, width, height;
+} GstD3DSurfaceMemory;
+
+static GstMemory *
+gst_d3d_surface_memory_allocator_alloc (GstAllocator * allocator, gsize size,
+    GstAllocationParams * params)
+{
+  g_assert_not_reached ();
+  return NULL;
+}
+
+static void
+gst_d3d_surface_memory_allocator_free (GstAllocator * allocator,
+    GstMemory * mem)
+{
+  GstD3DSurfaceMemory *dmem = (GstD3DSurfaceMemory *) mem;
+
+  /* If this is a sub-memory, do nothing */
+  if (mem->parent)
+    return;
+
+  if (dmem->lr.pBits)
+    g_warning ("d3dvideosink: Freeing memory that is still mapped");
+
+  IDirect3DSurface9_Release (dmem->surface);
+  gst_object_unref (dmem->sink);
+  g_mutex_clear (&dmem->lock);
+  g_slice_free (GstD3DSurfaceMemory, dmem);
+}
+
+static gpointer
+gst_d3d_surface_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
+{
+  GstD3DSurfaceMemory *parent;
+  gpointer ret = NULL;
+  gint d3d_flags = ((flags & GST_MAP_WRITE) == 0) ? D3DLOCK_READONLY : 0;
+
+  /* find the real parent */
+  if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
+    parent = (GstD3DSurfaceMemory *) mem;
+
+  g_mutex_lock (&parent->lock);
+  if (!parent->map_count
+      && IDirect3DSurface9_LockRect (parent->surface, &parent->lr, NULL,
+          d3d_flags) != D3D_OK) {
+    ret = NULL;
+    goto done;
+  }
+
+  ret = parent->lr.pBits;
+  parent->map_count++;
+
+done:
+  g_mutex_unlock (&parent->lock);
+
+  return ret;
+}
+
+static void
+gst_d3d_surface_memory_unmap (GstMemory * mem)
+{
+  GstD3DSurfaceMemory *parent;
+
+  /* find the real parent */
+  if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
+    parent = (GstD3DSurfaceMemory *) mem;
+
+  g_mutex_lock (&parent->lock);
+  parent->map_count--;
+  if (parent->map_count == 0) {
+    IDirect3DSurface9_UnlockRect (parent->surface);
+    memset (&parent->lr, 0, sizeof (parent->lr));
+  }
+
+  g_mutex_unlock (&parent->lock);
+}
+
+static GstMemory *
+gst_d3d_surface_memory_share (GstMemory * mem, gssize offset, gssize size)
+{
+  GstD3DSurfaceMemory *sub;
+  GstD3DSurfaceMemory *parent;
+
+  /* find the real parent */
+  if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
+    parent = (GstD3DSurfaceMemory *) mem;
+
+  if (size == -1)
+    size = mem->size - offset;
+
+  sub = g_slice_new0 (GstD3DSurfaceMemory);
+  /* the shared memory is always readonly */
+  gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) |
+      GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->allocator,
+      GST_MEMORY_CAST (parent), mem->maxsize, mem->align, mem->offset + offset,
+      size);
+
+  return GST_MEMORY_CAST (sub);
+}
+
+typedef struct
+{
+  GstAllocator parent;
+} GstD3DSurfaceMemoryAllocator;
+
+typedef struct
+{
+  GstAllocatorClass parent_class;
+} GstD3DSurfaceMemoryAllocatorClass;
+
+GType gst_d3d_surface_memory_allocator_get_type (void);
+G_DEFINE_TYPE (GstD3DSurfaceMemoryAllocator, gst_d3d_surface_memory_allocator,
+    GST_TYPE_ALLOCATOR);
+
+#define GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR   (gst_d3d_surface_memory_allocator_get_type())
+#define GST_IS_D3D_SURFACE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR))
+
+static void
+gst_d3d_surface_memory_allocator_class_init (GstD3DSurfaceMemoryAllocatorClass *
+    klass)
+{
+  GstAllocatorClass *allocator_class;
+
+  allocator_class = (GstAllocatorClass *) klass;
+
+  allocator_class->alloc = gst_d3d_surface_memory_allocator_alloc;
+  allocator_class->free = gst_d3d_surface_memory_allocator_free;
+}
+
+static void
+gst_d3d_surface_memory_allocator_init (GstD3DSurfaceMemoryAllocator * allocator)
+{
+  GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
+
+  alloc->mem_type = GST_D3D_SURFACE_MEMORY_NAME;
+  alloc->mem_map = gst_d3d_surface_memory_map;
+  alloc->mem_unmap = gst_d3d_surface_memory_unmap;
+  alloc->mem_share = gst_d3d_surface_memory_share;
+  /* fallback copy */
+
+  GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
+}
+
+G_DEFINE_TYPE (GstD3DSurfaceBufferPool, gst_d3dsurface_buffer_pool,
+    GST_TYPE_VIDEO_BUFFER_POOL);
+
+GstBufferPool *
+gst_d3dsurface_buffer_pool_new (GstD3DVideoSink * sink)
+{
+  GstD3DSurfaceBufferPool *pool;
+
+  pool = g_object_new (GST_TYPE_D3DSURFACE_BUFFER_POOL, NULL);
+  pool->sink = gst_object_ref (sink);
+
+  GST_LOG_OBJECT (pool, "new buffer pool %p", pool);
+
+  return GST_BUFFER_POOL_CAST (pool);
+}
+
+static void
+gst_d3dsurface_buffer_pool_finalize (GObject * object)
+{
+  GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (object);
+
+  GST_LOG_OBJECT (pool, "finalize buffer pool %p", pool);
+
+  gst_object_unref (pool->sink);
+  if (pool->allocator)
+    gst_object_unref (pool->allocator);
+
+  G_OBJECT_CLASS (gst_d3dsurface_buffer_pool_parent_class)->finalize (object);
+}
+
+static const gchar **
+gst_d3dsurface_buffer_pool_get_options (GstBufferPool * pool)
+{
+  static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
+
+  return options;
+}
+
+static gboolean
+gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool,
+    GstStructure * config)
+{
+  GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool);
+  GstCaps *caps;
+  GstVideoInfo info;
+
+  if (!GST_BUFFER_POOL_CLASS
+      (gst_d3dsurface_buffer_pool_parent_class)->set_config (bpool, config)) {
+    return FALSE;
+  }
+
+  if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
+      || !caps) {
+    GST_ERROR_OBJECT (pool, "Buffer pool configuration without caps");
+    return FALSE;
+  }
+
+  /* now parse the caps from the config */
+  if (!gst_video_info_from_caps (&info, caps)) {
+    GST_ERROR_OBJECT (pool, "Failed to parse caps %" GST_PTR_FORMAT, caps);
+    return FALSE;
+  }
+
+  if (gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&info)) ==
+      D3DFMT_UNKNOWN) {
+    GST_ERROR_OBJECT (pool, "Unsupported video format in caps %" GST_PTR_FORMAT,
+        caps);
+    return FALSE;
+  }
+
+  GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
+      caps);
+
+  pool->info = info;
+
+  pool->add_metavideo =
+      gst_buffer_pool_config_has_option (config,
+      GST_BUFFER_POOL_OPTION_VIDEO_META);
+
+  if (pool->add_metavideo)
+    pool->allocator =
+        g_object_new (GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR, NULL);
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_d3dsurface_buffer_pool_alloc (GstBufferPool * bpool, GstBuffer ** buffer,
+    GstBufferPoolAcquireParams * params)
+{
+  GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool);
+  GstD3DVideoSink *sink = pool->sink;
+  GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
+  GstD3DSurfaceMemory *mem;
+  LPDIRECT3DSURFACE9 surface;
+  D3DFORMAT d3dformat;
+  gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
+  gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
+  D3DLOCKED_RECT lr;
+  HRESULT hr;
+  gsize size = 0;
+
+  *buffer = NULL;
+  if (!pool->add_metavideo) {
+    GST_DEBUG_OBJECT (pool, "No video meta allowed, fallback alloc");
+    goto fallback;
+  }
+
+  d3dformat =
+      gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&pool->info));
+  hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d.
+      device.d3d_device, GST_VIDEO_INFO_WIDTH (&pool->info),
+      GST_VIDEO_INFO_HEIGHT (&pool->info), d3dformat, D3DPOOL_DEFAULT, &surface,
+      NULL);
+  if (hr != D3D_OK) {
+    GST_ERROR_OBJECT (sink, "Failed to create D3D surface");
+    goto fallback;
+  }
+
+  IDirect3DSurface9_LockRect (surface, &lr, NULL, D3DLOCK_READONLY);
+  if (!lr.pBits) {
+    GST_ERROR_OBJECT (sink, "Failed to lock D3D surface");
+    IDirect3DSurface9_Release (surface);
+    goto fallback;
+  }
+
+  switch (GST_VIDEO_INFO_FORMAT (&pool->info)) {
+    case GST_VIDEO_FORMAT_BGR:
+      offset[0] = 0;
+      stride[0] = lr.Pitch;
+      size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 3;
+      break;
+    case GST_VIDEO_FORMAT_BGRx:
+    case GST_VIDEO_FORMAT_RGBx:
+    case GST_VIDEO_FORMAT_BGRA:
+    case GST_VIDEO_FORMAT_RGBA:
+      offset[0] = 0;
+      stride[0] = lr.Pitch;
+      size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 4;
+      break;
+    case GST_VIDEO_FORMAT_RGB16:
+    case GST_VIDEO_FORMAT_RGB15:
+      offset[0] = 0;
+      stride[0] = lr.Pitch;
+      size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 2;
+      break;
+    case GST_VIDEO_FORMAT_YUY2:
+    case GST_VIDEO_FORMAT_UYVY:
+      offset[0] = 0;
+      stride[0] = lr.Pitch;
+      size = lr.Pitch * GST_VIDEO_INFO_HEIGHT (&pool->info) * 2;
+      break;
+    case GST_VIDEO_FORMAT_I420:
+    case GST_VIDEO_FORMAT_YV12:
+      offset[0] = 0;
+      stride[0] = lr.Pitch;
+      offset[2] =
+          offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 0);
+      stride[2] = lr.Pitch / 2;
+      offset[1] =
+          offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 2);
+      stride[1] = lr.Pitch / 2;
+      size =
+          offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 1);
+      break;
+    case GST_VIDEO_FORMAT_NV12:
+      offset[0] = 0;
+      stride[0] = lr.Pitch;
+      offset[1] =
+          offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 0);
+      stride[1] = lr.Pitch;
+      size =
+          offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (&pool->info, 1);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+  }
+
+  IDirect3DSurface9_UnlockRect (surface);
+
+  *buffer = gst_buffer_new ();
+
+  gst_buffer_add_video_meta_full (*buffer, GST_VIDEO_FRAME_FLAG_NONE,
+      GST_VIDEO_INFO_FORMAT (&pool->info), GST_VIDEO_INFO_WIDTH (&pool->info),
+      GST_VIDEO_INFO_HEIGHT (&pool->info),
+      GST_VIDEO_INFO_N_PLANES (&pool->info), offset, stride);
+
+  mem = g_slice_new0 (GstD3DSurfaceMemory);
+  gst_memory_init (GST_MEMORY_CAST (mem), 0, pool->allocator, NULL, size, 0, 0,
+      size);
+
+  mem->surface = surface;
+  mem->sink = gst_object_ref (sink);
+  mem->x = mem->y = 0;
+  mem->width = GST_VIDEO_INFO_WIDTH (&pool->info);
+  mem->height = GST_VIDEO_INFO_HEIGHT (&pool->info);
+  g_mutex_init (&mem->lock);
+
+  gst_buffer_append_memory (*buffer, GST_MEMORY_CAST (mem));
+
+  return GST_FLOW_OK;
+
+fallback:
+  {
+    return
+        GST_BUFFER_POOL_CLASS
+        (gst_d3dsurface_buffer_pool_parent_class)->alloc_buffer (bpool, buffer,
+        params);
+  }
+}
+
+static void
+gst_d3dsurface_buffer_pool_class_init (GstD3DSurfaceBufferPoolClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
+
+  gobject_class->finalize = gst_d3dsurface_buffer_pool_finalize;
+
+  gstbufferpool_class->get_options = gst_d3dsurface_buffer_pool_get_options;
+  gstbufferpool_class->set_config = gst_d3dsurface_buffer_pool_set_config;
+  gstbufferpool_class->alloc_buffer = gst_d3dsurface_buffer_pool_alloc;
+}
+
+static void
+gst_d3dsurface_buffer_pool_init (GstD3DSurfaceBufferPool * pool)
+{
+}
+
 GstCaps *
 d3d_supported_caps (GstD3DVideoSink * sink)
 {
@@ -759,6 +1145,14 @@ end:
 gboolean
 d3d_stop (GstD3DVideoSink * sink)
 {
+  if (sink->pool)
+    gst_buffer_pool_set_active (sink->pool, FALSE);
+  if (sink->fallback_pool)
+    gst_buffer_pool_set_active (sink->fallback_pool, FALSE);
+  gst_object_replace ((GstObject **) & sink->pool, NULL);
+  gst_object_replace ((GstObject **) & sink->fallback_pool, NULL);
+  gst_buffer_replace (&sink->fallback_buffer, NULL);
+
   /* Release D3D resources */
   d3d_set_window_handle (sink, 0, FALSE);
   return TRUE;
@@ -1031,51 +1425,43 @@ end:
 }
 
 static gboolean
-d3d_copy_buffer_to_surface (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 surface,
-    GstBuffer * buffer)
+d3d_copy_buffer (GstD3DVideoSink * sink, GstBuffer * from, GstBuffer * to)
 {
-  D3DLOCKED_RECT lr;
-  guint8 *dest;
-  int deststride;
   gboolean ret = FALSE;
-  gint unhdl_line = 0;
-  GstVideoFrame frame;
+  GstVideoFrame from_frame, to_frame;
+
+  memset (&from_frame, 0, sizeof (from_frame));
+  memset (&to_frame, 0, sizeof (to_frame));
+
   LOCK_SINK (sink);
 
   if (!sink->d3d.renderable || sink->d3d.device_lost)
     goto end;
 
-  if (!buffer
-      || !gst_video_frame_map (&frame, &sink->info, buffer, GST_MAP_READ)) {
+  if (!gst_video_frame_map (&from_frame, &sink->info, from, GST_MAP_READ) ||
+      !gst_video_frame_map (&to_frame, &sink->info, to, GST_MAP_WRITE)) {
     GST_ERROR_OBJECT (sink, "NULL GstBuffer");
     goto end;
   }
 
-  IDirect3DSurface9_LockRect (surface, &lr, NULL, 0);
-  dest = (guint8 *) lr.pBits;
-
-  if (!dest) {
-    GST_ERROR_OBJECT (sink, "No D3D surface dest buffer");
-    goto unlock_surface;
-  }
-
-  deststride = lr.Pitch;
-
   switch (sink->format) {
     case GST_VIDEO_FORMAT_YUY2:
     case GST_VIDEO_FORMAT_UYVY:{
       const guint8 *src;
-      gint srcstride;
+      guint8 *dst;
+      gint dststride, srcstride;
       gint i, h, w;
 
-      src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
-      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
-      h = GST_VIDEO_FRAME_HEIGHT (&frame);
-      w = GST_ROUND_UP_4 (GST_VIDEO_FRAME_WIDTH (&frame) * 2);
+      src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
+      dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
+      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
+      dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
+      h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
+      w = GST_ROUND_UP_4 (GST_VIDEO_FRAME_WIDTH (&from_frame) * 2);
 
       for (i = 0; i < h; i++) {
-        memcpy (dest, src, w);
-        dest += deststride;
+        memcpy (dst, src, w);
+        dst += dststride;
         src += srcstride;
       }
 
@@ -1084,36 +1470,21 @@ d3d_copy_buffer_to_surface (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 surface,
     case GST_VIDEO_FORMAT_I420:
     case GST_VIDEO_FORMAT_YV12:{
       const guint8 *src;
-      gint srcstride, deststride_;
-      guint8 *dest_;
-      gint i, j, h, h_, w_;
-
-      h = GST_VIDEO_FRAME_HEIGHT (&frame);
+      guint8 *dst;
+      gint srcstride, dststride;
+      gint i, j, h_, w_;
 
       for (i = 0; i < 3; i++) {
-        src = GST_VIDEO_FRAME_COMP_DATA (&frame, i);
-        srcstride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, i);
-        h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i);
-        w_ = GST_VIDEO_FRAME_COMP_WIDTH (&frame, i);
-
-        switch (i) {
-          case 0:
-            deststride_ = deststride;
-            dest_ = dest;
-            break;
-          case 2:
-            deststride_ = deststride / 2;
-            dest_ = dest + h * deststride;
-            break;
-          case 1:
-            deststride_ = deststride / 2;
-            dest_ = dest + h * deststride + h_ * deststride_;
-            break;
-        }
+        src = GST_VIDEO_FRAME_COMP_DATA (&from_frame, i);
+        dst = GST_VIDEO_FRAME_COMP_DATA (&to_frame, i);
+        srcstride = GST_VIDEO_FRAME_COMP_STRIDE (&from_frame, i);
+        dststride = GST_VIDEO_FRAME_COMP_STRIDE (&to_frame, i);
+        h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
+        w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
 
         for (j = 0; j < h_; j++) {
-          memcpy (dest_, src, w_);
-          dest_ += deststride_;
+          memcpy (dst, src, w_);
+          dst += dststride;
           src += srcstride;
         }
       }
@@ -1122,30 +1493,21 @@ d3d_copy_buffer_to_surface (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 surface,
     }
     case GST_VIDEO_FORMAT_NV12:{
       const guint8 *src;
-      gint srcstride;
-      guint8 *dest_;
-      gint i, j, h, h_, w_;
-
-      h = GST_VIDEO_FRAME_HEIGHT (&frame);
+      guint8 *dst;
+      gint srcstride, dststride;
+      gint i, j, h_, w_;
 
       for (i = 0; i < 2; i++) {
-        src = GST_VIDEO_FRAME_PLANE_DATA (&frame, i);
-        srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, i);
-        h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&frame, i);
-        w_ = GST_VIDEO_FRAME_COMP_WIDTH (&frame, i);
-
-        switch (i) {
-          case 0:
-            dest_ = dest;
-            break;
-          case 1:
-            dest_ = dest + h * deststride;
-            break;
-        }
+        src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, i);
+        dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, i);
+        srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, i);
+        dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, i);
+        h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
+        w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
 
         for (j = 0; j < h_; j++) {
-          memcpy (dest_, src, w_ * 2);
-          dest_ += deststride;
+          memcpy (dst, src, w_ * 2);
+          dst += dststride;
           src += srcstride;
         }
       }
@@ -1157,17 +1519,20 @@ d3d_copy_buffer_to_surface (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 surface,
     case GST_VIDEO_FORMAT_BGRx:
     case GST_VIDEO_FORMAT_RGBx:{
       const guint8 *src;
-      gint srcstride;
+      guint8 *dst;
+      gint srcstride, dststride;
       gint i, h, w;
 
-      src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
-      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
-      h = GST_VIDEO_FRAME_HEIGHT (&frame);
-      w = GST_VIDEO_FRAME_WIDTH (&frame) * 4;
+      src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
+      dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
+      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
+      dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
+      h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
+      w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 4;
 
       for (i = 0; i < h; i++) {
-        memcpy (dest, src, w);
-        dest += deststride;
+        memcpy (dst, src, w);
+        dst += dststride;
         src += srcstride;
       }
 
@@ -1175,17 +1540,20 @@ d3d_copy_buffer_to_surface (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 surface,
     }
     case GST_VIDEO_FORMAT_BGR:{
       const guint8 *src;
-      gint srcstride;
+      guint8 *dst;
+      gint srcstride, dststride;
       gint i, h, w;
 
-      src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
-      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
-      h = GST_VIDEO_FRAME_HEIGHT (&frame);
-      w = GST_VIDEO_FRAME_WIDTH (&frame) * 3;
+      src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
+      dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
+      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
+      dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
+      h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
+      w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 3;
 
       for (i = 0; i < h; i++) {
-        memcpy (dest, src, w);
-        dest += deststride;
+        memcpy (dst, src, w);
+        dst += dststride;
         src += srcstride;
       }
 
@@ -1194,45 +1562,47 @@ d3d_copy_buffer_to_surface (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 surface,
     case GST_VIDEO_FORMAT_RGB16:
     case GST_VIDEO_FORMAT_RGB15:{
       const guint8 *src;
-      gint srcstride;
+      guint8 *dst;
+      gint srcstride, dststride;
       gint i, h, w;
 
-      src = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0);
-      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0);
-      h = GST_VIDEO_FRAME_HEIGHT (&frame);
-      w = GST_VIDEO_FRAME_WIDTH (&frame) * 2;
+      src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
+      dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
+      srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
+      dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
+      h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
+      w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 2;
 
       for (i = 0; i < h; i++) {
-        memcpy (dest, src, w);
-        dest += deststride;
+        memcpy (dst, src, w);
+        dst += dststride;
         src += srcstride;
       }
 
       break;
     }
     default:
-      unhdl_line = __LINE__;
       goto unhandled_format;
   }
 
-  goto done;
-
-unhandled_format:
-  GST_ERROR_OBJECT (sink,
-      "Unhandled format [LN:%d] '%s' -> '%s' (should not get here)", unhdl_line,
-      gst_video_format_to_string (sink->format),
-      d3d_format_to_string (sink->d3d.format));
-  goto unlock_surface;
-
-done:
   ret = TRUE;
-unlock_surface:
-  IDirect3DSurface9_UnlockRect (surface);
-  gst_video_frame_unmap (&frame);
 
 end:
+  if (from_frame.buffer)
+    gst_video_frame_unmap (&from_frame);
+  if (to_frame.buffer)
+    gst_video_frame_unmap (&to_frame);
+
   UNLOCK_SINK (sink);
   return ret;
+
+unhandled_format:
+  GST_ERROR_OBJECT (sink,
+      "Unhandled format '%s' -> '%s' (should not get here)",
+      gst_video_format_to_string (sink->format),
+      d3d_format_to_string (sink->d3d.format));
+  ret = FALSE;
+  goto end;
 }
 
 static gboolean
@@ -1397,12 +1767,9 @@ GstFlowReturn
 d3d_render_buffer (GstD3DVideoSink * sink, GstBuffer * buf)
 {
   GstFlowReturn ret = GST_FLOW_OK;
-  GstMapInfo map;
+  GstMemory *mem;
   LPDIRECT3DSURFACE9 surface = NULL;
 
-  g_return_val_if_fail (gst_buffer_map (buf, &map, GST_MAP_READ) != FALSE,
-      GST_FLOW_ERROR);
-
   LOCK_SINK (sink);
 
   if (!sink->d3d.window_handle) {
@@ -1428,26 +1795,59 @@ d3d_render_buffer (GstD3DVideoSink * sink, GstBuffer * buf)
     goto end;
   }
 
-  if (!surface) {
-    HRESULT hr;
-    GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
+  if (gst_buffer_n_memory (buf) != 1 ||
+      (mem = gst_buffer_peek_memory (buf, 0)) == 0 ||
+      !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
+    GstBuffer *tmp;
+    GstBufferPoolAcquireParams params = { 0, };
 
-    hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d.
-        device.d3d_device, GST_VIDEO_SINK_WIDTH (sink),
-        GST_VIDEO_SINK_HEIGHT (sink), sink->d3d.format, D3DPOOL_DEFAULT,
-        &surface, NULL);
-    if (hr != D3D_OK || surface == NULL) {
-      GST_ERROR_OBJECT (sink, "Failed to create D3D surface");
+    if (!sink->fallback_pool
+        || !gst_buffer_pool_set_active (sink->fallback_pool, TRUE)) {
+      ret = GST_FLOW_NOT_NEGOTIATED;
+      goto end;
+    }
+
+    /* take a buffer from our pool, if there is no buffer in the pool something
+     * is seriously wrong, waiting for the pool here might deadlock when we try
+     * to go to PAUSED because we never flush the pool. */
+    params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
+    ret = gst_buffer_pool_acquire_buffer (sink->fallback_pool, &tmp, &params);
+    if (ret != GST_FLOW_OK)
+      goto end;
+
+    if (sink->fallback_buffer) {
+      gst_buffer_unref (sink->fallback_buffer);
+      sink->fallback_buffer = NULL;
+    }
+
+    mem = gst_buffer_peek_memory (tmp, 0);
+    if (!mem || !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
       ret = GST_FLOW_ERROR;
+      gst_buffer_unref (tmp);
       goto end;
     }
-    d3d_copy_buffer_to_surface (sink, surface, buf);
-    if (sink->d3d.surface)
-      IDirect3DSurface9_Release (sink->d3d.surface);
-    IDirect3DSurface9_AddRef (surface);
-    sink->d3d.surface = surface;
+    d3d_copy_buffer (sink, buf, tmp);
+    buf = tmp;
+
+    surface = ((GstD3DSurfaceMemory *) mem)->surface;
+
+    /* Need to keep an additional ref until the next buffer
+     * to make sure it isn't reused until then */
+    sink->fallback_buffer = buf;
+  } else {
+    mem = gst_buffer_peek_memory (buf, 0);
+    surface = ((GstD3DSurfaceMemory *) mem)->surface;
+
+    if (sink->fallback_buffer) {
+      gst_buffer_unref (sink->fallback_buffer);
+      sink->fallback_buffer = NULL;
+    }
   }
 
+  if (sink->d3d.surface)
+    IDirect3DSurface9_Release (sink->d3d.surface);
+  IDirect3DSurface9_AddRef (surface);
+  sink->d3d.surface = surface;
 
   if (!d3d_present_swap_chain (sink)) {
     ret = GST_FLOW_ERROR;
index d3467aa..1e25303 100644 (file)
@@ -92,7 +92,6 @@ typedef struct _GstD3DData {
   gboolean               device_lost;
 } GstD3DData;
 
-
 gboolean       d3d_class_init(GstD3DVideoSink * klass);
 void           d3d_class_destroy(GstD3DVideoSink * klass);
 
@@ -105,4 +104,33 @@ GstFlowReturn  d3d_render_buffer(GstD3DVideoSink * sink, GstBuffer * buf);
 GstCaps *      d3d_supported_caps(GstD3DVideoSink * sink);
 gboolean       d3d_set_render_format(GstD3DVideoSink * sink);
 
+#define GST_TYPE_D3DSURFACE_BUFFER_POOL      (gst_d3dsurface_buffer_pool_get_type())
+#define GST_IS_D3DSURFACE_BUFFER_POOL(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_D3DSURFACE_BUFFER_POOL))
+#define GST_D3DSURFACE_BUFFER_POOL(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_D3DSURFACE_BUFFER_POOL, GstD3DSurfaceBufferPool))
+#define GST_D3DSURFACE_BUFFER_POOL_CAST(obj) ((GstD3DSurfaceBufferPool*)(obj))
+
+typedef struct _GstD3DSurfaceBufferPool {
+  GstVideoBufferPool parent;
+
+  GstD3DVideoSink *sink;
+  GstVideoInfo info;
+  gboolean add_metavideo;
+
+  GstAllocator *allocator;
+} GstD3DSurfaceBufferPool;
+
+typedef struct _GstD3DSurfaceBufferPoolClass {
+  GstVideoBufferPoolClass parent_class;
+} GstD3DSurfaceBufferPoolClass;
+
+GType gst_d3dsurface_meta_api_get_type (void);
+#define GST_D3DSURFACE_META_API_TYPE  (gst_d3dsurface_meta_api_get_type())
+const GstMetaInfo * gst_d3dsurface_meta_get_info (void);
+#define GST_D3DSURFACE_META_INFO  (gst_d3dsurface_meta_get_info())
+
+#define gst_buffer_get_d3dsurface_meta(b) ((GstD3DSurfaceMeta*)gst_buffer_get_meta((b),GST_D3DSURFACE_META_API_TYPE))
+
+GType gst_d3dsurface_buffer_pool_get_type (void);
+GstBufferPool * gst_d3dsurface_buffer_pool_new (GstD3DVideoSink * sink);
+
 #endif /* _D3DHELPERS_H_ */
index cd57b57..5ce12eb 100644 (file)
@@ -185,6 +185,9 @@ gst_d3dvideosink_finalize (GObject * gobject)
 
   GST_DEBUG_OBJECT (sink, " ");
 
+  gst_object_replace ((GstObject **) & sink->pool, NULL);
+  gst_object_replace ((GstObject **) & sink->fallback_pool, NULL);
+
   gst_caps_replace (&sink->supported_caps, NULL);
 
   g_rec_mutex_clear (&sink->lock);
@@ -274,6 +277,9 @@ gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)
   gint display_par_n = 1, display_par_d = 1;    /* display's PAR */
   guint num, den;
   gchar *tmp = NULL;
+  GstBufferPool *newpool, *oldpool;
+  GstBufferPool *newfbpool, *oldfbpool;
+  GstStructure *config;
 
   GST_DEBUG_OBJECT (bsink, " ");
 
@@ -356,6 +362,40 @@ gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)
   /* Create a window (or start using an application-supplied one, then connect the graph */
   d3d_prepare_window (sink);
 
+  newpool = gst_d3dsurface_buffer_pool_new (sink);
+  config = gst_buffer_pool_get_config (newpool);
+  /* we need at least 2 buffer because we hold on to the last one */
+  gst_buffer_pool_config_set_params (config, caps, sink->info.size, 2, 0);
+  if (!gst_buffer_pool_set_config (newpool, config)) {
+    gst_object_unref (newpool);
+    GST_ERROR_OBJECT (sink, "Failed to set buffer pool configuration");
+    return FALSE;
+  }
+
+  newfbpool = gst_d3dsurface_buffer_pool_new (sink);
+  config = gst_buffer_pool_get_config (newfbpool);
+  /* we need at least 2 buffer because we hold on to the last one */
+  gst_buffer_pool_config_set_params (config, caps, sink->info.size, 2, 0);
+  /* Fallback pool must use videometa */
+  gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
+  if (!gst_buffer_pool_set_config (newfbpool, config)) {
+    gst_object_unref (newfbpool);
+    GST_ERROR_OBJECT (sink, "Failed to set buffer pool configuration");
+    return FALSE;
+  }
+
+  GST_OBJECT_LOCK (sink);
+  oldpool = sink->pool;
+  sink->pool = newpool;
+  oldfbpool = sink->fallback_pool;
+  sink->fallback_pool = newfbpool;
+  GST_OBJECT_UNLOCK (sink);
+
+  if (oldpool)
+    gst_object_unref (oldpool);
+  if (oldfbpool)
+    gst_object_unref (oldfbpool);
+
   return TRUE;
   /* ERRORS */
 incompatible_caps:
@@ -411,8 +451,73 @@ gst_d3dvideosink_stop (GstBaseSink * bsink)
 static gboolean
 gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
 {
+  GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink);
+  GstBufferPool *pool;
+  GstStructure *config;
+  GstCaps *caps;
+  guint size;
+  gboolean need_pool;
+
+  gst_query_parse_allocation (query, &caps, &need_pool);
+  if (!caps) {
+    GST_DEBUG_OBJECT (sink, "no caps specified");
+    return FALSE;
+  }
+
   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
 
+  GST_OBJECT_LOCK (sink);
+  pool = sink->pool ? gst_object_ref (sink->pool) : NULL;
+  GST_OBJECT_UNLOCK (sink);
+
+  if (pool) {
+    GstCaps *pcaps;
+
+    /* we had a pool, check caps */
+    GST_DEBUG_OBJECT (sink, "check existing pool caps");
+    config = gst_buffer_pool_get_config (pool);
+    gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
+
+    if (!gst_caps_is_equal (caps, pcaps)) {
+      GST_DEBUG_OBJECT (sink, "pool has different caps");
+      /* different caps, we can't use this pool */
+      gst_object_unref (pool);
+      pool = NULL;
+    }
+    gst_structure_free (config);
+  }
+
+  if (pool == NULL && need_pool) {
+    GstVideoInfo info;
+
+    if (!gst_video_info_from_caps (&info, caps)) {
+      GST_ERROR_OBJECT (sink, "allocation query has invalid caps %"
+          GST_PTR_FORMAT, caps);
+      return FALSE;
+    }
+
+    GST_DEBUG_OBJECT (sink, "create new pool");
+    pool = gst_d3dsurface_buffer_pool_new (sink);
+
+    /* the normal size of a frame */
+    size = info.size;
+
+    config = gst_buffer_pool_get_config (pool);
+    /* we need at least 2 buffer because we hold on to the last one */
+    gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
+    if (!gst_buffer_pool_set_config (pool, config)) {
+      gst_object_unref (pool);
+      GST_ERROR_OBJECT (sink, "failed to set pool configuration");
+      return FALSE;
+    }
+  }
+
+  if (pool) {
+    /* we need at least 2 buffer because we hold on to the last one */
+    gst_query_add_allocation_pool (query, pool, size, 2, 0);
+    gst_object_unref (pool);
+  }
+
   return TRUE;
 }
 
index 4531699..2ec2908 100644 (file)
@@ -52,6 +52,9 @@ struct _GstD3DVideoSink
   GstVideoInfo       info;
   gint               width;
   gint               height;
+  GstBufferPool      *pool;
+  GstBufferPool      *fallback_pool;
+  GstBuffer          *fallback_buffer;
 
   GstVideoRectangle  render_rect;