d3d11videosink: Draw window with cached texture on resize
authorSeungha Yang <seungha.yang@navercorp.com>
Mon, 2 Dec 2019 14:27:42 +0000 (23:27 +0900)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Thu, 5 Dec 2019 02:29:18 +0000 (02:29 +0000)
This would render nicer than presenting scene with broken aspect ratio,
especially in case of low framerate.

sys/d3d11/gstd3d11videosink.c
sys/d3d11/gstd3d11videosink.h
sys/d3d11/gstd3d11window.c
sys/d3d11/gstd3d11window.h

index 5b62fd1..35e96b6 100644 (file)
@@ -74,6 +74,7 @@ static gboolean gst_d3d11_video_sink_propose_allocation (GstBaseSink * sink,
     GstQuery * query);
 static gboolean gst_d3d11_video_sink_query (GstBaseSink * sink,
     GstQuery * query);
+static gboolean gst_d3d11_video_sink_unlock (GstBaseSink * sink);
 
 static GstFlowReturn
 gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf);
@@ -137,6 +138,7 @@ gst_d3d11_video_sink_class_init (GstD3D11VideoSinkClass * klass)
   basesink_class->propose_allocation =
       GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_propose_allocation);
   basesink_class->query = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_query);
+  basesink_class->unlock = GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_unlock);
 
   videosink_class->show_frame =
       GST_DEBUG_FUNCPTR (gst_d3d11_video_sink_show_frame);
@@ -246,10 +248,9 @@ gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
   gint video_par_n, video_par_d;        /* video's PAR */
   gint display_par_n = 1, display_par_d = 1;    /* display's PAR */
   guint num, den;
-  D3D11_TEXTURE2D_DESC desc = { 0, };
-  ID3D11Texture2D *staging;
   GError *error = NULL;
   const GstD3D11Format *d3d11_format = NULL;
+  GstStructure *config;
 
   GST_DEBUG_OBJECT (self, "set caps %" GST_PTR_FORMAT, caps);
 
@@ -370,27 +371,16 @@ gst_d3d11_video_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
     return FALSE;
   }
 
