d3dvideosink: support OverlayComposition for GPU overlay compositing

This commit is contained in:
Aaron Boxer 2019-10-22 14:10:30 -04:00 committed by GStreamer Merge Bot
parent 6892078b00
commit 86b01bb291
6 changed files with 631 additions and 21 deletions

View file

@ -25,6 +25,7 @@
#include "d3dvideosink.h"
#include "d3dhelpers.h"
#include "gstd3d9overlay.h"
#include <stdio.h>
@ -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;

View file

@ -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))

View file

@ -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;
}

View file

@ -0,0 +1,471 @@
/* GStreamer
* Copyright (C) 2019 Aaron Boxer <aaron.boxer@collabora.com>
*
* 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 <stdio.h>
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;
}

View file

@ -0,0 +1,58 @@
/* GStreamer
* Copyright (C) 2019 Aaron Boxer <aaron.boxer@collabora.com>
*
* 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 <gst/gst.h>
#include <gst/video/video.h>
#include <windows.h>
#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 <d3d9.h>
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_ */

View file

@ -1,6 +1,7 @@
d3dvideosink_sources = [
'd3dhelpers.c',
'd3dvideosink.c',
'gstd3d9overlay.c'
]
if host_system != 'windows' or get_option('d3dvideosink').disabled()