mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-07 09:59:55 +00:00
d3dvideosink: support OverlayComposition for GPU overlay compositing
This commit is contained in:
parent
6892078b00
commit
86b01bb291
6 changed files with 631 additions and 21 deletions
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
471
sys/d3dvideosink/gstd3d9overlay.c
Normal file
471
sys/d3dvideosink/gstd3d9overlay.c
Normal 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;
|
||||
}
|
58
sys/d3dvideosink/gstd3d9overlay.h
Normal file
58
sys/d3dvideosink/gstd3d9overlay.h
Normal 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_ */
|
|
@ -1,6 +1,7 @@
|
|||
d3dvideosink_sources = [
|
||||
'd3dhelpers.c',
|
||||
'd3dvideosink.c',
|
||||
'gstd3d9overlay.c'
|
||||
]
|
||||
|
||||
if host_system != 'windows' or get_option('d3dvideosink').disabled()
|
||||
|
|
Loading…
Reference in a new issue