From 86b01bb291d8453a7ad440b03df7a65a052ae84a Mon Sep 17 00:00:00 2001 From: Aaron Boxer Date: Tue, 22 Oct 2019 14:10:30 -0400 Subject: [PATCH] d3dvideosink: support OverlayComposition for GPU overlay compositing --- sys/d3dvideosink/d3dhelpers.c | 59 +++- sys/d3dvideosink/d3dhelpers.h | 5 + sys/d3dvideosink/d3dvideosink.c | 58 +++- sys/d3dvideosink/gstd3d9overlay.c | 471 ++++++++++++++++++++++++++++++ sys/d3dvideosink/gstd3d9overlay.h | 58 ++++ sys/d3dvideosink/meson.build | 1 + 6 files changed, 631 insertions(+), 21 deletions(-) create mode 100644 sys/d3dvideosink/gstd3d9overlay.c create mode 100644 sys/d3dvideosink/gstd3d9overlay.h diff --git a/sys/d3dvideosink/d3dhelpers.c b/sys/d3dvideosink/d3dhelpers.c index 031c12f9e1..c9e7a82d87 100644 --- a/sys/d3dvideosink/d3dhelpers.c +++ b/sys/d3dvideosink/d3dhelpers.c @@ -25,6 +25,7 @@ #include "d3dvideosink.h" #include "d3dhelpers.h" +#include "gstd3d9overlay.h" #include @@ -840,10 +841,10 @@ d3d_supported_caps (GstD3DVideoSink * sink) gst_value_list_append_value (&va, &v); } - caps = gst_caps_new_simple ("video/x-raw", - "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, - "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, - "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + caps = + gst_caps_make_writable (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD + (sink))); + gst_caps_set_value (caps, "format", &va); g_value_unset (&v); g_value_unset (&va); @@ -897,7 +898,7 @@ end: return ret; } -static gboolean +gboolean d3d_get_hwnd_window_size (HWND hwnd, gint * width, gint * height) { RECT sz; @@ -1534,6 +1535,7 @@ d3d_resize_swap_chain (GstD3DVideoSink * sink) } sink->d3d.swapchain = swapchain; + sink->d3d.overlay_needs_resize = TRUE; ret = TRUE; end: @@ -1747,15 +1749,35 @@ d3d_present_swap_chain (GstD3DVideoSink * sink) CHECK_D3D_SWAPCHAIN (sink, end); /* Set the render target to our swap chain */ - IDirect3DSwapChain9_GetBackBuffer (sink->d3d.swapchain, 0, + hr = IDirect3DSwapChain9_GetBackBuffer (sink->d3d.swapchain, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer); - IDirect3DDevice9_SetRenderTarget (klass->d3d.device.d3d_device, 0, + ERROR_CHECK_HR (hr) { + CASE_HR_ERR (D3DERR_INVALIDCALL); + CASE_HR_ERR_END (sink, "IDirect3DSwapChain9_GetBackBuffer"); + goto end; + } + hr = IDirect3DDevice9_SetRenderTarget (klass->d3d.device.d3d_device, 0, back_buffer); - IDirect3DSurface9_Release (back_buffer); + ERROR_CHECK_HR (hr) { + CASE_HR_ERR (D3DERR_INVALIDCALL); + CASE_HR_ERR_END (sink, "IDirect3DDevice9_SetRenderTarget"); + goto end; + } + hr = IDirect3DSurface9_Release (back_buffer); + ERROR_CHECK_HR (hr) { + CASE_HR_ERR (D3DERR_INVALIDCALL); + CASE_HR_ERR_END (sink, "IDirect3DSurface9_Release"); + goto end; + } /* Clear the target */ - IDirect3DDevice9_Clear (klass->d3d.device.d3d_device, 0, NULL, + hr = IDirect3DDevice9_Clear (klass->d3d.device.d3d_device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0); + ERROR_CHECK_HR (hr) { + CASE_HR_ERR (D3DERR_INVALIDCALL); + CASE_HR_ERR_END (sink, "IDirect3DDevice9_Clear"); + goto end; + } hr = IDirect3DDevice9_BeginScene (klass->d3d.device.d3d_device); ERROR_CHECK_HR (hr) { @@ -1764,11 +1786,26 @@ d3d_present_swap_chain (GstD3DVideoSink * sink) goto end; } + if (!gst_d3d9_overlay_set_render_state (sink)) { + IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device); + goto end; + } + /* Stretch and blit ops, to copy offscreen surface buffer * to Display back buffer. */ - d3d_stretch_and_copy (sink, back_buffer); - IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device); + if (!d3d_stretch_and_copy (sink, back_buffer) || + !gst_d3d9_overlay_render (sink)) { + IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device); + goto end; + } + + hr = IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device); + ERROR_CHECK_HR (hr) { + CASE_HR_ERR (D3DERR_INVALIDCALL); + CASE_HR_ERR_END (sink, "IDirect3DDevice9_EndScene"); + goto end; + } if (d3d_get_render_rects (sink->d3d.render_rect, &dstr, &srcr)) { pDestRect = &dstr; diff --git a/sys/d3dvideosink/d3dhelpers.h b/sys/d3dvideosink/d3dhelpers.h index 36623290de..13bad4e69c 100644 --- a/sys/d3dvideosink/d3dhelpers.h +++ b/sys/d3dvideosink/d3dhelpers.h @@ -90,6 +90,10 @@ typedef struct _GstD3DData { GstVideoRectangle * render_rect; gboolean renderable; gboolean device_lost; + + /* list of GstD3DVideoSinkOverlay structs */ + GList * overlay; + gboolean overlay_needs_resize; } GstD3DData; gboolean d3d_class_init(GstD3DVideoSink * klass); @@ -103,6 +107,7 @@ void d3d_expose_window(GstD3DVideoSink * sink); GstFlowReturn d3d_render_buffer(GstD3DVideoSink * sink, GstBuffer * buf); GstCaps * d3d_supported_caps(GstD3DVideoSink * sink); gboolean d3d_set_render_format(GstD3DVideoSink * sink); +gboolean d3d_get_hwnd_window_size (HWND hwnd, gint * width, gint * height); #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)) diff --git a/sys/d3dvideosink/d3dvideosink.c b/sys/d3dvideosink/d3dvideosink.c index 582b23fc18..73aaf9c3d5 100644 --- a/sys/d3dvideosink/d3dvideosink.c +++ b/sys/d3dvideosink/d3dvideosink.c @@ -24,6 +24,7 @@ #endif #include "d3dvideosink.h" +#include "gstd3d9overlay.h" #define ELEMENT_NAME "d3dvideosink" @@ -42,14 +43,15 @@ enum #define DEFAULT_STREAM_STOP_ON_CLOSE TRUE #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE +#define GST_D3D9_VIDEO_FORMATS \ + "{ I420, YV12, UYVY, YUY2, NV12, BGRx, RGBx, BGRA, RGBA, BGR, RGB16, RGB15 }" static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw, " - "format = (string) { I420, YV12, UYVY, YUY2, NV12, BGRx, RGBx, RGBA, BGRA, BGR, RGB16, RGB15 }, " - "framerate = (fraction) [ 0, MAX ], " - "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]") - ); + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_D3D9_VIDEO_FORMATS) ";" + GST_VIDEO_CAPS_MAKE_WITH_FEATURES + (GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, + GST_D3D9_VIDEO_FORMATS))); GST_DEBUG_CATEGORY (gst_d3dvideosink_debug); #define GST_CAT_DEFAULT gst_d3dvideosink_debug @@ -81,6 +83,8 @@ static GstCaps *gst_d3dvideosink_get_caps (GstBaseSink * basesink, static gboolean gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps); static gboolean gst_d3dvideosink_start (GstBaseSink * sink); static gboolean gst_d3dvideosink_stop (GstBaseSink * sink); +static GstFlowReturn gst_d3dvideosink_prepare (GstBaseSink * bsink, + GstBuffer * buf); static gboolean gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query); /* GstVideoSink */ @@ -118,6 +122,7 @@ gst_d3dvideosink_class_init (GstD3DVideoSinkClass * klass) gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3dvideosink_set_caps); gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_d3dvideosink_start); gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_d3dvideosink_stop); + gstbasesink_class->prepare = GST_DEBUG_FUNCPTR (gst_d3dvideosink_prepare); gstbasesink_class->propose_allocation = GST_DEBUG_FUNCPTR (gst_d3dvideosink_propose_allocation); @@ -172,7 +177,8 @@ gst_d3dvideosink_init (GstD3DVideoSink * sink) sink->stream_stop_on_close = DEFAULT_STREAM_STOP_ON_CLOSE; sink->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS; sink->d3d.surface = NULL; - + sink->d3d.overlay = NULL; + sink->d3d.overlay_needs_resize = FALSE; g_rec_mutex_init (&sink->lock); } @@ -442,11 +448,30 @@ gst_d3dvideosink_stop (GstBaseSink * bsink) GST_DEBUG_OBJECT (bsink, "Stop() called"); d3d_stop (sink); + gst_d3d9_overlay_free (sink); d3d_class_destroy (sink); return TRUE; } +static GstFlowReturn +gst_d3dvideosink_prepare (GstBaseSink * bsink, GstBuffer * buf) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); + GstFlowReturn ret = GST_FLOW_OK; + + GST_TRACE ("preparing buffer:%p", buf); + + if (GST_VIDEO_SINK_WIDTH (sink) < 1 || GST_VIDEO_SINK_HEIGHT (sink) < 1) { + return GST_FLOW_NOT_NEGOTIATED; + } + + GST_OBJECT_LOCK (sink); + ret = gst_d3d9_overlay_prepare (sink, buf); + GST_OBJECT_UNLOCK (sink); + return ret; +} + static gboolean gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query) { @@ -456,16 +481,13 @@ gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query) GstCaps *caps; guint size; gboolean need_pool; + GstStructure *allocation_meta = NULL; 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_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); - #ifdef DISABLE_BUFFER_POOL return TRUE; #endif @@ -522,6 +544,22 @@ gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query) if (pool) gst_object_unref (pool); + if (sink->width != 0 && sink->height != 0) { + allocation_meta = + gst_structure_new ("GstVideoOverlayCompositionMeta", + "width", G_TYPE_UINT, sink->width, + "height", G_TYPE_UINT, sink->height, NULL); + GST_DEBUG ("sending allocation query with size %dx%d", + sink->width, sink->height); + } + gst_query_add_allocation_meta (query, + GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta); + if (allocation_meta) + gst_structure_free (allocation_meta); + + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); + return TRUE; } diff --git a/sys/d3dvideosink/gstd3d9overlay.c b/sys/d3dvideosink/gstd3d9overlay.c new file mode 100644 index 0000000000..e2573f1e73 --- /dev/null +++ b/sys/d3dvideosink/gstd3d9overlay.c @@ -0,0 +1,471 @@ +/* GStreamer + * Copyright (C) 2019 Aaron Boxer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "d3dvideosink.h" +#include "gstd3d9overlay.h" + +#include + +GST_DEBUG_CATEGORY_EXTERN (gst_d3dvideosink_debug); +#define GST_CAT_DEFAULT gst_d3dvideosink_debug + +#define ERROR_CHECK_HR(hr) \ + if(hr != S_OK) { \ + const gchar * str_err=NULL, *t1=NULL; \ + gchar tmp[128]=""; \ + switch(hr) +#define CASE_HR_ERR(hr_err) \ + case hr_err: str_err = #hr_err; break; +#define CASE_HR_DBG_ERR_END(sink, gst_err_msg, level) \ + default: \ + t1=gst_err_msg; \ + sprintf(tmp, "HR-SEV:%u HR-FAC:%u HR-CODE:%u", (guint)HRESULT_SEVERITY(hr), (guint)HRESULT_FACILITY(hr), (guint)HRESULT_CODE(hr)); \ + str_err = tmp; \ + } /* end switch */ \ + GST_CAT_LEVEL_LOG(GST_CAT_DEFAULT, level, sink, "%s HRESULT: %s", t1?t1:"", str_err); +#define CASE_HR_ERR_END(sink, gst_err_msg) \ + CASE_HR_DBG_ERR_END(sink, gst_err_msg, GST_LEVEL_ERROR) +#define CASE_HR_DBG_END(sink, gst_err_msg) \ + CASE_HR_DBG_ERR_END(sink, gst_err_msg, GST_LEVEL_DEBUG) +#define D3D9_CHECK(call) hr = call; \ + ERROR_CHECK_HR (hr) { \ + CASE_HR_ERR (D3DERR_INVALIDCALL); \ + CASE_HR_ERR_END (sink, #call); \ + goto end; \ + } + + +typedef struct _textured_vertex +{ + float x, y, z, rhw; // The transformed(screen space) position for the vertex. + float tu, tv; // Texture coordinates +} textured_vertex; + +/* Transformed vertex with 1 set of texture coordinates */ +static DWORD tri_fvf = D3DFVF_XYZRHW | D3DFVF_TEX1; + +static gboolean +_is_rectangle_in_overlays (GList * overlays, + GstVideoOverlayRectangle * rectangle); +static gboolean +_is_overlay_in_composition (GstVideoOverlayComposition * composition, + GstD3DVideoSinkOverlay * overlay); +static HRESULT +gst_d3d9_overlay_init_vb (GstD3DVideoSink * sink, + GstD3DVideoSinkOverlay * overlay); +static gboolean gst_d3d9_overlay_resize (GstD3DVideoSink * sink); +static void +gst_d3d9_overlay_calc_dest_rect (GstD3DVideoSink * sink, RECT * dest_rect); +static void gst_d3d9_overlay_free_overlay (GstD3DVideoSink * sink, + GstD3DVideoSinkOverlay * overlay); + +static void +gst_d3d9_overlay_calc_dest_rect (GstD3DVideoSink * sink, RECT * dest_rect) +{ + if (sink->force_aspect_ratio) { + gint window_width; + gint window_height; + GstVideoRectangle src; + GstVideoRectangle dst; + GstVideoRectangle result; + + memset (&dst, 0, sizeof (dst)); + memset (&src, 0, sizeof (src)); + + /* Set via GstXOverlay set_render_rect */ + if (sink->d3d.render_rect) { + memcpy (&dst, sink->d3d.render_rect, sizeof (dst)); + } else { + d3d_get_hwnd_window_size (sink->d3d.window_handle, &window_width, + &window_height); + dst.w = window_width; + dst.h = window_height; + } + + src.w = GST_VIDEO_SINK_WIDTH (sink); + src.h = GST_VIDEO_SINK_HEIGHT (sink); + + gst_video_sink_center_rect (src, dst, &result, TRUE); + + dest_rect->left = result.x; + dest_rect->top = result.y; + dest_rect->right = result.x + result.w; + dest_rect->bottom = result.y + result.h; + } else if (sink->d3d.render_rect) { + dest_rect->left = 0; + dest_rect->top = 0; + dest_rect->right = sink->d3d.render_rect->w; + dest_rect->bottom = sink->d3d.render_rect->h; + } else { + /* get client window size */ + GetClientRect (sink->d3d.window_handle, dest_rect); + } +} + +static void +gst_d3d9_overlay_free_overlay (GstD3DVideoSink * sink, + GstD3DVideoSinkOverlay * overlay) +{ + if (G_LIKELY (overlay)) { + if (overlay->texture) { + HRESULT hr = IDirect3DTexture9_Release (overlay->texture); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, "Failed to release D3D texture"); + } + } + if (overlay->g_list_vb) { + HRESULT hr = IDirect3DVertexBuffer9_Release (overlay->g_list_vb); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, "Failed to release D3D vertex buffer"); + } + } + gst_video_overlay_rectangle_unref (overlay->rectangle); + g_free (overlay); + } +} + +static gboolean +_is_rectangle_in_overlays (GList * overlays, + GstVideoOverlayRectangle * rectangle) +{ + GList *l; + + for (l = overlays; l != NULL; l = l->next) { + GstD3DVideoSinkOverlay *overlay = (GstD3DVideoSinkOverlay *) l->data; + if (overlay->rectangle == rectangle) + return TRUE; + } + return FALSE; +} + +static gboolean +_is_overlay_in_composition (GstVideoOverlayComposition * composition, + GstD3DVideoSinkOverlay * overlay) +{ + guint i; + + for (i = 0; i < gst_video_overlay_composition_n_rectangles (composition); i++) { + GstVideoOverlayRectangle *rectangle = + gst_video_overlay_composition_get_rectangle (composition, i); + if (overlay->rectangle == rectangle) + return TRUE; + } + return FALSE; +} + +GstFlowReturn +gst_d3d9_overlay_prepare (GstD3DVideoSink * sink, GstBuffer * buf) +{ + GList *l = NULL; + GstVideoOverlayComposition *composition = NULL; + guint num_overlays, i; + GstVideoOverlayCompositionMeta *composition_meta = + gst_buffer_get_video_overlay_composition_meta (buf); + + if (!composition_meta) { + gst_d3d9_overlay_free (sink); + return GST_FLOW_OK; + } + l = sink->d3d.overlay; + composition = composition_meta->overlay; + num_overlays = gst_video_overlay_composition_n_rectangles (composition); + + GST_DEBUG_OBJECT (sink, "GstVideoOverlayCompositionMeta found."); + + /* add new overlays to list */ + for (i = 0; i < num_overlays; i++) { + GstVideoOverlayRectangle *rectangle = + gst_video_overlay_composition_get_rectangle (composition, i); + + if (!_is_rectangle_in_overlays (sink->d3d.overlay, rectangle)) { + GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink); + GstVideoOverlayFormatFlags flags; + gint x, y; + guint width, height; + HRESULT hr = 0; + GstMapInfo info; + GstBuffer *from = NULL; + GstD3DVideoSinkOverlay *overlay = g_new0 (GstD3DVideoSinkOverlay, 1); + overlay->rectangle = gst_video_overlay_rectangle_ref (rectangle); + if (!gst_video_overlay_rectangle_get_render_rectangle + (overlay->rectangle, &x, &y, &width, &height)) { + GST_ERROR_OBJECT (sink, + "Failed to get overlay rectangle of dimension (%d,%d)", width, + height); + g_free (overlay); + continue; + } + hr = IDirect3DDevice9_CreateTexture (klass->d3d.device.d3d_device, + width, height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + &overlay->texture, NULL); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, + "Failed to create D3D texture of dimensions (%d,%d)", width, + height); + g_free (overlay); + continue; + } + flags = gst_video_overlay_rectangle_get_flags (rectangle); + /* FIXME: investigate support for pre-multiplied vs. non-pre-multiplied alpha */ + from = gst_video_overlay_rectangle_get_pixels_unscaled_argb + (rectangle, flags); + if (gst_buffer_map (from, &info, GST_MAP_READ)) { + /* 1. lock texture */ + D3DLOCKED_RECT rect; + hr = IDirect3DTexture9_LockRect (overlay->texture, 0, &rect, NULL, + D3DUSAGE_WRITEONLY); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, "Failed to lock D3D texture"); + gst_buffer_unmap (from, &info); + gst_d3d9_overlay_free_overlay (sink, overlay); + continue; + } + /* 2. copy */ + memcpy (rect.pBits, info.data, info.size); + /* 3. unlock texture */ + hr = IDirect3DTexture9_UnlockRect (overlay->texture, 0); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, "Failed to unlock D3D texture"); + gst_buffer_unmap (from, &info); + gst_d3d9_overlay_free_overlay (sink, overlay); + continue; + } + gst_buffer_unmap (from, &info); + hr = gst_d3d9_overlay_init_vb (sink, overlay); + if (FAILED (hr)) { + gst_d3d9_overlay_free_overlay (sink, overlay); + continue; + } + } + sink->d3d.overlay = g_list_append (sink->d3d.overlay, overlay); + } + } + /* remove old overlays from list */ + while (l != NULL) { + GList *next = l->next; + GstD3DVideoSinkOverlay *overlay = (GstD3DVideoSinkOverlay *) l->data; + + if (!_is_overlay_in_composition (composition, overlay)) { + gst_d3d9_overlay_free_overlay (sink, overlay); + sink->d3d.overlay = g_list_delete_link (sink->d3d.overlay, l); + } + l = next; + } + + return GST_FLOW_OK; +} + +gboolean +gst_d3d9_overlay_resize (GstD3DVideoSink * sink) +{ + GList *l = sink->d3d.overlay; + + while (l != NULL) { + GList *next = l->next; + GstD3DVideoSinkOverlay *overlay = (GstD3DVideoSinkOverlay *) l->data; + HRESULT hr = gst_d3d9_overlay_init_vb (sink, overlay); + + if (FAILED (hr)) { + return FALSE; + } + l = next; + } + + return TRUE; +} + +void +gst_d3d9_overlay_free (GstD3DVideoSink * sink) +{ + GList *l = sink->d3d.overlay; + + while (l != NULL) { + GList *next = l->next; + GstD3DVideoSinkOverlay *overlay = (GstD3DVideoSinkOverlay *) l->data; + + gst_d3d9_overlay_free_overlay (sink, overlay); + sink->d3d.overlay = g_list_delete_link (sink->d3d.overlay, l); + l = next; + } + g_list_free (sink->d3d.overlay); + sink->d3d.overlay = NULL; +} + +static HRESULT +gst_d3d9_overlay_init_vb (GstD3DVideoSink * sink, + GstD3DVideoSinkOverlay * overlay) +{ + GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink); + gint x = 0, y = 0; + guint width = 0, height = 0; + float scaleX = 1.0f, scaleY = 1.0f; + RECT dest_rect; + guint dest_width, dest_height; + void *vb_vertices = NULL; + HRESULT hr = 0; + int vert_count, byte_count; + + if (GST_VIDEO_SINK_WIDTH (sink) < 1 || GST_VIDEO_SINK_HEIGHT (sink) < 1) { + return D3D_OK; + } + + if (!gst_video_overlay_rectangle_get_render_rectangle + (overlay->rectangle, &x, &y, &width, &height)) { + GST_ERROR_OBJECT (sink, "Failed to get overlay rectangle"); + return 0; + } + if (width < 1 || height < 1) { + return D3D_OK; + } + memset (&dest_rect, 0, sizeof (dest_rect)); + gst_d3d9_overlay_calc_dest_rect (sink, &dest_rect); + dest_width = dest_rect.right - dest_rect.left; + dest_height = dest_rect.bottom - dest_rect.top; + scaleX = (float) dest_width / width; + scaleY = (float) dest_height / height; + x = dest_rect.left + x * scaleX; + y = dest_rect.top + y * scaleY; + width *= scaleX; + height *= scaleY; + + /* a quad is composed of six vertices */ + vert_count = 6; + byte_count = vert_count * sizeof (textured_vertex); + overlay->g_list_count = vert_count / 3; + + /* destroy existing buffer */ + if (overlay->g_list_vb) { + hr = IDirect3DVertexBuffer9_Release (overlay->g_list_vb); + if (hr != D3D_OK) { + GST_ERROR_OBJECT (sink, "Failed to release D3D vertex buffer"); + } + } + hr = IDirect3DDevice9_CreateVertexBuffer (klass->d3d.device.d3d_device, byte_count, /* Length */ + D3DUSAGE_WRITEONLY, /* Usage */ + tri_fvf, /* FVF */ + D3DPOOL_MANAGED, /* Pool */ + &overlay->g_list_vb, /* ppVertexBuffer */ + NULL); /* Handle */ + if (FAILED (hr)) { + GST_ERROR_OBJECT (sink, "Error Creating vertex buffer"); + return hr; + } + + hr = IDirect3DVertexBuffer9_Lock (overlay->g_list_vb, 0, /* Offset */ + 0, /* SizeToLock */ + &vb_vertices, /* Vertices */ + 0); /* Flags */ + if (FAILED (hr)) { + GST_ERROR_OBJECT (sink, "Error Locking vertex buffer"); + return hr; + } + { + textured_vertex data[] = { + + {x, y + height, 1, 1, 0, 1} + , {x, y, 1, 1, 0, 0} + , {x + width, y, 1, 1, 1, 0} + , + {x, y + height, 1, 1, 0, 1} + , {x + width, y, 1, 1, 1, 0} + , {x + width, + y + height, 1, 1, 1, 1} + + }; + memcpy (vb_vertices, data, byte_count); + } + hr = IDirect3DVertexBuffer9_Unlock (overlay->g_list_vb); + if (FAILED (hr)) { + GST_ERROR_OBJECT (sink, "Error Unlocking vertex buffer"); + return hr; + } + + return D3D_OK; +} + +gboolean +gst_d3d9_overlay_set_render_state (GstD3DVideoSink * sink) +{ + HRESULT hr = 0; + GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink); + gboolean ret = FALSE; + + D3D9_CHECK (IDirect3DDevice9_SetRenderState (klass->d3d.device.d3d_device, + D3DRS_ALPHABLENDENABLE, TRUE)); + D3D9_CHECK (IDirect3DDevice9_SetRenderState (klass->d3d.device.d3d_device, + D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)); + D3D9_CHECK (IDirect3DDevice9_SetRenderState (klass->d3d.device.d3d_device, + D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA)); + + ret = TRUE; +end: + return ret; +} + +gboolean +gst_d3d9_overlay_render (GstD3DVideoSink * sink) +{ + HRESULT hr = 0; + GList *iter = NULL; + gboolean ret = FALSE; + GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink); + + if (!sink->d3d.overlay) + return TRUE; + + if (sink->d3d.overlay_needs_resize && !gst_d3d9_overlay_resize (sink)) + return FALSE; + sink->d3d.overlay_needs_resize = FALSE; + iter = sink->d3d.overlay; + while (iter != NULL) { + GList *next = iter->next; + GstD3DVideoSinkOverlay *overlay = (GstD3DVideoSinkOverlay *) iter->data; + + if (!overlay->g_list_vb) { + GST_ERROR_OBJECT (sink, "Overlay is missing vertex buffer"); + goto end; + } + if (!overlay->texture) { + GST_ERROR_OBJECT (sink, "Overlay is missing texture"); + goto end; + } + D3D9_CHECK (IDirect3DDevice9_SetTexture (klass->d3d.device.d3d_device, 0, + (IDirect3DBaseTexture9 *) overlay->texture)) + /* Bind our Vertex Buffer */ + D3D9_CHECK (IDirect3DDevice9_SetFVF (klass->d3d.device.d3d_device, + tri_fvf)) + D3D9_CHECK (IDirect3DDevice9_SetStreamSource (klass->d3d.device.d3d_device, 0, /* StreamNumber */ + overlay->g_list_vb, /* StreamData */ + 0, /* OffsetInBytes */ + sizeof (textured_vertex))) + /* Stride */ + //Render from our Vertex Buffer + D3D9_CHECK (IDirect3DDevice9_DrawPrimitive (klass->d3d.device.d3d_device, D3DPT_TRIANGLELIST, /* PrimitiveType */ + 0, /* StartVertex */ + overlay->g_list_count)) /* PrimitiveCount */ + iter = next; + } + ret = TRUE; +end: + return ret; +} diff --git a/sys/d3dvideosink/gstd3d9overlay.h b/sys/d3dvideosink/gstd3d9overlay.h new file mode 100644 index 0000000000..a85e1cb075 --- /dev/null +++ b/sys/d3dvideosink/gstd3d9overlay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) 2019 Aaron Boxer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _GST_D3D9_OVERLAY_H_ +#define _GST_D3D9_OVERLAY_H_ + +#include +#include + +#include + +#if defined(__MINGW32__) +# ifndef _OBJC_NO_COM_ +# if defined(__cplusplus) && !defined(CINTERFACE) +# if defined(__GNUC__) && __GNUC__ < 3 && !defined(NOCOMATTRIBUTE) +# define DECLARE_INTERFACE_IID_(i,b,d) _COM_interface __attribute__((com_interface)) i : public b +# else +# define DECLARE_INTERFACE_IID_(i,b,d) _COM_interface i : public b +# endif +# elif !defined(DECLARE_INTERFACE_IID_) +# define DECLARE_INTERFACE_IID_(i,b,d) DECLARE_INTERFACE(i) +# endif +# endif +# if !defined(__MSABI_LONG) +# define __MSABI_LONG(x) x ## l +# endif +#endif + +#include + +typedef struct _GstD3DVideoSinkOverlay +{ + GstVideoOverlayRectangle *rectangle; + LPDIRECT3DTEXTURE9 texture; + IDirect3DVertexBuffer9 *g_list_vb; + int g_list_count; +} GstD3DVideoSinkOverlay; + +GstFlowReturn gst_d3d9_overlay_prepare (GstD3DVideoSink *sink, GstBuffer * buf); +void gst_d3d9_overlay_free (GstD3DVideoSink * sink); +gboolean gst_d3d9_overlay_set_render_state (GstD3DVideoSink * sink); +gboolean gst_d3d9_overlay_render (GstD3DVideoSink * sink); +#endif /* _GST_D3D9_OVERLAY_H_ */ diff --git a/sys/d3dvideosink/meson.build b/sys/d3dvideosink/meson.build index eeaf07b1b8..5a7e9defa2 100644 --- a/sys/d3dvideosink/meson.build +++ b/sys/d3dvideosink/meson.build @@ -1,6 +1,7 @@ d3dvideosink_sources = [ 'd3dhelpers.c', 'd3dvideosink.c', + 'gstd3d9overlay.c' ] if host_system != 'windows' or get_option('d3dvideosink').disabled()