-  if (self->fallback_staging) {
-    gst_d3d11_device_release_texture (self->device, self->fallback_staging);
-    self->fallback_staging = NULL;
-  }
-
-  desc.Width = GST_VIDEO_SINK_WIDTH (self);
-  desc.Height = GST_VIDEO_SINK_HEIGHT (self);
-  desc.MipLevels = 1;
-  desc.Format = self->dxgi_format;
-  desc.SampleDesc.Count = 1;
-  desc.ArraySize = 1;
-  desc.Usage = D3D11_USAGE_STAGING;
-  desc.CPUAccessFlags = (D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE);
-
-  staging = gst_d3d11_device_create_texture (self->device, &desc, NULL);
-  if (!staging) {
-    GST_ERROR_OBJECT (self, "cannot create fallback staging texture");
-    return FALSE;
+  if (self->fallback_pool) {
+    gst_buffer_pool_set_active (self->fallback_pool, FALSE);
+    gst_object_unref (self->fallback_pool);
   }
 
-  self->fallback_staging = staging;
+  self->fallback_pool = gst_d3d11_buffer_pool_new (self->device);
+  config = gst_buffer_pool_get_config (self->fallback_pool);
+  gst_buffer_pool_config_set_params (config,
+      caps, GST_VIDEO_INFO_SIZE (&self->info), 0, 2);
+  gst_buffer_pool_set_config (self->fallback_pool, config);
 
   return TRUE;
 
@@ -492,9 +482,10 @@ gst_d3d11_video_sink_stop (GstBaseSink * sink)
 
   GST_DEBUG_OBJECT (self, "Stop");
 
-  if (self->fallback_staging) {
-    ID3D11Texture2D_Release (self->fallback_staging);
-    self->fallback_staging = NULL;
+  if (self->fallback_pool) {
+    gst_buffer_pool_set_active (self->fallback_pool, FALSE);
+    gst_object_unref (self->fallback_pool);
+    self->fallback_pool = NULL;
   }
 
   gst_clear_object (&self->device);
@@ -604,75 +595,28 @@ gst_d3d11_video_sink_query (GstBaseSink * sink, GstQuery * query)
   return GST_BASE_SINK_CLASS (parent_class)->query (sink, query);
 }
 
-typedef struct
-{
-  GstD3D11VideoSink *sink;
-
-  GstVideoFrame *frame;
-  ID3D11Resource *resource;
-
-  GstFlowReturn ret;
-} FrameUploadData;
-
-static void
-_upload_frame (GstD3D11Device * device, gpointer data)
+static gboolean
+gst_d3d11_video_sink_unlock (GstBaseSink * sink)
 {
-  GstD3D11VideoSink *self;
-  HRESULT hr;
-  ID3D11DeviceContext *device_context;
-  FrameUploadData *upload_data = (FrameUploadData *) data;
-  D3D11_MAPPED_SUBRESOURCE d3d11_map;
-  guint i;
-  guint8 *dst;
-
-  self = upload_data->sink;
-
-  device_context = gst_d3d11_device_get_device_context_handle (device);
-
-  hr = ID3D11DeviceContext_Map (device_context,
-      upload_data->resource, 0, D3D11_MAP_WRITE, 0, &d3d11_map);
-
-  if (FAILED (hr)) {
-    GST_ERROR_OBJECT (self, "cannot map d3d11 staging texture");
-    upload_data->ret = GST_FLOW_ERROR;
-    return;
-  }
-
-  dst = d3d11_map.pData;
-  for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (upload_data->frame); i++) {
-    guint w, h;
-    guint j;
-    guint8 *src;
-    gint src_stride;
+  GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
 
-    w = GST_VIDEO_FRAME_COMP_WIDTH (upload_data->frame, i) *
-        GST_VIDEO_FRAME_COMP_PSTRIDE (upload_data->frame, i);
-    h = GST_VIDEO_FRAME_COMP_HEIGHT (upload_data->frame, i);
-    src = GST_VIDEO_FRAME_PLANE_DATA (upload_data->frame, i);
-    src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (upload_data->frame, i);
+  if (self->window)
+    gst_d3d11_window_flush (self->window);
 
-    for (j = 0; j < h; j++) {
-      memcpy (dst, src, w);
-      dst += d3d11_map.RowPitch;
-      src += src_stride;
-    }
-  }
-
-  ID3D11DeviceContext_Unmap (device_context, upload_data->resource, 0);
+  return TRUE;
 }
 
 static GstFlowReturn
 gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
 {
   GstD3D11VideoSink *self = GST_D3D11_VIDEO_SINK (sink);
-  GstVideoFrame frame;
-  FrameUploadData data;
-  ID3D11Texture2D *texture;
   GstMapInfo map;
   GstFlowReturn ret;
   GstMemory *mem;
   GstVideoRectangle rect = { 0, };
   GstVideoCropMeta *crop;
+  GstBuffer *render_buf;
+  gboolean need_unref = FALSE;
 
   if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
       && gst_memory_is_type (mem, GST_D3D11_MEMORY_NAME)) {
@@ -691,30 +635,49 @@ gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
       gst_memory_unmap (mem, &map);
     }
 
-    texture = dmem->texture;
+    render_buf = buf;
   } else {
+    GstVideoFrame frame, fallback_frame;
+
+    if (!self->fallback_pool ||
+        !gst_buffer_pool_set_active (self->fallback_pool, TRUE) ||
+        !gst_buffer_pool_acquire_buffer (self->fallback_pool, &render_buf,
+            NULL)) {
+      GST_ERROR_OBJECT (self, "fallback pool is unavailable");
+
+      return GST_FLOW_ERROR;
+    }
+
     if (!gst_video_frame_map (&frame, &self->info, buf, GST_MAP_READ)) {
       GST_ERROR_OBJECT (self, "cannot map video frame");
+      gst_buffer_unref (render_buf);
+
+      return GST_FLOW_ERROR;
+    }
+
+    if (!gst_video_frame_map (&fallback_frame, &self->info, buf, GST_MAP_WRITE)) {
+      GST_ERROR_OBJECT (self, "cannot map fallback frame");
+      gst_video_frame_unmap (&frame);
+      gst_buffer_unref (render_buf);
+
       return GST_FLOW_ERROR;
     }
 
     GST_TRACE_OBJECT (self,
         "buffer %p out of our pool, write to stage buffer", buf);
 
-    data.sink = self;
-    data.frame = &frame;
-    data.resource = (ID3D11Resource *) self->fallback_staging;
-    data.ret = GST_FLOW_OK;
+    if (!gst_video_frame_copy (&fallback_frame, &frame)) {
+      GST_ERROR_OBJECT (self, "cannot copy to fallback frame");
+      gst_video_frame_unmap (&frame);
+      gst_video_frame_unmap (&fallback_frame);
+      gst_buffer_unref (render_buf);
 
-    gst_d3d11_device_thread_add (self->device, (GstD3D11DeviceThreadFunc)
-        _upload_frame, &data);
-
-    if (data.ret != GST_FLOW_OK)
-      return data.ret;
+      return GST_FLOW_ERROR;
+    }
 
     gst_video_frame_unmap (&frame);
-
-    texture = self->fallback_staging;
+    gst_video_frame_unmap (&fallback_frame);
+    need_unref = TRUE;
   }
 
   gst_d3d11_window_show (self->window);
@@ -730,7 +693,9 @@ gst_d3d11_video_sink_show_frame (GstVideoSink * sink, GstBuffer * buf)
     rect.h = self->video_height;
   }
 
-  ret = gst_d3d11_window_render (self->window, texture, &rect);
+  ret = gst_d3d11_window_render (self->window, render_buf, &rect);
+  if (need_unref)
+    gst_buffer_unref (render_buf);
 
   if (ret == GST_D3D11_WINDOW_FLOW_CLOSED) {
     GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
index fa2f202..5e6ac7c 100644 (file)
@@ -63,7 +63,7 @@ struct _GstD3D11VideoSink
   GstVideoRectangle render_rect;
   gboolean pending_render_rect;
 
-  ID3D11Texture2D *fallback_staging;
+  GstBufferPool *fallback_pool;
 };
 
 struct _GstD3D11VideoSinkClass
index f8dd386..316d591 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "gstd3d11window.h"
 #include "gstd3d11device.h"
+#include "gstd3d11memory.h"
 
 #include <windows.h>
 
@@ -66,6 +67,15 @@ GST_DEBUG_CATEGORY_STATIC (gst_d3d11_window_debug);
 #define gst_d3d11_window_parent_class parent_class
 G_DEFINE_TYPE (GstD3D11Window, gst_d3d11_window, GST_TYPE_OBJECT);
 
+typedef struct
+{
+  GstD3D11Window *window;
+  GstVideoRectangle *rect;
+  GstBuffer *buffer;
+
+  GstFlowReturn ret;
+} FramePresentData;
+
 static void gst_d3d11_window_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
 static void gst_d3d11_window_get_property (GObject * object, guint prop_id,
@@ -77,6 +87,8 @@ static gboolean _create_window (GstD3D11Window * self, GError ** error);
 static void _open_window (GstD3D11Window * self);
 static void _close_window (GstD3D11Window * self);
 static void release_external_win_id (GstD3D11Window * self);
+static void _present_on_device_thread (GstD3D11Device * device,
+    FramePresentData * data);
 
 static void
 gst_d3d11_window_class_init (GstD3D11WindowClass * klass)
@@ -246,6 +258,7 @@ gst_d3d11_window_dispose (GObject * object)
         (GstD3D11DeviceThreadFunc) gst_d3d11_window_release_resources, self);
   }
 
+  gst_clear_buffer (&self->cached_buffer);
   gst_clear_object (&self->device);
 
   G_OBJECT_CLASS (parent_class)->dispose (object);
@@ -580,6 +593,16 @@ gst_d3d11_window_on_resize (GstD3D11Device * device, GstD3D11Window * window)
   }
 
   ID3D11DeviceContext_OMSetRenderTargets (d3d11_context, 1, &window->rtv, NULL);
+  if (window->cached_buffer) {
+    FramePresentData present_data = { 0, };
+
+    present_data.window = window;
+    present_data.rect = &window->rect;
+    present_data.buffer = window->cached_buffer;
+    GST_DEBUG_OBJECT (window, "redraw cached buffer");
+
+    _present_on_device_thread (window->device, &present_data);
+  }
 }
 
 static void
@@ -1032,15 +1055,6 @@ gst_d3d11_window_get_surface_dimensions (GstD3D11Window * window,
     *height = window->surface_height;
 }
 
-typedef struct
-{
-  GstD3D11Window *window;
-  ID3D11Resource *resource;
-  GstVideoRectangle *rect;
-
-  GstFlowReturn ret;
-} FramePresentData;
-
 static void
 _present_on_device_thread (GstD3D11Device * device, FramePresentData * data)
 {
@@ -1050,23 +1064,28 @@ _present_on_device_thread (GstD3D11Device * device, FramePresentData * data)
   float black[] = { 0.0f, 0.0f, 0.0f, 0.0f };
   D3D11_BOX src_box;
 
-  src_box.left = data->rect->x;
-  src_box.right = data->rect->x + data->rect->w;
-  src_box.top = data->rect->y;
-  src_box.bottom = data->rect->y + data->rect->h;
-  src_box.front = 0;
-  src_box.back = 1;
-
   device_context = gst_d3d11_device_get_device_context_handle (device);
+  gst_buffer_replace (&self->cached_buffer, data->buffer);
+
+  if (self->cached_buffer) {
+    GstD3D11Memory *mem =
+        (GstD3D11Memory *) gst_buffer_peek_memory (self->cached_buffer, 0);
+
+    self->rect = *data->rect;
+    src_box.left = self->rect.x;
+    src_box.right = self->rect.x + self->rect.w;
+    src_box.top = self->rect.y;
+    src_box.bottom = self->rect.y + self->rect.h;
+    src_box.front = 0;
+    src_box.back = 1;
 
-  if (data->resource) {
     ID3D11DeviceContext_OMSetRenderTargets (device_context,
         1, &self->rtv, NULL);
     ID3D11DeviceContext_ClearRenderTargetView (device_context, self->rtv,
         black);
     ID3D11DeviceContext_CopySubresourceRegion (device_context,
         (ID3D11Resource *) self->backbuffer, 0, self->render_rect.x,
-        self->render_rect.y, 0, data->resource, 0, &src_box);
+        self->render_rect.y, 0, (ID3D11Resource *) mem->texture, 0, &src_box);
   }
 
   hr = IDXGISwapChain_Present (self->swap_chain, 0, DXGI_PRESENT_DO_NOT_WAIT);
@@ -1080,14 +1099,22 @@ _present_on_device_thread (GstD3D11Device * device, FramePresentData * data)
 }
 
 GstFlowReturn
-gst_d3d11_window_render (GstD3D11Window * window, ID3D11Texture2D * texture,
+gst_d3d11_window_render (GstD3D11Window * window, GstBuffer * buffer,
     GstVideoRectangle * rect)
 {
   FramePresentData data;
+  GstMemory *mem;
 
   g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), GST_FLOW_ERROR);
   g_return_val_if_fail (rect != NULL, GST_FLOW_ERROR);
 
+  mem = gst_buffer_peek_memory (buffer, 0);
+  if (!gst_is_d3d11_memory (mem)) {
+    GST_ERROR_OBJECT (window, "Invalid buffer");
+
+    return GST_FLOW_ERROR;
+  }
+
   if (!window->external_win_id && !window->internal_win_id) {
     GST_ERROR_OBJECT (window, "Output window was closed");
     return GST_D3D11_WINDOW_FLOW_CLOSED;
@@ -1105,8 +1132,8 @@ gst_d3d11_window_render (GstD3D11Window * window, ID3D11Texture2D * texture,
   GST_OBJECT_UNLOCK (window);
 
   data.window = window;
-  data.resource = (ID3D11Resource *) texture;
   data.rect = rect;
+  data.buffer = buffer;
   data.ret = GST_FLOW_OK;
 
   gst_d3d11_device_thread_add (window->device,
@@ -1114,3 +1141,21 @@ gst_d3d11_window_render (GstD3D11Window * window, ID3D11Texture2D * texture,
 
   return data.ret;
 }
+
+static void
+gst_d3d11_window_flush_internal (GstD3D11Device * device,
+    GstD3D11Window * window)
+{
+  gst_clear_buffer (&window->cached_buffer);
+}
+
+gboolean
+gst_d3d11_window_flush (GstD3D11Window * window)
+{
+  g_return_val_if_fail (GST_IS_D3D11_WINDOW (window), FALSE);
+
+  gst_d3d11_device_thread_add (window->device,
+      (GstD3D11DeviceThreadFunc) gst_d3d11_window_flush_internal, window);
+
+  return TRUE;
+}
index 0d573fc..91229e3 100644 (file)
@@ -48,8 +48,12 @@ struct _GstD3D11Window
   GstVideoMasteringDisplayInfo mastering_display_info;
   GstVideoContentLightLevel content_light_level;
 
+  /* calculated rect with aspect ratio and window area */
   GstVideoRectangle render_rect;
 
+  /* requested rect via gst_d3d11_window_render */
+  GstVideoRectangle rect;
+
   GMutex lock;
   GCond cond;
 
@@ -89,6 +93,8 @@ struct _GstD3D11Window
 
   gboolean force_aspect_ratio;
   gboolean enable_navigation_events;
+
+  GstBuffer *cached_buffer;
 };
 
 struct _GstD3D11WindowClass
@@ -123,9 +129,11 @@ gboolean gst_d3d11_window_prepare (GstD3D11Window * window,
                                    GError ** error);
 
 GstFlowReturn gst_d3d11_window_render (GstD3D11Window * window,
-                                       ID3D11Texture2D * texture,
+                                       GstBuffer * buffer,
                                        GstVideoRectangle * src_rect);
 
+gboolean      gst_d3d11_window_flush  (GstD3D11Window * window);
+
 G_END_DECLS
 
 #endif /* __GST_D3D11_WINDOW_H__ */