From 655e511a5c1704df907eaa62d6d9a6d32d2fd032 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sat, 4 Jun 2011 18:43:45 -0700 Subject: [PATCH] d3dvideosink: Add plugin Fixes #651782. Patch from various authors, by way of ossbuild. --- configure.ac | 39 + sys/Makefile.am | 10 +- sys/d3dvideosink/Makefile.am | 16 + sys/d3dvideosink/d3dvideosink.c | 2624 +++++++++++++++++ sys/d3dvideosink/d3dvideosink.h | 106 + sys/d3dvideosink/directx/d3d.c | 65 + sys/d3dvideosink/directx/d3d.h | 99 + sys/d3dvideosink/directx/directx.h | 33 + sys/d3dvideosink/directx/directx10/dx10.c | 27 + sys/d3dvideosink/directx/directx10/dx10.h | 39 + sys/d3dvideosink/directx/directx10/dx10_d3d.c | 77 + sys/d3dvideosink/directx/directx10/dx10_d3d.h | 72 + sys/d3dvideosink/directx/directx11/dx11.c | 27 + sys/d3dvideosink/directx/directx11/dx11.h | 39 + sys/d3dvideosink/directx/directx11/dx11_d3d.c | 75 + sys/d3dvideosink/directx/directx11/dx11_d3d.h | 72 + sys/d3dvideosink/directx/directx9/dx9.c | 27 + sys/d3dvideosink/directx/directx9/dx9.h | 38 + sys/d3dvideosink/directx/directx9/dx9_d3d.c | 73 + sys/d3dvideosink/directx/directx9/dx9_d3d.h | 71 + sys/d3dvideosink/directx/dx.c | 282 ++ sys/d3dvideosink/directx/dx.h | 265 ++ 22 files changed, 4174 insertions(+), 2 deletions(-) create mode 100644 sys/d3dvideosink/Makefile.am create mode 100644 sys/d3dvideosink/d3dvideosink.c create mode 100644 sys/d3dvideosink/d3dvideosink.h create mode 100644 sys/d3dvideosink/directx/d3d.c create mode 100644 sys/d3dvideosink/directx/d3d.h create mode 100644 sys/d3dvideosink/directx/directx.h create mode 100644 sys/d3dvideosink/directx/directx10/dx10.c create mode 100644 sys/d3dvideosink/directx/directx10/dx10.h create mode 100644 sys/d3dvideosink/directx/directx10/dx10_d3d.c create mode 100644 sys/d3dvideosink/directx/directx10/dx10_d3d.h create mode 100644 sys/d3dvideosink/directx/directx11/dx11.c create mode 100644 sys/d3dvideosink/directx/directx11/dx11.h create mode 100644 sys/d3dvideosink/directx/directx11/dx11_d3d.c create mode 100644 sys/d3dvideosink/directx/directx11/dx11_d3d.h create mode 100644 sys/d3dvideosink/directx/directx9/dx9.c create mode 100644 sys/d3dvideosink/directx/directx9/dx9.h create mode 100644 sys/d3dvideosink/directx/directx9/dx9_d3d.c create mode 100644 sys/d3dvideosink/directx/directx9/dx9_d3d.h create mode 100644 sys/d3dvideosink/directx/dx.c create mode 100644 sys/d3dvideosink/directx/dx.h diff --git a/configure.ac b/configure.ac index 617e07e6ba..772f453109 100644 --- a/configure.ac +++ b/configure.ac @@ -454,6 +454,44 @@ int main () AC_SUBST(HAVE_DIRECTSOUND) ]) +dnl Direct3D +translit(dnm, m, l) AM_CONDITIONAL(USE_DIRECT3D, true) +AG_GST_CHECK_FEATURE(DIRECT3D, [Direct3D plug-in], direct3dsink, [ + HAVE_DIRECT3D="no" + save_CFLAGS="$CFLAGS" + save_LDFLAGS="$LDFLAGS" + save_LIBS="$LIBS" + CFLAGS="$CFLAGS $DIRECTX_CFLAGS" + LDFLAGS="$LDFLAGS $DIRECTX_LDFLAGS" + LIBS="$LIBS -ld3d -lgdi32" + AC_MSG_CHECKING(for Direct3D LDFLAGS) + AC_LINK_IFELSE([ +#include +#include + +int main () +{ + GetStockObject(0); + Direct3DCreate(NULL, NULL, NULL); + + return 0; +} +], + [HAVE_DIRECT3D="yes"], + [HAVE_DIRECT3D="no"]) + AC_MSG_RESULT($HAVE_DIRECT3D) + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + + if test "x$HAVE_DIRECT3D" = "xyes"; then + dnl this is much more than we want + DIRECT3D_LIBS="-ld3d -ldxguid -lgdi32" + AC_SUBST(DIRECT3D_LIBS) + fi + AC_SUBST(HAVE_DIRECT3D) +]) + dnl DirectDraw translit(dnm, m, l) AM_CONDITIONAL(USE_DIRECTDRAW, true) AG_GST_CHECK_FEATURE(DIRECTDRAW, [DirectDraw plug-in], directdrawsink, [ @@ -1888,6 +1926,7 @@ sys/acmenc/Makefile sys/acmmp3dec/Makefile sys/applemedia/Makefile sys/avc/Makefile +sys/d3dvideosink/Makefile sys/decklink/Makefile sys/directdraw/Makefile sys/directsound/Makefile diff --git a/sys/Makefile.am b/sys/Makefile.am index 7fa59ae0cc..89e898d0ac 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -22,6 +22,12 @@ endif # CDROM_DIR= # endif +if USE_DIRECT3D +D3DVIDEOSINK_DIR=d3dvideosink +else +D3DVIDEOSINK_DIR= +endif + if USE_DECKLINK DECKLINK_DIR=decklink else @@ -107,9 +113,9 @@ else AVC_DIR= endif -SUBDIRS = $(ACM_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(DECKLINK_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(SHM_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) +SUBDIRS = $(ACM_DIR) $(APPLE_MEDIA_DIR) $(AVC_DIR) $(D3DVIDEOSINK) $(DECKLINK_DIR) $(DIRECTDRAW_DIR) $(DIRECTSOUND_DIR) $(DVB_DIR) $(FBDEV_DIR) $(LINSYS_DIR) $(OSX_VIDEO_DIR) $(QT_DIR) $(SHM_DIR) $(VCD_DIR) $(VDPAU_DIR) $(WININET_DIR) -DIST_SUBDIRS = acmenc acmmp3dec applemedia avc decklink directdraw directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ +DIST_SUBDIRS = acmenc acmmp3dec applemedia avc d3dvideosink decklink directdraw directsound dvb linsys fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \ osxvideo qtwrapper shm vcd vdpau wasapi wininet winks winscreencap include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/sys/d3dvideosink/Makefile.am b/sys/d3dvideosink/Makefile.am new file mode 100644 index 0000000000..6e0ecf316c --- /dev/null +++ b/sys/d3dvideosink/Makefile.am @@ -0,0 +1,16 @@ +plugin_LTLIBRARIES = libgstd3dvideosink.la + +libgstd3dvideosink_la_SOURCES = d3dvideosink.c directx/d3d.c directx/dx.c \ + directx/directx9/dx9.c directx/directx9/dx9_d3d.c \ + directx/directx10/dx10.c directx/directx10/dx10_d3d.c \ + directx/directx11/dx11.c directx/directx11/dx11_d3d.c +libgstd3dvideosink_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS) +libgstd3dvideosink_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-$(GST_MAJORMINOR) -lgstinterfaces-$(GST_MAJORMINOR) +libgstd3dvideosink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -lgdi32 +libgstd3dvideosink_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = d3dvideosink.h directx/d3d.h directx/dx.h directx/directx.h \ + directx/directx9/dx9.h directx/directx9/dx9_d3d.h \ + directx/directx10/dx10.h directx/directx10/dx10_d3d.h \ + directx/directx11/dx11.h directx/directx11/dx11_d3d.h diff --git a/sys/d3dvideosink/d3dvideosink.c b/sys/d3dvideosink/d3dvideosink.c new file mode 100644 index 0000000000..9ca6b960e2 --- /dev/null +++ b/sys/d3dvideosink/d3dvideosink.c @@ -0,0 +1,2624 @@ +/* GStreamer + * Copyright (C) 2010-2011 David Hoyt + * Copyright (C) 2010 Andoni Morales + * + * 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" + +#define IPC_SET_WINDOW 1 +#define IDT_DEVICELOST 1 + +/* Provide access to data that will be shared among all instantiations of this element */ +#define GST_D3DVIDEOSINK_SHARED_D3D_LOCK g_static_mutex_lock (&shared_d3d_lock); +#define GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK g_static_mutex_unlock (&shared_d3d_lock); +#define GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK g_static_mutex_lock (&shared_d3d_dev_lock); +#define GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK g_static_mutex_unlock (&shared_d3d_dev_lock); +#define GST_D3DVIDEOSINK_SHARED_D3D_HOOK_LOCK g_static_mutex_lock (&shared_d3d_hook_lock); +#define GST_D3DVIDEOSINK_SHARED_D3D_HOOK_UNLOCK g_static_mutex_unlock (&shared_d3d_hook_lock); +typedef struct _GstD3DVideoSinkShared GstD3DVideoSinkShared; +struct _GstD3DVideoSinkShared +{ + LPDIRECT3D9 d3d; + LPDIRECT3DDEVICE9 d3ddev; + D3DCAPS9 d3dcaps; + D3DFORMAT d3ddmformat; + D3DFORMAT d3dformat; + D3DFORMAT d3dfourcc; + D3DFORMAT d3dstencilformat; + D3DTEXTUREFILTERTYPE d3dfiltertype; + gboolean d3dEnableAutoDepthStencil; + + GList *element_list; + gint32 element_count; + + gboolean device_lost; + UINT_PTR device_lost_timer; + + HWND hidden_window_handle; + HANDLE hidden_window_created_signal; + GThread *hidden_window_thread; + + GHashTable *hook_tbl; +}; +typedef struct _GstD3DVideoSinkHookData GstD3DVideoSinkHookData; +struct _GstD3DVideoSinkHookData +{ + HHOOK hook; + HWND window_handle; + DWORD thread_id; + DWORD process_id; +}; +/* Holds our shared information */ +static GstD3DVideoSinkShared shared; +/* Define a shared lock to synchronize the creation/destruction of the d3d device */ +static GStaticMutex shared_d3d_lock = G_STATIC_MUTEX_INIT; +static GStaticMutex shared_d3d_dev_lock = G_STATIC_MUTEX_INIT; +static GStaticMutex shared_d3d_hook_lock = G_STATIC_MUTEX_INIT; +/* Hold a reference to our dll's HINSTANCE */ +static HINSTANCE g_hinstDll = NULL; + +typedef struct _IPCData IPCData; +struct _IPCData +{ + HWND hwnd; + LONG_PTR wnd_proc; +}; +/* Holds data that may be used to communicate across processes */ +/*static IPCData ipc_data;*/ +/*static COPYDATASTRUCT ipc_cds;*/ + +GST_DEBUG_CATEGORY (d3dvideosink_debug); +#define GST_CAT_DEFAULT d3dvideosink_debug + +/* TODO: Support RGB! */ +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ YUY2, UYVY, YV12, I420 }")) + //";" GST_VIDEO_CAPS_RGBx) + ); + +static void gst_d3dvideosink_init_interfaces (GType type); + +GST_BOILERPLATE_FULL (GstD3DVideoSink, gst_d3dvideosink, GstVideoSink, + GST_TYPE_VIDEO_SINK, gst_d3dvideosink_init_interfaces); + +enum +{ + PROP_0, PROP_KEEP_ASPECT_RATIO, PROP_PIXEL_ASPECT_RATIO, + PROP_ENABLE_NAVIGATION_EVENTS, PROP_LAST +}; + +/* GObject methods */ +static void gst_d3dvideosink_finalize (GObject * gobject); +static void gst_d3dvideosink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_d3dvideosink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +/* GstElement methods */ +static GstStateChangeReturn gst_d3dvideosink_change_state (GstElement * element, + GstStateChange transition); + +/* GstBaseSink methods */ +static gboolean gst_d3dvideosink_start (GstBaseSink * bsink); +static gboolean gst_d3dvideosink_stop (GstBaseSink * bsink); +static gboolean gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps); +static GstCaps *gst_d3dvideosink_get_caps (GstBaseSink * bsink); +static GstFlowReturn gst_d3dvideosink_show_frame (GstVideoSink * sink, + GstBuffer * buffer); + +/* GstXOverlay methods */ +static void gst_d3dvideosink_set_window_handle (GstXOverlay * overlay, + guintptr window_id); +static void gst_d3dvideosink_expose (GstXOverlay * overlay); + +/* GstNavigation methods */ +static void gst_d3dvideosink_navigation_send_event (GstNavigation * navigation, + GstStructure * structure); + +/* WndProc methods */ +LRESULT APIENTRY WndProc (HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam); +LRESULT APIENTRY SharedHiddenWndProc (HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam); +static void gst_d3dvideosink_wnd_proc (GstD3DVideoSink * sink, HWND hWnd, + UINT message, WPARAM wParam, LPARAM lParam); + +/* HookProc methods */ +LRESULT APIENTRY WndProcHook (HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam); +LRESULT CALLBACK gst_d3dvideosink_hook_proc (int nCode, WPARAM wParam, + LPARAM lParam); + +/* Paint/update methods */ +static void gst_d3dvideosink_update (GstBaseSink * bsink); +static gboolean gst_d3dvideosink_refresh (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_update_all (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_refresh_all (GstD3DVideoSink * sink); +static void gst_d3dvideosink_stretch (GstD3DVideoSink * sink, + LPDIRECT3DSURFACE9 backBuffer); + +/* Misc methods */ +BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad); +static void gst_d3dvideosink_remove_window_for_renderer (GstD3DVideoSink * + sink); +static gboolean gst_d3dvideosink_initialize_direct3d (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_initialize_d3d_device (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_initialize_swap_chain (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_resize_swap_chain (GstD3DVideoSink * sink, + gint width, gint height); +static gboolean gst_d3dvideosink_notify_device_lost (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_notify_device_reset (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_device_lost (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_release_swap_chain (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_release_d3d_device (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_release_direct3d (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_window_size (GstD3DVideoSink * sink, + gint * width, gint * height); +static gboolean gst_d3dvideosink_direct3d_supported (GstD3DVideoSink * sink); +static gboolean gst_d3dvideosink_shared_hidden_window_thread (GstD3DVideoSink * + sink); +static void gst_d3dvideosink_hook_window_for_renderer (GstD3DVideoSink * sink); +static void gst_d3dvideosink_unhook_window_for_renderer (GstD3DVideoSink * + sink); +static void gst_d3dvideosink_unhook_all_windows (void); +static void gst_d3dvideosink_log_debug (const gchar * file, + const gchar * function, gint line, const gchar * format, va_list args); +static void gst_d3dvideosink_log_warning (const gchar * file, + const gchar * function, gint line, const gchar * format, va_list args); +static void gst_d3dvideosink_log_error (const gchar * file, + const gchar * function, gint line, const gchar * format, va_list args); + +static DirectXInitParams directx_init_params = { + gst_d3dvideosink_log_debug, gst_d3dvideosink_log_warning, + gst_d3dvideosink_log_error +}; + +/* TODO: event, preroll, buffer_alloc? + * buffer_alloc won't generally be all that useful because the renderers require a + * different stride to GStreamer's implicit values. + */ + +BOOL WINAPI +DllMain (HINSTANCE hinstDll, DWORD fdwReason, PVOID fImpLoad) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + g_hinstDll = hinstDll; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + gst_d3dvideosink_unhook_all_windows (); + break; + } + return TRUE; +} + +static gboolean +gst_d3dvideosink_interface_supported (GstImplementsInterface * iface, + GType type) +{ + return (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_NAVIGATION); +} + +static void +gst_d3dvideosink_interface_init (GstImplementsInterfaceClass * klass) +{ + klass->supported = gst_d3dvideosink_interface_supported; +} + +static void +gst_d3dvideosink_xoverlay_interface_init (GstXOverlayClass * iface) +{ + iface->set_window_handle = gst_d3dvideosink_set_window_handle; + iface->expose = gst_d3dvideosink_expose; +} + +static void +gst_d3dvideosink_navigation_interface_init (GstNavigationInterface * iface) +{ + iface->send_event = gst_d3dvideosink_navigation_send_event; +} + +static void +gst_d3dvideosink_init_interfaces (GType type) +{ + static const GInterfaceInfo iface_info = { + (GInterfaceInitFunc) gst_d3dvideosink_interface_init, + NULL, + NULL + }; + + static const GInterfaceInfo xoverlay_info = { + (GInterfaceInitFunc) gst_d3dvideosink_xoverlay_interface_init, + NULL, + NULL + }; + + static const GInterfaceInfo navigation_info = { + (GInterfaceInitFunc) gst_d3dvideosink_navigation_interface_init, + NULL, + NULL, + }; + + g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, + &iface_info); + g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info); + g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info); + + GST_DEBUG_CATEGORY_INIT (d3dvideosink_debug, "d3dvideosink", 0, + "Direct3D video sink"); +} + +static void +gst_d3dvideosink_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_details_simple (element_class, "Direct3D video sink", + "Sink/Video", + "Display data using a Direct3D video renderer", + "David Hoyt "); +} + +static void +gst_d3dvideosink_class_init (GstD3DVideoSinkClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + GstVideoSinkClass *gstvideosink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + gstvideosink_class = (GstVideoSinkClass *) klass; + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_d3dvideosink_finalize); + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_d3dvideosink_set_property); + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_d3dvideosink_get_property); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_d3dvideosink_change_state); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_d3dvideosink_get_caps); + 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->unlock = GST_DEBUG_FUNCPTR (gst_d3dvideosink_unlock); */ + /*gstbasesink_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_d3dvideosink_unlock_stop); */ + + gstvideosink_class->show_frame = + GST_DEBUG_FUNCPTR (gst_d3dvideosink_show_frame); + + /* Add properties */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_KEEP_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio", + "Force aspect ratio", + "When enabled, scaling will respect original aspect ratio", FALSE, + (GParamFlags) G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_PIXEL_ASPECT_RATIO, g_param_spec_string ("pixel-aspect-ratio", + "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", "1/1", + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_ENABLE_NAVIGATION_EVENTS, + g_param_spec_boolean ("enable-navigation-events", + "Enable navigation events", + "When enabled, navigation events are sent upstream", TRUE, + (GParamFlags) G_PARAM_READWRITE)); + + /* Initialize DirectX abstraction */ + GST_DEBUG ("Initializing DirectX abstraction layer"); + directx_initialize (&directx_init_params); + + /* Initialize DirectX API */ + if (!directx_initialize_best_available_api ()) + GST_DEBUG ("Unable to initialize DirectX"); + + /* Determine DirectX version */ + klass->directx_api = directx_get_best_available_api (); + klass->directx_version = + (klass->directx_api != + NULL ? klass->directx_api->version : DIRECTX_VERSION_UNKNOWN); + klass->is_directx_supported = directx_is_supported (); +} + +static void +gst_d3dvideosink_clear (GstD3DVideoSink * sink) +{ + sink->enable_navigation_events = TRUE; + sink->keep_aspect_ratio = FALSE; + + sink->window_closed = FALSE; + sink->window_handle = NULL; + sink->is_new_window = FALSE; + sink->is_hooked = FALSE; +} + +static void +gst_d3dvideosink_init (GstD3DVideoSink * sink, GstD3DVideoSinkClass * klass) +{ + gst_d3dvideosink_clear (sink); + + sink->d3d_swap_chain_lock = g_mutex_new (); + + sink->par = g_new0 (GValue, 1); + g_value_init (sink->par, GST_TYPE_FRACTION); + gst_value_set_fraction (sink->par, 1, 1); + + /* TODO: Copied from GstVideoSink; should we use that as base class? */ + /* 20ms is more than enough, 80-130ms is noticable */ + gst_base_sink_set_max_lateness (GST_BASE_SINK (sink), 20 * GST_MSECOND); + gst_base_sink_set_qos_enabled (GST_BASE_SINK (sink), TRUE); +} + +static void +gst_d3dvideosink_finalize (GObject * gobject) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (gobject); + + if (sink->par) { + g_free (sink->par); + sink->par = NULL; + } + + g_mutex_free (sink->d3d_swap_chain_lock); + sink->d3d_swap_chain_lock = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (gobject); +} + +static void +gst_d3dvideosink_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (object); + + switch (prop_id) { + case PROP_ENABLE_NAVIGATION_EVENTS: + sink->enable_navigation_events = g_value_get_boolean (value); + break; + case PROP_KEEP_ASPECT_RATIO: + sink->keep_aspect_ratio = g_value_get_boolean (value); + break; + case PROP_PIXEL_ASPECT_RATIO: + g_free (sink->par); + sink->par = g_new0 (GValue, 1); + g_value_init (sink->par, GST_TYPE_FRACTION); + if (!g_value_transform (value, sink->par)) { + g_warning ("Could not transform string to aspect ratio"); + gst_value_set_fraction (sink->par, 1, 1); + } + GST_DEBUG_OBJECT (sink, "set PAR to %d/%d", + gst_value_get_fraction_numerator (sink->par), + gst_value_get_fraction_denominator (sink->par)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_d3dvideosink_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (object); + + switch (prop_id) { + case PROP_ENABLE_NAVIGATION_EVENTS: + g_value_set_boolean (value, sink->enable_navigation_events); + break; + case PROP_KEEP_ASPECT_RATIO: + g_value_set_boolean (value, sink->keep_aspect_ratio); + break; + case PROP_PIXEL_ASPECT_RATIO: + g_value_transform (sink->par, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstCaps * +gst_d3dvideosink_get_caps (GstBaseSink * basesink) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (basesink); + + return + gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink))); +} + +static void +gst_d3dvideosink_close_window (GstD3DVideoSink * sink) +{ + if (!sink || !sink->window_handle) + return; + + if (!sink->is_new_window) { + gst_d3dvideosink_remove_window_for_renderer (sink); + return; + } + + SendMessage (sink->window_handle, WM_CLOSE, (WPARAM) NULL, (WPARAM) NULL); + g_thread_join (sink->window_thread); + sink->is_new_window = FALSE; +} + +static gboolean +gst_d3dvideosink_create_shared_hidden_window (GstD3DVideoSink * sink) +{ + GST_DEBUG ("Creating Direct3D hidden window"); + + shared.hidden_window_created_signal = CreateSemaphore (NULL, 0, 1, NULL); + if (shared.hidden_window_created_signal == NULL) + goto failed; + + shared.hidden_window_thread = g_thread_create ((GThreadFunc) + gst_d3dvideosink_shared_hidden_window_thread, sink, TRUE, NULL); + + /* wait maximum 60 seconds for window to be created */ + if (WaitForSingleObject (shared.hidden_window_created_signal, + 60000) != WAIT_OBJECT_0) + goto failed; + + CloseHandle (shared.hidden_window_created_signal); + + GST_DEBUG ("Successfully created Direct3D hidden window, handle: %p", + shared.hidden_window_handle); + + return (shared.hidden_window_handle != NULL); + +failed: + CloseHandle (shared.hidden_window_created_signal); + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, + ("Error creating Direct3D hidden window"), (NULL)); + return FALSE; +} + +static gboolean +gst_d3dvideosink_shared_hidden_window_created (GstD3DVideoSink * sink) +{ + /* Should only be called from the shared window thread. */ + ReleaseSemaphore (shared.hidden_window_created_signal, 1, NULL); + return TRUE; +} + +static gboolean +gst_d3dvideosink_shared_hidden_window_thread (GstD3DVideoSink * sink) +{ + WNDCLASS WndClass; + HWND hWnd; + MSG msg; + + memset (&WndClass, 0, sizeof (WNDCLASS)); + WndClass.hInstance = GetModuleHandle (NULL); + WndClass.lpszClassName = TEXT ("GST-Shared-Hidden-D3DSink"); + WndClass.lpfnWndProc = SharedHiddenWndProc; + if (!RegisterClass (&WndClass)) { + GST_ERROR ("Unable to register Direct3D hidden window class"); + return FALSE; + } + + hWnd = CreateWindowEx (0, WndClass.lpszClassName, + TEXT ("GStreamer Direct3D hidden window"), + WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, WndClass.hInstance, sink); + + if (hWnd == NULL) { + GST_ERROR_OBJECT (sink, "Failed to create Direct3D hidden window"); + goto error; + } + + GST_DEBUG ("Direct3D hidden window handle: %p", hWnd); + + shared.hidden_window_handle = hWnd; + shared.device_lost_timer = 0; + + GST_DEBUG ("Initializing Direct3D"); + SendMessage (shared.hidden_window_handle, WM_DIRECTX_D3D_INIT_DEVICE, 0, 0); + GST_DEBUG ("Direct3D initialization complete"); + + gst_d3dvideosink_shared_hidden_window_created (sink); + + GST_DEBUG ("Entering Direct3D hidden window message loop"); + + /* start message loop processing */ + while (TRUE) { + while (GetMessage (&msg, NULL, 0, 0)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + if (msg.message == WM_QUIT || msg.message == WM_CLOSE) + break; + } + + GST_DEBUG ("Leaving Direct3D hidden window message loop"); + +/*success:*/ + /* Kill the device lost timer if it's running */ + if (shared.device_lost_timer != 0) + KillTimer (hWnd, shared.device_lost_timer); + UnregisterClass (WndClass.lpszClassName, WndClass.hInstance); + + shared.device_lost_timer = 0; + return TRUE; + +error: + /* Kill the device lost timer if it's running */ + if (shared.device_lost_timer != 0) + KillTimer (hWnd, shared.device_lost_timer); + if (hWnd) + DestroyWindow (hWnd); + UnregisterClass (WndClass.lpszClassName, WndClass.hInstance); + + shared.hidden_window_handle = NULL; + shared.device_lost_timer = 0; + + ReleaseSemaphore (shared.hidden_window_created_signal, 1, NULL); + return FALSE; +} + +LRESULT APIENTRY +SharedHiddenWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + GstD3DVideoSink *sink; + + if (message == WM_CREATE) { + /* lParam holds a pointer to a CREATESTRUCT instance which in turn holds the parameter used when creating the window. */ + sink = (GstD3DVideoSink *) ((LPCREATESTRUCT) lParam)->lpCreateParams; + + /* In our case, this is a pointer to the sink. So we immediately attach it for use in subsequent calls. */ + SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) sink); + } + + sink = (GstD3DVideoSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA); + + switch (message) { + case WM_DIRECTX_D3D_INIT_DEVICE: + { + gst_d3dvideosink_initialize_d3d_device (sink); + break; + } + case WM_DIRECTX_D3D_INIT_DEVICELOST: + { + if (!shared.device_lost) { + //GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK + //GST_D3DVIDEOSINK_SHARED_D3D_LOCK + + shared.device_lost = TRUE; + + /* Handle device lost by creating a timer and posting WM_D3D_DEVICELOST twice a second */ + /* Create a timer to periodically check the d3d device and attempt to recreate it */ + shared.device_lost_timer = SetTimer (hWnd, IDT_DEVICELOST, 500, NULL); + + /* Try it once immediately */ + SendMessage (hWnd, WM_DIRECTX_D3D_DEVICELOST, 0, 0); + } + break; + } + case WM_TIMER: + { + /* Did we receive a message to check if the device is available again? */ + if (wParam == IDT_DEVICELOST) { + /* This will synchronously call SharedHiddenWndProc() because this thread is the one that created the window. */ + SendMessage (hWnd, WM_DIRECTX_D3D_DEVICELOST, 0, 0); + return 0; + } + break; + } + case WM_DIRECTX_D3D_DEVICELOST: + { + gst_d3dvideosink_device_lost (sink); + break; + } + case WM_DIRECTX_D3D_END_DEVICELOST: + { + if (shared.device_lost) { + /* gst_d3dvideosink_notify_device_reset() sends this message. */ + if (shared.device_lost_timer != 0) + KillTimer (hWnd, shared.device_lost_timer); + + shared.device_lost_timer = 0; + shared.device_lost = FALSE; + + /* Refresh the video with the last buffer */ + gst_d3dvideosink_update_all (sink); + + /* Then redraw just in case we don't have a last buffer */ + gst_d3dvideosink_refresh_all (sink); + + //GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + //GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK + } + break; + } + case WM_DESTROY: + { + PostQuitMessage (0); + return 0; + } + } + + return DefWindowProc (hWnd, message, wParam, lParam); +} + +static void +gst_d3dvideosink_close_shared_hidden_window (GstD3DVideoSink * sink) +{ + if (!shared.hidden_window_handle) + return; + + SendMessage (shared.hidden_window_handle, WM_CLOSE, (WPARAM) NULL, + (WPARAM) NULL); + if (shared.hidden_window_thread) { + g_thread_join (shared.hidden_window_thread); + shared.hidden_window_thread = NULL; + } + shared.hidden_window_handle = NULL; + + GST_DEBUG ("Successfully closed Direct3D hidden window"); +} + +/* WNDPROC for application-supplied windows */ +LRESULT APIENTRY +WndProcHook (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + /* Handle certain actions specially on the window passed to us. + * Then forward back to the original window. + */ + GstD3DVideoSink *sink = + (GstD3DVideoSink *) GetProp (hWnd, TEXT ("GstD3DVideoSink")); + + switch (message) { + case WM_ERASEBKGND: + return TRUE; + case WM_COPYDATA: + { + gst_d3dvideosink_wnd_proc (sink, hWnd, message, wParam, lParam); + return TRUE; + } + case WM_PAINT: + { + LRESULT ret; + ret = CallWindowProc (sink->prevWndProc, hWnd, message, wParam, lParam); + /* Call this afterwards to ensure that our paint happens last */ + gst_d3dvideosink_wnd_proc (sink, hWnd, message, wParam, lParam); + return ret; + } + default: + { + /* Check it */ + gst_d3dvideosink_wnd_proc (sink, hWnd, message, wParam, lParam); + return CallWindowProc (sink->prevWndProc, hWnd, message, wParam, lParam); + } + } +} + +/* WndProc for our default window, if the application didn't supply one */ +LRESULT APIENTRY +WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + GstD3DVideoSink *sink; + + if (message == WM_CREATE) { + /* lParam holds a pointer to a CREATESTRUCT instance which in turn holds the parameter used when creating the window. */ + GstD3DVideoSink *sink = + (GstD3DVideoSink *) ((LPCREATESTRUCT) lParam)->lpCreateParams; + + /* In our case, this is a pointer to the sink. So we immediately attach it for use in subsequent calls. */ + SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) sink); + + /* signal application we created a window */ + gst_x_overlay_got_window_handle (GST_X_OVERLAY (sink), (guintptr) hWnd); + } + + + sink = (GstD3DVideoSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA); + gst_d3dvideosink_wnd_proc (sink, hWnd, message, wParam, lParam); + + switch (message) { + case WM_ERASEBKGND: + case WM_COPYDATA: + return TRUE; + + case WM_DESTROY: + { + PostQuitMessage (0); + return 0; + } + } + + return DefWindowProc (hWnd, message, wParam, lParam); +} + +static void +gst_d3dvideosink_wnd_proc (GstD3DVideoSink * sink, HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COPYDATA: + { + PCOPYDATASTRUCT p_ipc_cds; + p_ipc_cds = (PCOPYDATASTRUCT) lParam; + switch (p_ipc_cds->dwData) { + case IPC_SET_WINDOW: + { + IPCData *p_ipc_data; + p_ipc_data = (IPCData *) p_ipc_cds->dwData; + + GST_DEBUG ("Received IPC call to subclass the window handler"); + + sink->window_handle = p_ipc_data->hwnd; + sink->prevWndProc = + (WNDPROC) SetWindowLongPtr (sink->window_handle, GWLP_WNDPROC, + (LONG_PTR) p_ipc_data->wnd_proc); + break; + } + } + break; + } + case WM_PAINT: + { + gst_d3dvideosink_refresh (sink); + break; + } + case WM_SIZE: + case WM_DIRECTX_D3D_RESIZE: + { + gint width; + gint height; + gst_d3dvideosink_window_size (sink, &width, &height); + gst_d3dvideosink_resize_swap_chain (sink, width, height); + gst_d3dvideosink_refresh (sink); + //gst_d3dvideosink_resize_swap_chain(sink, MAX(1, ABS(LOWORD(lParam))), MAX(1, ABS(HIWORD(lParam)))); + break; + } + case WM_CLOSE: + case WM_DESTROY: + { + sink->window_closed = TRUE; + //GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL)); + break; + } + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP: + { + if (!sink->enable_navigation_events) + break; + } + { + gunichar2 wcrep[128]; + if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) { + gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL); + if (utfrep) { + if (message == WM_CHAR || message == WM_KEYDOWN) + gst_navigation_send_key_event (GST_NAVIGATION (sink), "key-press", + utfrep); + if (message == WM_CHAR || message == WM_KEYUP) + gst_navigation_send_key_event (GST_NAVIGATION (sink), + "key-release", utfrep); + g_free (utfrep); + } + } + break; + } + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MOUSEMOVE: + { + if (!sink->enable_navigation_events) + break; + } + { + gint x, y, button; + const gchar *action; + + switch (message) { + case WM_MOUSEMOVE: + button = 0; + action = "mouse-move"; + break; + case WM_LBUTTONDOWN: + button = 1; + action = "mouse-button-press"; + break; + case WM_LBUTTONUP: + button = 1; + action = "mouse-button-release"; + break; + case WM_RBUTTONDOWN: + button = 2; + action = "mouse-button-press"; + break; + case WM_RBUTTONUP: + button = 2; + action = "mouse-button-release"; + break; + case WM_MBUTTONDOWN: + button = 3; + action = "mouse-button-press"; + break; + case WM_MBUTTONUP: + button = 3; + action = "mouse-button-release"; + break; + default: + button = 4; + action = NULL; + break; + } + + x = LOWORD (lParam); + y = HIWORD (lParam); + + if (button == 0) { + GST_DEBUG_OBJECT (sink, "Mouse moved to %dx%d", x, y); + } else + GST_DEBUG_OBJECT (sink, "Mouse button %d pressed at %dx%d", button, x, + y); + + if (button < 4) + gst_navigation_send_mouse_event (GST_NAVIGATION (sink), action, + button, x, y); + + break; + } + } +} + +static gpointer +gst_d3dvideosink_window_thread (GstD3DVideoSink * sink) +{ + WNDCLASS WndClass; + int width, height; + int offx, offy; + DWORD exstyle, style; + HWND video_window; + RECT rect; + int screenwidth; + int screenheight; + MSG msg; + + memset (&WndClass, 0, sizeof (WNDCLASS)); + WndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + WndClass.hInstance = GetModuleHandle (NULL); + WndClass.lpszClassName = TEXT ("GST-D3DSink"); + WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH); + WndClass.hCursor = LoadCursor (NULL, IDC_ARROW); + WndClass.hIcon = LoadIcon (NULL, IDI_APPLICATION); + WndClass.cbClsExtra = 0; + WndClass.cbWndExtra = 0; + WndClass.lpfnWndProc = WndProc; + RegisterClass (&WndClass); + + /* By default, create a normal top-level window, the size of the video. */ + + /* GST_VIDEO_SINK_WIDTH() is the aspect-ratio-corrected size of the video. */ + /* GetSystemMetrics() returns the width of the dialog's border (doubled b/c of left and right borders). */ + width = GST_VIDEO_SINK_WIDTH (sink) + GetSystemMetrics (SM_CXSIZEFRAME) * 2; + height = GST_VIDEO_SINK_HEIGHT (sink) + GetSystemMetrics (SM_CYCAPTION) + + (GetSystemMetrics (SM_CYSIZEFRAME) * 2); + + SystemParametersInfo (SPI_GETWORKAREA, 0, &rect, 0); + screenwidth = rect.right - rect.left; + screenheight = rect.bottom - rect.top; + offx = rect.left; + offy = rect.top; + + /* Make it fit into the screen without changing the aspect ratio. */ + if (width > screenwidth) { + double ratio = (double) screenwidth / (double) width; + width = screenwidth; + height = (int) (height * ratio); + } + + if (height > screenheight) { + double ratio = (double) screenheight / (double) height; + height = screenheight; + width = (int) (width * ratio); + } + + style = WS_OVERLAPPEDWINDOW; /* Normal top-level window */ + exstyle = 0; + + video_window = CreateWindowEx (exstyle, TEXT ("GST-D3DSink"), + TEXT ("GStreamer Direct3D sink default window"), + style, offx, offy, width, height, NULL, NULL, WndClass.hInstance, sink); + + if (video_window == NULL) { + GST_ERROR_OBJECT (sink, "Failed to create window"); + return NULL; + } + + sink->is_new_window = TRUE; + sink->window_handle = video_window; + + /* Now show the window, as appropriate */ + ShowWindow (video_window, SW_SHOWNORMAL); + + /* Trigger the initial paint of the window */ + UpdateWindow (video_window); + + ReleaseSemaphore (sink->window_created_signal, 1, NULL); + + /* start message loop processing our default window messages */ + while (TRUE) { + //while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + while (GetMessage (&msg, NULL, 0, 0)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + + if (msg.message == WM_QUIT || msg.message == WM_CLOSE) + break; + } + + UnregisterClass (WndClass.lpszClassName, WndClass.hInstance); + sink->window_handle = NULL; + return NULL; + +/*destroy_window:*/ +/* if (video_window) { */ +/* DestroyWindow(video_window); */ +/* UnregisterClass(WndClass.lpszClassName, WndClass.hInstance); */ +/* } */ +/* sink->window_handle = NULL; */ +/* ReleaseSemaphore (sink->window_created_signal, 1, NULL); */ +/* return NULL; */ +} + +static gboolean +gst_d3dvideosink_create_default_window (GstD3DVideoSink * sink) +{ + if (shared.device_lost) + return FALSE; + + sink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL); + if (sink->window_created_signal == NULL) + goto failed; + + sink->window_thread = + g_thread_create ((GThreadFunc) gst_d3dvideosink_window_thread, sink, TRUE, + NULL); + + /* wait maximum 10 seconds for window to be created */ + if (WaitForSingleObject (sink->window_created_signal, 10000) != WAIT_OBJECT_0) + goto failed; + + CloseHandle (sink->window_created_signal); + return (sink->window_handle != NULL); + +failed: + CloseHandle (sink->window_created_signal); + GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, + ("Error creating our default window"), (NULL)); + return FALSE; +} + +static void +gst_d3dvideosink_set_window_handle (GstXOverlay * overlay, guintptr window_id) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (overlay); + HWND hWnd = (HWND) window_id; + + if (hWnd == sink->window_handle) { + GST_DEBUG ("Window already set"); + return; + } + + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK (sink); + { + /* If we're already playing/paused, then we need to lock the swap chain, and recreate it with the new window. */ + gboolean init_swap_chain = sink->d3d_swap_chain != NULL; + + gst_d3dvideosink_release_swap_chain (sink); + + /* Close our existing window if there is one */ + gst_d3dvideosink_close_window (sink); + + /* Save our window id */ + sink->window_handle = hWnd; + + if (init_swap_chain) + gst_d3dvideosink_initialize_swap_chain (sink); + } + +/*success:*/ + GST_DEBUG ("Direct3D window id successfully changed for sink %p to %p", sink, + hWnd); + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return; +/*error:*/ +/* GST_DEBUG("Error attempting to change the window id for sink %d to %d", sink, hWnd); */ +/* GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK(sink); */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return; */ +} + +/* Hook for out-of-process rendering */ +LRESULT CALLBACK +gst_d3dvideosink_hook_proc (int nCode, WPARAM wParam, LPARAM lParam) +{ + //LPCWPSTRUCT p = (LPCWPSTRUCT)lParam; + // + //if (p && p->hwnd) + // WndProcHook(p->hwnd, p->message, p->wParam, p->lParam); + return CallNextHookEx (NULL, nCode, wParam, lParam); +} + +static void +gst_d3dvideosink_set_window_for_renderer (GstD3DVideoSink * sink) +{ + WNDPROC currWndProc; + + /* Application has requested a specific window ID */ + sink->is_new_window = FALSE; + currWndProc = (WNDPROC) GetWindowLongPtr (sink->window_handle, GWLP_WNDPROC); + if (sink->prevWndProc != currWndProc && currWndProc != WndProcHook) + sink->prevWndProc = + (WNDPROC) SetWindowLongPtr (sink->window_handle, GWLP_WNDPROC, + (LONG_PTR) WndProcHook); + + /* Allows us to pick up the video sink inside the msg handler */ + SetProp (sink->window_handle, TEXT ("GstD3DVideoSink"), sink); + + if (!(sink->prevWndProc)) { + /* If we were unable to set the window procedure, it's possible we're attempting to render into the */ + /* window from a separate process. In that case, we need to use a windows hook to see the messages */ + /* going to the window we're drawing on. We must take special care that our hook is properly removed */ + /* when we're done. */ + GST_DEBUG ("Unable to set window procedure. Error: %s", + g_win32_error_message (GetLastError ())); + GST_D3DVIDEOSINK_SHARED_D3D_HOOK_LOCK + gst_d3dvideosink_hook_window_for_renderer (sink); + GST_D3DVIDEOSINK_SHARED_D3D_HOOK_UNLOCK} else { + GST_DEBUG ("Set wndproc to %p from %p", WndProcHook, sink->prevWndProc); + GST_DEBUG ("Set renderer window to %p", sink->window_handle); + } + + sink->is_new_window = FALSE; +} + +static HHOOK +gst_d3dvideosink_find_hook (DWORD pid, DWORD tid) +{ + HWND key; + GHashTableIter iter; + GstD3DVideoSinkHookData *value; + + if (!shared.hook_tbl) + return NULL; + + g_hash_table_iter_init (&iter, shared.hook_tbl); + while (g_hash_table_iter_next (&iter, (gpointer) & key, (gpointer) & value)) { + if (value && value->process_id == pid && value->thread_id == tid) + return value->hook; + } + return NULL; +} + +static GstD3DVideoSinkHookData * +gst_d3dvideosink_hook_data (HWND window_id) +{ + if (!shared.hook_tbl) + return NULL; + return (GstD3DVideoSinkHookData *) g_hash_table_lookup (shared.hook_tbl, + window_id); +} + +static GstD3DVideoSinkHookData * +gst_d3dvideosink_register_hook_data (HWND window_id) +{ + GstD3DVideoSinkHookData *data; + if (!shared.hook_tbl) + shared.hook_tbl = g_hash_table_new (NULL, NULL); + data = + (GstD3DVideoSinkHookData *) g_hash_table_lookup (shared.hook_tbl, + window_id); + if (!data) { + data = + (GstD3DVideoSinkHookData *) g_malloc (sizeof (GstD3DVideoSinkHookData)); + memset (data, 0, sizeof (GstD3DVideoSinkHookData)); + g_hash_table_insert (shared.hook_tbl, window_id, data); + } + return data; +} + +static gboolean +gst_d3dvideosink_unregister_hook_data (HWND window_id) +{ + GstD3DVideoSinkHookData *data; + if (!shared.hook_tbl) + return FALSE; + data = + (GstD3DVideoSinkHookData *) g_hash_table_lookup (shared.hook_tbl, + window_id); + if (!data) + return TRUE; + if (g_hash_table_remove (shared.hook_tbl, window_id)) + g_free (data); + return TRUE; +} + +static void +gst_d3dvideosink_hook_window_for_renderer (GstD3DVideoSink * sink) +{ + /* Ensure that our window hook isn't already installed. */ + if (!sink->is_new_window && !sink->is_hooked && sink->window_handle) { + DWORD pid; + DWORD tid; + + GST_DEBUG ("Attempting to apply a windows hook in process %lu.", + GetCurrentProcessId ()); + + /* Get thread id of the window in question. */ + tid = GetWindowThreadProcessId (sink->window_handle, &pid); + + if (tid) { + HHOOK hook; + GstD3DVideoSinkHookData *data; + + /* Only apply a hook if there's not one already there. It's possible this is the case if there are multiple */ + /* embedded windows that we're hooking inside of the same dialog/thread. */ + + hook = gst_d3dvideosink_find_hook (pid, tid); + data = gst_d3dvideosink_register_hook_data (sink->window_handle); + if (data && !hook) { + GST_DEBUG + ("No other hooks exist for pid %lu and tid %lu. Attempting to add one.", + pid, tid); + hook = + SetWindowsHookEx (WH_CALLWNDPROCRET, gst_d3dvideosink_hook_proc, + g_hinstDll, tid); + } + + sink->is_hooked = (hook ? TRUE : FALSE); + + if (sink->is_hooked) { + data->hook = hook; + data->process_id = pid; + data->thread_id = tid; + data->window_handle = sink->window_handle; + + PostThreadMessage (tid, WM_NULL, 0, 0); + + GST_DEBUG ("Window successfully hooked. GetLastError() returned: %s", + g_win32_error_message (GetLastError ())); + } else { + /* Ensure that we clean up any allocated memory. */ + if (data) + gst_d3dvideosink_unregister_hook_data (sink->window_handle); + GST_DEBUG + ("Unable to hook the window. The system provided error was: %s", + g_win32_error_message (GetLastError ())); + } + } + } +} + +static void +gst_d3dvideosink_unhook_window_for_renderer (GstD3DVideoSink * sink) +{ + if (!sink->is_new_window && sink->is_hooked && sink->window_handle) { + GstD3DVideoSinkHookData *data; + + GST_DEBUG ("Unhooking a window in process %lu.", GetCurrentProcessId ()); + + data = gst_d3dvideosink_hook_data (sink->window_handle); + if (data) { + DWORD pid; + DWORD tid; + HHOOK hook; + + /* Save off a temp ref to the data */ + hook = data->hook; + tid = data->thread_id; + pid = data->process_id; + + /* Free the memory */ + if (gst_d3dvideosink_unregister_hook_data (sink->window_handle)) { + /* Check if there's anyone else who still has the hook. If so, then we do nothing. */ + /* If not, then go ahead and unhook. */ + if (gst_d3dvideosink_find_hook (pid, tid)) { + UnhookWindowsHookEx (hook); + GST_DEBUG ("Unhooked the window for process %lu and thread %lu.", pid, + tid); + } + } + } + + sink->is_hooked = FALSE; + + GST_DEBUG ("Window successfully unhooked in process %lu.", + GetCurrentProcessId ()); + } +} + +static void +gst_d3dvideosink_unhook_all_windows (void) +{ + /* Unhook all windows that may be currently hooked. This is mainly a precaution in case */ + /* a wayward process doesn't properly set state back to NULL (which would remove the hook). */ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK + GST_D3DVIDEOSINK_SHARED_D3D_LOCK GST_D3DVIDEOSINK_SHARED_D3D_HOOK_LOCK { + GList *item; + GstD3DVideoSink *s; + + GST_DEBUG ("Attempting to unhook all windows for process %lu", + GetCurrentProcessId ()); + + for (item = g_list_first (shared.element_list); item; item = item->next) { + s = (GstD3DVideoSink *) item->data; + gst_d3dvideosink_unhook_window_for_renderer (s); + } + } +GST_D3DVIDEOSINK_SHARED_D3D_HOOK_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK} + +static void +gst_d3dvideosink_remove_window_for_renderer (GstD3DVideoSink * sink) +{ + //GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK + //GST_D3DVIDEOSINK_SHARED_D3D_LOCK + //GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK(sink); + { + GST_DEBUG ("Removing custom rendering window procedure"); + if (!sink->is_new_window && sink->window_handle) { + WNDPROC currWndProc; + + /* Retrieve current msg handler */ + currWndProc = + (WNDPROC) GetWindowLongPtr (sink->window_handle, GWLP_WNDPROC); + + /* Return control of application window */ + if (sink->prevWndProc != NULL && currWndProc == WndProcHook) { + SetWindowLongPtr (sink->window_handle, GWLP_WNDPROC, + (LONG_PTR) sink->prevWndProc); + + sink->prevWndProc = NULL; + sink->window_handle = NULL; + sink->is_new_window = FALSE; + } + } + + GST_D3DVIDEOSINK_SHARED_D3D_HOOK_LOCK + gst_d3dvideosink_unhook_window_for_renderer (sink); + GST_D3DVIDEOSINK_SHARED_D3D_HOOK_UNLOCK + /* Remove the property associating our sink with the window */ + RemoveProp (sink->window_handle, TEXT ("GstD3DVideoSink")); + } + //GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK(sink); + //GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + //GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK +} + +static void +gst_d3dvideosink_prepare_window (GstD3DVideoSink * sink) +{ + /* Give the app a last chance to supply a window id */ + if (!sink->window_handle) { + gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sink)); + } + + /* If the app supplied one, use it. Otherwise, go ahead + * and create (and use) our own window */ + if (sink->window_handle) { + gst_d3dvideosink_set_window_for_renderer (sink); + } else { + gst_d3dvideosink_create_default_window (sink); + } + + gst_d3dvideosink_initialize_swap_chain (sink); +} + +static GstStateChangeReturn +gst_d3dvideosink_change_state (GstElement * element, GstStateChange transition) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + gst_d3dvideosink_initialize_direct3d (sink); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_d3dvideosink_remove_window_for_renderer (sink); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + gst_d3dvideosink_release_direct3d (sink); + gst_d3dvideosink_clear (sink); + break; + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + } + + return ret; +} + +static gboolean +gst_d3dvideosink_start (GstBaseSink * bsink) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); + + /* Determine if Direct 3D is supported */ + return gst_d3dvideosink_direct3d_supported (sink); +} + +static gboolean +gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstD3DVideoSink *sink; + GstCaps *sink_caps; + gint video_width, video_height; + gint video_par_n, video_par_d; /* video's PAR */ + gint display_par_n, display_par_d; /* display's PAR */ + gint fps_n, fps_d; + guint num, den; + + sink = GST_D3DVIDEOSINK (bsink); + sink_caps = gst_static_pad_template_get_caps (&sink_template); + + GST_DEBUG_OBJECT (sink, + "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %" + GST_PTR_FORMAT, sink_caps, caps); + + if (!gst_caps_can_intersect (sink_caps, caps)) + goto incompatible_caps; + + if (!gst_video_format_parse_caps (caps, &sink->format, &video_width, + &video_height)) + goto invalid_format; + + if (!gst_video_parse_caps_framerate (caps, &fps_n, &fps_d) || + !video_width || !video_height) + goto incomplete_caps; + + /* get aspect ratio from caps if it's present, and + * convert video width and height to a display width and height + * using wd / hd = wv / hv * PARv / PARd */ + + /* get video's PAR */ + if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &video_par_n, + &video_par_d)) { + video_par_n = 1; + video_par_d = 1; + } + /* get display's PAR */ + if (sink->par) { + display_par_n = gst_value_get_fraction_numerator (sink->par); + display_par_d = gst_value_get_fraction_denominator (sink->par); + } else { + display_par_n = 1; + display_par_d = 1; + } + + if (!gst_video_calculate_display_ratio (&num, &den, video_width, + video_height, video_par_n, video_par_d, display_par_n, display_par_d)) + goto no_disp_ratio; + + GST_DEBUG_OBJECT (sink, + "video width/height: %dx%d, calculated display ratio: %d/%d", + video_width, video_height, num, den); + + /* now find a width x height that respects this display ratio. + * prefer those that have one of w/h the same as the incoming video + * using wd / hd = num / den */ + + /* start with same height, because of interlaced video */ + /* check hd / den is an integer scale factor, and scale wd with the PAR */ + if (video_height % den == 0) { + GST_DEBUG_OBJECT (sink, "keeping video height"); + GST_VIDEO_SINK_WIDTH (sink) = (guint) + gst_util_uint64_scale_int (video_height, num, den); + GST_VIDEO_SINK_HEIGHT (sink) = video_height; + } else if (video_width % num == 0) { + GST_DEBUG_OBJECT (sink, "keeping video width"); + GST_VIDEO_SINK_WIDTH (sink) = video_width; + GST_VIDEO_SINK_HEIGHT (sink) = (guint) + gst_util_uint64_scale_int (video_width, den, num); + } else { + GST_DEBUG_OBJECT (sink, "approximating while keeping video height"); + GST_VIDEO_SINK_WIDTH (sink) = (guint) + gst_util_uint64_scale_int (video_height, num, den); + GST_VIDEO_SINK_HEIGHT (sink) = video_height; + } + GST_DEBUG_OBJECT (sink, "scaling to %dx%d", + GST_VIDEO_SINK_WIDTH (sink), GST_VIDEO_SINK_HEIGHT (sink)); + + if (GST_VIDEO_SINK_WIDTH (sink) <= 0 || GST_VIDEO_SINK_HEIGHT (sink) <= 0) + goto no_display_size; + + sink->width = video_width; + sink->height = video_height; + + /* Create a window (or start using an application-supplied one, then connect the graph */ + gst_d3dvideosink_prepare_window (sink); + + return TRUE; + /* ERRORS */ +incompatible_caps: + { + GST_ERROR_OBJECT (sink, "caps incompatible"); + return FALSE; + } +incomplete_caps: + { + GST_DEBUG_OBJECT (sink, "Failed to retrieve either width, " + "height or framerate from intersected caps"); + return FALSE; + } +invalid_format: + { + gchar *caps_txt = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (sink, + "Could not locate image format from caps %s", caps_txt); + g_free (caps_txt); + return FALSE; + } +no_disp_ratio: + { + GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL), + ("Error calculating the output display ratio of the video.")); + return FALSE; + } +no_display_size: + { + GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL), + ("Error calculating the output display ratio of the video.")); + return FALSE; + } +} + +static gboolean +gst_d3dvideosink_stop (GstBaseSink * bsink) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); + gst_d3dvideosink_close_window (sink); + gst_d3dvideosink_release_swap_chain (sink); + return TRUE; +} + +static GstFlowReturn +gst_d3dvideosink_show_frame (GstVideoSink * vsink, GstBuffer * buffer) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (vsink); + + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK (sink); + { + HRESULT hr; + LPDIRECT3DSURFACE9 backBuffer; + + if (!shared.d3ddev) { + if (!shared.device_lost) { + GST_WARNING ("No Direct3D device has been created, stopping"); + goto error; + } else { + GST_WARNING + ("Direct3D device is lost. Maintaining flow until it has been reset."); + goto success; + } + } + + if (!sink->d3d_offscreen_surface) { + GST_WARNING ("No Direct3D offscreen surface has been created, stopping"); + goto error; + } + + if (!sink->d3d_swap_chain) { + GST_WARNING ("No Direct3D swap chain has been created, stopping"); + goto error; + } + + if (sink->window_closed) { + GST_WARNING ("Window has been closed, stopping"); + goto error; + } + + if (sink->window_handle && !sink->is_new_window) { + if (shared.d3ddev) { + gint win_width = 0, win_height = 0; + D3DPRESENT_PARAMETERS d3dpp; + + ZeroMemory (&d3dpp, sizeof (d3dpp)); + + if (gst_d3dvideosink_window_size (sink, &win_width, &win_height)) { + IDirect3DSwapChain9_GetPresentParameters (sink->d3d_swap_chain, + &d3dpp); + if ((d3dpp.BackBufferWidth > 0 && d3dpp.BackBufferHeight > 0 + && win_width != d3dpp.BackBufferWidth) + || win_height != d3dpp.BackBufferHeight) + gst_d3dvideosink_resize_swap_chain (sink, win_width, win_height); + } + } + } + + /* Set the render target to our swap chain */ + IDirect3DSwapChain9_GetBackBuffer (sink->d3d_swap_chain, 0, + D3DBACKBUFFER_TYPE_MONO, &backBuffer); + IDirect3DDevice9_SetRenderTarget (shared.d3ddev, 0, backBuffer); + IDirect3DSurface9_Release (backBuffer); + + /* Clear the target */ + IDirect3DDevice9_Clear (shared.d3ddev, 0, NULL, D3DCLEAR_TARGET, + D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0); + + if (SUCCEEDED (IDirect3DDevice9_BeginScene (shared.d3ddev))) { + if (GST_BUFFER_DATA (buffer)) { + D3DLOCKED_RECT lr; + guint8 *dest, *source; + int srcstride, dststride, i; + + IDirect3DSurface9_LockRect (sink->d3d_offscreen_surface, &lr, NULL, 0); + dest = (guint8 *) lr.pBits; + source = GST_BUFFER_DATA (buffer); + + if (dest) { + if (gst_video_format_is_yuv (sink->format)) { + guint32 fourcc = gst_video_format_to_fourcc (sink->format); + + switch (fourcc) { + case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + case GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V'): + case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): + dststride = lr.Pitch; + srcstride = GST_BUFFER_SIZE (buffer) / sink->height; + for (i = 0; i < sink->height; ++i) + memcpy (dest + dststride * i, source + srcstride * i, + srcstride); + break; + case GST_MAKE_FOURCC ('I', '4', '2', '0'): + case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): + { + int srcystride, srcvstride, srcustride; + int dstystride, dstvstride, dstustride; + int rows; + guint8 *srcv, *srcu, *dstv, *dstu; + + rows = sink->height; + + /* Source y, u and v strides */ + srcystride = GST_ROUND_UP_4 (sink->width); + srcustride = GST_ROUND_UP_8 (sink->width) / 2; + srcvstride = GST_ROUND_UP_8 (srcystride) / 2; + + /* Destination y, u and v strides */ + dstystride = lr.Pitch; + dstustride = dstystride / 2; + dstvstride = dstustride; + + srcu = source + srcystride * GST_ROUND_UP_2 (rows); + srcv = srcu + srcustride * GST_ROUND_UP_2 (rows) / 2; + + if (fourcc == GST_MAKE_FOURCC ('I', '4', '2', '0')) { + /* swap u and v planes */ + dstv = dest + dstystride * rows; + dstu = dstv + dstustride * rows / 2; + } else { + dstu = dest + dstystride * rows; + dstv = dstu + dstustride * rows / 2; + } + + for (i = 0; i < rows; ++i) { + /* Copy the y plane */ + memcpy (dest + dstystride * i, source + srcystride * i, + srcystride); + } + + for (i = 0; i < rows / 2; ++i) { + /* Copy the u plane */ + memcpy (dstu + dstustride * i, srcu + srcustride * i, + srcustride); + /* Copy the v plane */ + memcpy (dstv + dstvstride * i, srcv + srcvstride * i, + srcvstride); + } + break; + } + default: + g_assert_not_reached (); + } + } else if (gst_video_format_is_rgb (sink->format)) { + dststride = lr.Pitch; + srcstride = GST_BUFFER_SIZE (buffer) / sink->height; + for (i = 0; i < sink->height; ++i) + memcpy (dest + dststride * i, source + srcstride * i, srcstride); + } + } + + IDirect3DSurface9_UnlockRect (sink->d3d_offscreen_surface); + } + gst_d3dvideosink_stretch (sink, backBuffer); + IDirect3DDevice9_EndScene (shared.d3ddev); + } + /* Swap back and front buffers on video card and present to the user */ + if (FAILED (hr = + IDirect3DSwapChain9_Present (sink->d3d_swap_chain, NULL, NULL, NULL, + NULL, 0))) { + switch (hr) { + case D3DERR_DEVICELOST: + case D3DERR_DEVICENOTRESET: + gst_d3dvideosink_notify_device_lost (sink); + break; + default: + goto wrong_state; + } + } + } + +success: + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return GST_FLOW_OK; +wrong_state: + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return GST_FLOW_WRONG_STATE; +/*unexpected:*/ +/* GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK(sink); */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return GST_FLOW_UNEXPECTED; */ +error: + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return GST_FLOW_ERROR; +} + +/* Simply redraws the last item on our offscreen surface to the window */ +static gboolean +gst_d3dvideosink_refresh (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK (sink); + { + HRESULT hr; + LPDIRECT3DSURFACE9 backBuffer; + + if (!shared.d3ddev) { + if (!shared.device_lost) + GST_DEBUG ("No Direct3D device has been created"); + goto error; + } + + if (!sink->d3d_offscreen_surface) { + GST_DEBUG ("No Direct3D offscreen surface has been created"); + goto error; + } + + if (!sink->d3d_swap_chain) { + GST_DEBUG ("No Direct3D swap chain has been created"); + goto error; + } + + if (sink->window_closed) { + GST_DEBUG ("Window has been closed"); + goto error; + } + + /* Set the render target to our swap chain */ + IDirect3DSwapChain9_GetBackBuffer (sink->d3d_swap_chain, 0, + D3DBACKBUFFER_TYPE_MONO, &backBuffer); + IDirect3DDevice9_SetRenderTarget (shared.d3ddev, 0, backBuffer); + IDirect3DSurface9_Release (backBuffer); + + /* Clear the target */ + IDirect3DDevice9_Clear (shared.d3ddev, 0, NULL, D3DCLEAR_TARGET, + D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0); + + if (SUCCEEDED (IDirect3DDevice9_BeginScene (shared.d3ddev))) { + gst_d3dvideosink_stretch (sink, backBuffer); + IDirect3DDevice9_EndScene (shared.d3ddev); + } + + /* Swap back and front buffers on video card and present to the user */ + if (FAILED (hr = + IDirect3DSwapChain9_Present (sink->d3d_swap_chain, NULL, NULL, NULL, + NULL, 0))) { + switch (hr) { + case D3DERR_DEVICELOST: + case D3DERR_DEVICENOTRESET: + gst_d3dvideosink_notify_device_lost (sink); + break; + default: + goto error; + } + } + } + +/*success:*/ + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +error: + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return FALSE; +} + +static gboolean +gst_d3dvideosink_update_all (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + { + GList *item; + GstD3DVideoSink *s; + for (item = g_list_first (shared.element_list); item; item = item->next) { + s = (GstD3DVideoSink *) item->data; + gst_d3dvideosink_update (GST_BASE_SINK (s)); + } + } +/*success:*/ + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +/*error:*/ +/* GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return FALSE; */ +} + +static gboolean +gst_d3dvideosink_refresh_all (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + { + GList *item; + GstD3DVideoSink *s; + for (item = g_list_first (shared.element_list); item; item = item->next) { + s = (GstD3DVideoSink *) item->data; + gst_d3dvideosink_refresh (s); + } + } +/*success:*/ + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +/*error:*/ +/* GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return FALSE; */ +} + +static void +gst_d3dvideosink_stretch (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 backBuffer) +{ + if (sink->keep_aspect_ratio) { + gint window_width; + gint window_height; + RECT r; + GstVideoRectangle src; + GstVideoRectangle dst; + GstVideoRectangle result; + + gst_d3dvideosink_window_size (sink, &window_width, &window_height); + + src.w = GST_VIDEO_SINK_WIDTH (sink); + src.h = GST_VIDEO_SINK_HEIGHT (sink); + + dst.w = window_width; + dst.h = window_height; + + gst_video_sink_center_rect (src, dst, &result, TRUE); + + r.left = result.x; + r.top = result.y; + r.right = result.x + result.w; + r.bottom = result.y + result.h; + + IDirect3DDevice9_StretchRect (shared.d3ddev, sink->d3d_offscreen_surface, + NULL, backBuffer, &r, sink->d3dfiltertype); + } else { + IDirect3DDevice9_StretchRect (shared.d3ddev, sink->d3d_offscreen_surface, + NULL, backBuffer, NULL, sink->d3dfiltertype); + } +} + +static void +gst_d3dvideosink_expose (GstXOverlay * overlay) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (overlay); + GstBuffer *last_buffer; + + last_buffer = gst_base_sink_get_last_buffer (GST_BASE_SINK (sink)); + if (last_buffer) { + gst_d3dvideosink_show_frame (GST_VIDEO_SINK (sink), last_buffer); + gst_buffer_unref (last_buffer); + } +} + +static void +gst_d3dvideosink_update (GstBaseSink * bsink) +{ + GstBuffer *last_buffer; + + last_buffer = gst_base_sink_get_last_buffer (bsink); + if (last_buffer) { + gst_d3dvideosink_show_frame (GST_VIDEO_SINK (bsink), last_buffer); + gst_buffer_unref (last_buffer); + } +} + +/* TODO: How can we implement these? Figure that out... */ +/* +static gboolean +gst_d3dvideosink_unlock (GstBaseSink * bsink) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); + + return TRUE; +} + +static gboolean +gst_d3dvideosink_unlock_stop (GstBaseSink * bsink) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink); + + return TRUE; +} +*/ + +static gboolean +gst_d3dvideosink_initialize_direct3d (GstD3DVideoSink * sink) +{ + /* Let's hope this is never a problem (they have millions of d3d elements going at the same time) */ + if (shared.element_count >= G_MAXINT32) { + GST_ERROR + ("There are too many d3dvideosink elements. Creating more elements would put this element into an unknown state."); + return FALSE; + } + + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + /* Add to our GList containing all of our elements. */ + /* GLists are doubly-linked lists and calling prepend() prevents it from having to traverse the entire list just to add one item. */ + shared.element_list = g_list_prepend (shared.element_list, sink); + + /* Increment our count of the number of elements we have */ + shared.element_count++; + if (shared.element_count > 1) + goto success; + + /* We want to initialize direct3d only for the first element that's using it. */ + /* We'll destroy this once all elements using direct3d have been finalized. */ + /* See gst_d3dvideosink_release_direct3d() for details. */ + + /* We create a window that's hidden and used by the Direct3D device. The */ + /* device is shared among all d3dvideosink windows. */ + + GST_DEBUG ("Creating hidden window for Direct3D"); + if (!gst_d3dvideosink_create_shared_hidden_window (sink)) + goto error; + +success: + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +error: + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return FALSE; +} + +static gboolean +gst_d3dvideosink_initialize_d3d_device (GstD3DVideoSink * sink) +{ + HRESULT hr; + DWORD d3dcreate; + LPDIRECT3D9 d3d; + D3DCAPS9 d3dcaps; + D3DFORMAT d3ddmformat; + D3DDISPLAYMODE d3ddm; + LPDIRECT3DDEVICE9 d3ddev; + D3DPRESENT_PARAMETERS d3dpp; + D3DTEXTUREFILTERTYPE d3dfiltertype; + GstD3DVideoSinkClass *klass; + DirectXAPI *api; + + if (!sink) { + GST_WARNING ("Missing gobject instance."); + goto error; + } + + klass = GST_D3DVIDEOSINK_GET_CLASS (sink); + if (!klass) { + GST_WARNING ("Unable to retrieve gobject class"); + goto error; + } + + api = klass->directx_api; + if (!api) { + GST_WARNING ("Missing DirectX api"); + goto error; + } + //d3d = Direct3DCreate9(D3D_SDK_VERSION); + d3d = + (LPDIRECT3D9) DX9_D3D_COMPONENT_CALL_FUNC (DIRECTX_D3D (api), + Direct3DCreate9, D3D_SDK_VERSION); + if (!d3d) { + GST_WARNING ("Unable to create Direct3D interface"); + goto error; + } + + if (FAILED (IDirect3D9_GetAdapterDisplayMode (d3d, D3DADAPTER_DEFAULT, + &d3ddm))) { + /* Prevent memory leak */ + IDirect3D9_Release (d3d); + GST_WARNING ("Unable to request adapter display mode"); + goto error; + } + + if (FAILED (IDirect3D9_GetDeviceCaps (d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, + &d3dcaps))) { + /* Prevent memory leak */ + IDirect3D9_Release (d3d); + GST_WARNING ("Unable to request device caps"); + goto error; + } + + /* Ask DirectX to please not clobber the FPU state when making DirectX API calls. */ + /* This can cause libraries such as cairo to misbehave in certain scenarios. */ + d3dcreate = 0 | D3DCREATE_FPU_PRESERVE; + + /* Determine vertex processing capabilities. Some cards have issues using software vertex processing. */ + /* Courtesy http://www.chadvernon.com/blog/resources/directx9/improved-direct3d-initialization/ */ + if ((d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == + D3DDEVCAPS_HWTRANSFORMANDLIGHT) { + d3dcreate |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + /* if ((d3dcaps.DevCaps & D3DDEVCAPS_PUREDEVICE) == D3DDEVCAPS_PUREDEVICE) */ + /* d3dcreate |= D3DCREATE_PUREDEVICE; */ + } else { + d3dcreate |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + } + + /* Check the filter type. */ + if ((d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) == + D3DPTFILTERCAPS_MINFLINEAR + && (d3dcaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) == + D3DPTFILTERCAPS_MAGFLINEAR) { + d3dfiltertype = D3DTEXF_LINEAR; + } else { + d3dfiltertype = D3DTEXF_NONE; + } + + /* Setup the display mode format. */ + d3ddmformat = d3ddm.Format; + + ZeroMemory (&d3dpp, sizeof (d3dpp)); + //d3dpp.Flags = D3DPRESENTFLAG_VIDEO; + d3dpp.Windowed = TRUE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.BackBufferCount = 1; + d3dpp.BackBufferFormat = d3ddmformat; + d3dpp.BackBufferWidth = 1; + d3dpp.BackBufferHeight = 1; + d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; //D3DPRESENT_INTERVAL_IMMEDIATE; + + GST_DEBUG ("Creating Direct3D device for hidden window %p", + shared.hidden_window_handle); + + if (FAILED (hr = IDirect3D9_CreateDevice (d3d, + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + shared.hidden_window_handle, d3dcreate, &d3dpp, &d3ddev))) { + /* Prevent memory leak */ + IDirect3D9_Release (d3d); + GST_WARNING ("Unable to create Direct3D device. Result: %ld (0x%lx)", hr, + hr); + goto error; + } + //if (FAILED(IDirect3DDevice9_GetDeviceCaps( + // d3ddev, + // &d3dcaps + //))) { + // /* Prevent memory leak */ + // IDirect3D9_Release(d3d); + // GST_WARNING ("Unable to retrieve Direct3D device caps"); + // goto error; + //} + + shared.d3d = d3d; + shared.d3ddev = d3ddev; + shared.d3ddmformat = d3ddmformat; + shared.d3dfiltertype = d3dfiltertype; + +/*success:*/ + return TRUE; +error: + return FALSE; +} + +static gboolean +gst_d3dvideosink_initialize_swap_chain (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK (sink); + { + gint width; + gint height; + //D3DDISPLAYMODE mode; + D3DPRESENT_PARAMETERS d3dpp; + D3DFORMAT d3dformat; + D3DFORMAT d3dfourcc; + //D3DFORMAT d3dstencilformat; + LPDIRECT3DSWAPCHAIN9 d3dswapchain; + LPDIRECT3DSURFACE9 d3dsurface; + D3DTEXTUREFILTERTYPE d3dfiltertype; + //gboolean d3dEnableAutoDepthStencil; + + /* This should always work since gst_d3dvideosink_initialize_direct3d() should have always been called previously */ + if (!shared.d3ddev) { + GST_ERROR ("Direct3D device has not been initialized"); + goto error; + } + + GST_DEBUG ("Initializing Direct3D swap chain for sink %p", sink); + + if (gst_video_format_is_yuv (sink->format)) { + switch (gst_video_format_to_fourcc (sink->format)) { + case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'): + d3dformat = D3DFMT_X8R8G8B8; + d3dfourcc = (D3DFORMAT) MAKEFOURCC ('Y', 'U', 'Y', '2'); + break; + //case GST_MAKE_FOURCC ('Y', 'U', 'V', 'Y'): + // d3dformat = D3DFMT_X8R8G8B8; + // d3dfourcc = (D3DFORMAT)MAKEFOURCC('Y', 'U', 'V', 'Y'); + // break; + case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): + d3dformat = D3DFMT_X8R8G8B8; + d3dfourcc = (D3DFORMAT) MAKEFOURCC ('U', 'Y', 'V', 'Y'); + break; + case GST_MAKE_FOURCC ('Y', 'V', '1', '2'): + case GST_MAKE_FOURCC ('I', '4', '2', '0'): + d3dformat = D3DFMT_X8R8G8B8; + d3dfourcc = (D3DFORMAT) MAKEFOURCC ('Y', 'V', '1', '2'); + break; + default: + g_assert_not_reached (); + goto error; + } + } else if (gst_video_format_is_rgb (sink->format)) { + d3dformat = D3DFMT_X8R8G8B8; + d3dfourcc = D3DFMT_X8R8G8B8; + } else { + g_assert_not_reached (); + goto error; + } + + GST_DEBUG ("Determined Direct3D format: %d", d3dfourcc); + + //Stencil/depth buffers aren't created by default when using swap chains + //if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(shared.d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dformat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D32))) { + // d3dstencilformat = D3DFMT_D32; + // d3dEnableAutoDepthStencil = TRUE; + //} else if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(shared.d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dformat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D24X8))) { + // d3dstencilformat = D3DFMT_D24X8; + // d3dEnableAutoDepthStencil = TRUE; + //} else if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(shared.d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dformat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, D3DFMT_D16))) { + // d3dstencilformat = D3DFMT_D16; + // d3dEnableAutoDepthStencil = TRUE; + //} else { + // d3dstencilformat = D3DFMT_X8R8G8B8; + // d3dEnableAutoDepthStencil = FALSE; + //} + // + //GST_DEBUG("Determined Direct3D stencil format: %d", d3dstencilformat); + + GST_DEBUG ("Direct3D back buffer size: %dx%d", GST_VIDEO_SINK_WIDTH (sink), + GST_VIDEO_SINK_HEIGHT (sink)); + + /* Get the current size of the window */ + gst_d3dvideosink_window_size (sink, &width, &height); + + ZeroMemory (&d3dpp, sizeof (d3dpp)); + d3dpp.Windowed = TRUE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.hDeviceWindow = sink->window_handle; + d3dpp.BackBufferFormat = d3dformat; + d3dpp.BackBufferWidth = width; + d3dpp.BackBufferHeight = height; + + if (FAILED (IDirect3DDevice9_CreateAdditionalSwapChain (shared.d3ddev, + &d3dpp, &d3dswapchain))) + goto error; + + if (FAILED (IDirect3DDevice9_CreateOffscreenPlainSurface (shared.d3ddev, + sink->width, sink->height, d3dfourcc, D3DPOOL_DEFAULT, + &d3dsurface, NULL))) { + /* Ensure that we release our newly created swap chain to prevent memory leaks */ + IDirect3DSwapChain9_Release (d3dswapchain); + goto error; + } + + /* Determine texture filtering support. If it's supported for this format, use the filter type determined when we created the dev and checked the dev caps. */ + if (SUCCEEDED (IDirect3D9_CheckDeviceFormat (shared.d3d, D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, shared.d3ddmformat, D3DUSAGE_QUERY_FILTER, + D3DRTYPE_TEXTURE, d3dformat))) { + d3dfiltertype = shared.d3dfiltertype; + } else { + d3dfiltertype = D3DTEXF_NONE; + } + + GST_DEBUG ("Direct3D stretch rect texture filter: %d", d3dfiltertype); + + sink->d3dformat = d3dformat; + sink->d3dfourcc = d3dfourcc; + sink->d3dfiltertype = d3dfiltertype; + sink->d3d_swap_chain = d3dswapchain; + sink->d3d_offscreen_surface = d3dsurface; + } + +/*success:*/ + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +error: + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return FALSE; +} + +static gboolean +gst_d3dvideosink_resize_swap_chain (GstD3DVideoSink * sink, gint width, + gint height) +{ + if (width <= 0 || height <= 0 || width > GetSystemMetrics (SM_CXFULLSCREEN) + || height > GetSystemMetrics (SM_CYFULLSCREEN)) { + GST_DEBUG ("Invalid size"); + return FALSE; + } + + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK (sink); + { + int ref_count; + D3DPRESENT_PARAMETERS d3dpp; + LPDIRECT3DSWAPCHAIN9 d3dswapchain; + + GST_DEBUG ("Resizing Direct3D swap chain for sink %p to %dx%d", sink, width, + height); + + if (!shared.d3d || !shared.d3ddev) { + if (!shared.device_lost) + GST_WARNING ("Direct3D device has not been initialized"); + goto error; + } + + if (!sink->d3d_swap_chain) { + GST_DEBUG ("Direct3D swap chain has not been initialized"); + goto error; + } + + /* Get the parameters used to create this swap chain */ + if (FAILED (IDirect3DSwapChain9_GetPresentParameters (sink->d3d_swap_chain, + &d3dpp))) { + GST_DEBUG + ("Unable to determine Direct3D present parameters for swap chain"); + goto error; + } + + /* Release twice because IDirect3DSwapChain9_GetPresentParameters() adds a reference */ + while ((ref_count = + IDirect3DSwapChain9_Release (sink->d3d_swap_chain)) > 0); + sink->d3d_swap_chain = NULL; + GST_DEBUG ("Old Direct3D swap chain released. Reference count: %d", + ref_count); + + /* Adjust back buffer width/height */ + d3dpp.BackBufferWidth = width; + d3dpp.BackBufferHeight = height; + + if (FAILED (IDirect3DDevice9_CreateAdditionalSwapChain (shared.d3ddev, + &d3dpp, &d3dswapchain))) + goto error; + + sink->d3d_swap_chain = d3dswapchain; + } + +/*success:*/ + GST_DEBUG ("Direct3D swap chain successfully resized for sink %p", sink); + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +error: + GST_DEBUG ("Error attempting to resize the Direct3D swap chain for sink %p", + sink); + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return FALSE; +} + +static gboolean +gst_d3dvideosink_release_swap_chain (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK (sink); + { + GST_DEBUG ("Releasing Direct3D swap chain for sink %p", sink); + + /* This should always work since gst_d3dvideosink_initialize_direct3d() should have always been called previously */ + if (!shared.d3d || !shared.d3ddev) { + if (!shared.device_lost) + GST_ERROR ("Direct3D device has not been initialized"); + goto error; + } + + if (!sink->d3d_swap_chain && !sink->d3d_offscreen_surface) + goto success; + + if (sink->d3d_offscreen_surface) { + int ref_count; + while ((ref_count = + IDirect3DSurface9_Release (sink->d3d_offscreen_surface)) > 0); + sink->d3d_offscreen_surface = NULL; + GST_DEBUG + ("Direct3D offscreen surface released for sink %p. Reference count: %d", + sink, ref_count); + } + + if (sink->d3d_swap_chain) { + int ref_count; + while ((ref_count = + IDirect3DSwapChain9_Release (sink->d3d_swap_chain)) > 0); + sink->d3d_swap_chain = NULL; + GST_DEBUG + ("Direct3D swap chain released for sink %p. Reference count: %d", + sink, ref_count); + } + } + +success: + GST_DEBUG ("Direct3D swap chain successfully released for sink %p", sink); + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +error: + GST_DEBUG ("Error attempting to release the Direct3D swap chain for sink %p", + sink); + GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK (sink); + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return FALSE; +} + +static gboolean +gst_d3dvideosink_notify_device_lost (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + { + /* Send notification asynchronously */ + PostMessage (shared.hidden_window_handle, WM_DIRECTX_D3D_INIT_DEVICELOST, 0, + 0); + } +/*success:*/ + GST_DEBUG ("Successfully sent notification of device lost event for sink %p", + sink); + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK + return TRUE; +/*error:*/ +/* GST_DEBUG("Error attempting to send notification of device lost event for sink %d", sink); */ +/* GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return FALSE; */ +} + +static gboolean +gst_d3dvideosink_notify_device_reset (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + { + /* Send notification synchronously -- let's ensure the timer's been killed before returning */ + SendMessage (shared.hidden_window_handle, WM_DIRECTX_D3D_END_DEVICELOST, 0, + 0); + } +/*success:*/ + GST_DEBUG ("Successfully sent notification of device reset event for sink %p", + sink); + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK + return TRUE; +/*error:*/ +/* GST_DEBUG("Error attempting to send notification of reset lost event for sink %d", sink); */ +/* GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return FALSE; */ +} + +static gboolean +gst_d3dvideosink_device_lost (GstD3DVideoSink * sink) +{ + /* Must be called from hidden window's message loop! */ + + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + { + GST_DEBUG ("Direct3D device lost. Resetting the device."); + + if (g_thread_self () != shared.hidden_window_thread) + { + GST_ERROR + ("Direct3D device can only be reset by the thread that created it."); + goto error; + } + + if (!shared.device_lost && (!shared.d3d || !shared.d3ddev)) + { + GST_ERROR ("Direct3D device has not been initialized"); + goto error; + } + + { + GList *item; + GstD3DVideoSink *s; + + /* This is technically a bit different from the normal. We don't call reset(), instead */ + /* we recreate everything from scratch. */ + + /* Release all swap chains, surfaces, buffers, etc. */ + for (item = g_list_first (shared.element_list); item; item = item->next) { + s = (GstD3DVideoSink *) item->data; + gst_d3dvideosink_release_swap_chain (s); + } + + /* Release the device */ + if (!gst_d3dvideosink_release_d3d_device (NULL)) + goto error; + + /* Recreate device */ + if (!gst_d3dvideosink_initialize_d3d_device (sink)) + goto error; + + /* Reinitialize all swap chains, surfaces, buffers, etc. */ + for (item = g_list_first (shared.element_list); item; item = item->next) { + s = (GstD3DVideoSink *) item->data; + gst_d3dvideosink_initialize_swap_chain (s); + } + } + + /* Let the hidden window know that it's okay to kill the timer */ + gst_d3dvideosink_notify_device_reset (sink); + } + +/*success:*/ + GST_DEBUG ("Direct3D device has successfully been reset."); + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +error: + GST_DEBUG ("Unable to successfully reset the Direct3D device."); + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return FALSE; +} + +static gboolean +gst_d3dvideosink_release_d3d_device (GstD3DVideoSink * sink) +{ + GST_DEBUG ("Cleaning all Direct3D objects"); + + if (shared.d3ddev) { + int ref_count; + ref_count = IDirect3DDevice9_Release (shared.d3ddev); + shared.d3ddev = NULL; + GST_DEBUG ("Direct3D device released. Reference count: %d", ref_count); + } + + if (shared.d3d) { + int ref_count; + ref_count = IDirect3D9_Release (shared.d3d); + shared.d3d = NULL; + GST_DEBUG ("Direct3D object released. Reference count: %d", ref_count); + } + + return TRUE; +} + +static gboolean +gst_d3dvideosink_release_direct3d (GstD3DVideoSink * sink) +{ + GST_D3DVIDEOSINK_SHARED_D3D_DEV_LOCK GST_D3DVIDEOSINK_SHARED_D3D_LOCK + /* Be absolutely sure that we've released this sink's hook (if any). */ + gst_d3dvideosink_unhook_window_for_renderer (sink); + + /* Remove item from the list */ + shared.element_list = g_list_remove (shared.element_list, sink); + + /* Decrement our count of the number of elements we have */ + shared.element_count--; + if (shared.element_count < 0) + shared.element_count = 0; + if (shared.element_count > 0) + goto success; + + gst_d3dvideosink_release_d3d_device (sink); + + GST_DEBUG ("Closing hidden Direct3D window"); + gst_d3dvideosink_close_shared_hidden_window (sink); + +success: + GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK + GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK return TRUE; +/*error:*/ +/* GST_D3DVIDEOSINK_SHARED_D3D_UNLOCK */ +/* GST_D3DVIDEOSINK_SHARED_D3D_DEV_UNLOCK */ +/* return FALSE; */ +} + +static gboolean +gst_d3dvideosink_window_size (GstD3DVideoSink * sink, gint * width, + gint * height) +{ + if (!sink || !sink->window_handle) { + if (width && height) { + *width = 0; + *height = 0; + } + return FALSE; + } + + { + RECT sz; + GetClientRect (sink->window_handle, &sz); + + *width = MAX (1, ABS (sz.right - sz.left)); + *height = MAX (1, ABS (sz.bottom - sz.top)); + } + return TRUE; +} + +static void +gst_d3dvideosink_navigation_send_event (GstNavigation * navigation, + GstStructure * structure) +{ + GstD3DVideoSink *sink = GST_D3DVIDEOSINK (navigation); + gint window_width; + gint window_height; + GstEvent *e; + GstVideoRectangle src, dst, result; + double x, y, old_x, old_y; + GstPad *pad = NULL; + + gst_d3dvideosink_window_size (sink, &window_width, &window_height); + + src.w = GST_VIDEO_SINK_WIDTH (sink); + src.h = GST_VIDEO_SINK_HEIGHT (sink); + dst.w = window_width; + dst.h = window_height; + + e = gst_event_new_navigation (structure); + + if (sink->keep_aspect_ratio) { + gst_video_sink_center_rect (src, dst, &result, TRUE); + } else { + result.x = 0; + result.y = 0; + result.w = dst.w; + result.h = dst.h; + } + + /* Our coordinates can be wrong here if we centered the video */ + + /* Converting pointer coordinates to the non scaled geometry */ + if (gst_structure_get_double (structure, "pointer_x", &old_x)) { + x = old_x; + + if (x <= result.x) { + x = 0; + } else if (x >= result.x + result.w) { + x = src.w; + } else { + x = MAX (0, MIN (src.w, MAX (0, x - result.x) / result.w * src.w)); + } + GST_DEBUG_OBJECT (sink, + "translated navigation event x coordinate from %f to %f", old_x, x); + gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL); + } + if (gst_structure_get_double (structure, "pointer_y", &old_y)) { + y = old_y; + + if (y <= result.y) { + y = 0; + } else if (y >= result.y + result.h) { + y = src.h; + } else { + y = MAX (0, MIN (src.h, MAX (0, y - result.y) / result.h * src.h)); + } + GST_DEBUG_OBJECT (sink, + "translated navigation event y coordinate from %f to %f", old_y, y); + gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL); + } + + pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink)); + + if (GST_IS_PAD (pad) && GST_IS_EVENT (e)) { + gst_pad_send_event (pad, e); + gst_object_unref (pad); + } +} + +static gboolean +gst_d3dvideosink_direct3d_supported (GstD3DVideoSink * sink) +{ + GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink); + + return (klass != NULL && klass->is_directx_supported); +} + +static void +gst_d3dvideosink_log_debug (const gchar * file, const gchar * function, + gint line, const gchar * format, va_list args) +{ + if (G_UNLIKELY (GST_LEVEL_DEBUG <= __gst_debug_min)) + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_DEBUG, file, function, + line, NULL, format, args); +} + +static void +gst_d3dvideosink_log_warning (const gchar * file, const gchar * function, + gint line, const gchar * format, va_list args) +{ + if (G_UNLIKELY (GST_LEVEL_WARNING <= __gst_debug_min)) + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING, file, function, + line, NULL, format, args); +} + +static void +gst_d3dvideosink_log_error (const gchar * file, const gchar * function, + gint line, const gchar * format, va_list args) +{ + if (G_UNLIKELY (GST_LEVEL_ERROR <= __gst_debug_min)) + gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_ERROR, file, function, + line, NULL, format, args); +} + +/* Plugin entry point */ +static gboolean +plugin_init (GstPlugin * plugin) +{ + /* PRIMARY: this is the best videosink to use on windows */ + if (!gst_element_register (plugin, "d3dvideosink", + GST_RANK_PRIMARY, GST_TYPE_D3DVIDEOSINK)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "d3dsinkwrapper", + "Direct3D sink wrapper plugin", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/sys/d3dvideosink/d3dvideosink.h b/sys/d3dvideosink/d3dvideosink.h new file mode 100644 index 0000000000..e067dd3f16 --- /dev/null +++ b/sys/d3dvideosink/d3dvideosink.h @@ -0,0 +1,106 @@ +/* GStreamer + * Copyright (C) 2010 David Hoyt + * + * 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 __D3DVIDEOSINK_H__ +#define __D3DVIDEOSINK_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "directx/directx.h" + +#ifdef _MSC_VER +#pragma warning( disable : 4090 4024) +#endif + +G_BEGIN_DECLS +#define GST_TYPE_D3DVIDEOSINK (gst_d3dvideosink_get_type()) +#define GST_D3DVIDEOSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_D3DVIDEOSINK,GstD3DVideoSink)) +#define GST_D3DVIDEOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_D3DVIDEOSINK,GstD3DVideoSinkClass)) +#define GST_D3DVIDEOSINK_GET_CLASS(obj) (GST_D3DVIDEOSINK_CLASS(G_OBJECT_GET_CLASS(obj))) +#define GST_IS_D3DVIDEOSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_D3DVIDEOSINK)) +#define GST_IS_D3DVIDEOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_D3DVIDEOSINK)) + +typedef struct _GstD3DVideoSink GstD3DVideoSink; +typedef struct _GstD3DVideoSinkClass GstD3DVideoSinkClass; + +#define GST_D3DVIDEOSINK_SWAP_CHAIN_LOCK(sink) g_mutex_lock (GST_D3DVIDEOSINK (sink)->d3d_swap_chain_lock); +#define GST_D3DVIDEOSINK_SWAP_CHAIN_UNLOCK(sink) g_mutex_unlock (GST_D3DVIDEOSINK (sink)->d3d_swap_chain_lock); + +struct _GstD3DVideoSink +{ + GstVideoSink sink; + + /* source rectangle */ + gint width; + gint height; + + GstVideoFormat format; + + gboolean enable_navigation_events; + + gboolean keep_aspect_ratio; + GValue *par; + + /* If the window is closed, we set this and error out */ + gboolean window_closed; + + /* The video window set through GstXOverlay */ + HWND window_handle; + + /* If we created the window, it needs to be closed in ::stop() */ + gboolean is_new_window; + + /* If we create our own window, we run it from another thread */ + GThread *window_thread; + HANDLE window_created_signal; + + /* If we use an app-supplied window, we need to hook its WNDPROC */ + WNDPROC prevWndProc; + gboolean is_hooked; + + GMutex *d3d_swap_chain_lock; + LPDIRECT3DSWAPCHAIN9 d3d_swap_chain; + LPDIRECT3DSURFACE9 d3d_offscreen_surface; + + D3DFORMAT d3dformat; + D3DFORMAT d3dfourcc; + D3DTEXTUREFILTERTYPE d3dfiltertype; +}; + +struct _GstD3DVideoSinkClass +{ + GstVideoSinkClass parent_class; + + gboolean is_directx_supported; + gint directx_version; + DirectXAPI *directx_api; +}; + +GType gst_d3dvideosink_get_type (void); + +G_END_DECLS +#endif /* __D3DVIDEOSINK_H__ */ diff --git a/sys/d3dvideosink/directx/d3d.c b/sys/d3dvideosink/directx/d3d.c new file mode 100644 index 0000000000..33589a3804 --- /dev/null +++ b/sys/d3dvideosink/directx/d3d.c @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#include "directx.h" + +const DirectXD3D * +directx_d3d_create (const DirectXAPI * api) +{ + if (!api) + return NULL; + + return DIRECTX_D3D_CALL_API_FUNCTION (api, create, api); +} + +gboolean +directx_d3d_resize (const DirectXD3D * d3d) +{ + if (!d3d) + return FALSE; + + return DIRECTX_D3D_CALL_FUNCTION (d3d, resize, d3d); +} + +gboolean +directx_d3d_device_lost (const DirectXD3D * d3d) +{ + if (!d3d) + return FALSE; + + return DIRECTX_D3D_CALL_FUNCTION (d3d, device_lost, d3d); +} + +gboolean +directx_d3d_notify_device_reset (const DirectXD3D * d3d) +{ + if (!d3d) + return FALSE; + + return DIRECTX_D3D_CALL_FUNCTION (d3d, notify_device_reset, d3d); +} + +gboolean +directx_d3d_release (const DirectXD3D * d3d) +{ + if (!d3d) + return FALSE; + + return DIRECTX_D3D_CALL_FUNCTION (d3d, release, d3d); +} diff --git a/sys/d3dvideosink/directx/d3d.h b/sys/d3dvideosink/directx/d3d.h new file mode 100644 index 0000000000..c58310fd9f --- /dev/null +++ b/sys/d3dvideosink/directx/d3d.h @@ -0,0 +1,99 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_D3D_H__ +#define __DIRECTX_D3D_H__ + +#include + +#include "dx.h" + +G_BEGIN_DECLS + +#define WM_DIRECTX_D3D_INIT_DEVICE WM_DIRECTX + 1 +#define WM_DIRECTX_D3D_INIT_DEVICELOST WM_DIRECTX + 2 +#define WM_DIRECTX_D3D_DEVICELOST WM_DIRECTX + 3 +#define WM_DIRECTX_D3D_END_DEVICELOST WM_DIRECTX + 4 +#define WM_DIRECTX_D3D_RESIZE WM_DIRECTX + 5 + +#define DIRECTX_D3D_API(version, dispatch_table, init_function, create_function, resize_function, device_lost_function, notify_device_reset_function, release_function) \ + static gpointer DIRECTX_API_COMPONENT_D3D_ ## version ## _DISPATCH_TABLE = &dispatch_table; \ + static DirectXAPIComponentD3D DIRECTX_API_COMPONENT_D3D_ ## version ## _INIT = { \ + create_function /*create_function*/ \ + , resize_function /*resize_function*/ \ + , device_lost_function /*device_lost_function*/ \ + , notify_device_reset_function /*notify_device_reset_function*/ \ + , release_function /*release_function*/ \ + , NULL /*private_data*/ \ + }; \ + static void init_directx_api_component_d3d_ ## version ## _(const DirectXAPI* api) { \ + gpointer private_data = &DIRECTX_API_COMPONENT_D3D_ ## version ## _INIT; \ + gpointer vtable = DIRECTX_API_COMPONENT_D3D_ ## version ## _DISPATCH_TABLE; \ + DIRECTX_SET_COMPONENT_INIT(DIRECTX_D3D(api), init_function); \ + DIRECTX_SET_COMPONENT_DATA(DIRECTX_D3D(api), private_data); \ + DIRECTX_SET_COMPONENT_DISPATCH_TABLE(DIRECTX_D3D(api), vtable); \ + } + +#define INITIALIZE_DIRECTX_D3D_API(version, api) \ + init_directx_api_component_d3d_ ## version ## _(api); + +#define DIRECTX_D3D_FUNCTIONS(d3d) ((DirectXAPIComponentD3D*)d3d->d3d_component) +#define DIRECTX_D3D_API_FUNCTIONS(api) ((DirectXAPIComponentD3D*)DIRECTX_D3D_COMPONENT_DATA(api)) +#define DIRECTX_D3D_CALL_FUNCTION(d3d, func_name, ...) (DIRECTX_D3D_FUNCTIONS(d3d)->func_name(__VA_ARGS__)) +#define DIRECTX_D3D_CALL_API_FUNCTION(api, func_name, ...) (DIRECTX_D3D_API_FUNCTIONS(api)->func_name(__VA_ARGS__)) + +typedef struct _DirectXD3D DirectXD3D; +typedef struct _DirectXAPIComponentD3D DirectXAPIComponentD3D; + +/* Function pointers */ +typedef DirectXD3D* (*DirectXD3DCreateFunction) (const DirectXAPI* api); +typedef gboolean (*DirectXD3DResizeFunction) (const DirectXD3D* d3d); +typedef gboolean (*DirectXD3DDeviceLostFunction) (const DirectXD3D* d3d); +typedef gboolean (*DirectXD3DNotifyDeviceResetFunction) (const DirectXD3D* d3d); +typedef gboolean (*DirectXD3DReleaseFunction) (const DirectXD3D* d3d); + +struct _DirectXAPIComponentD3D +{ + DirectXD3DCreateFunction create; + DirectXD3DResizeFunction resize; + DirectXD3DDeviceLostFunction device_lost; + DirectXD3DNotifyDeviceResetFunction notify_device_reset; + DirectXD3DReleaseFunction release; + + gpointer private_data; +}; + +struct _DirectXD3D +{ + DirectXAPI* api; + DirectXAPIComponent* api_component; + DirectXAPIComponentD3D* d3d_component; + + gpointer private_data; +}; + +const DirectXD3D* directx_d3d_create(const DirectXAPI* api); +gboolean directx_d3d_resize(const DirectXD3D* d3d); +gboolean directx_d3d_device_lost(const DirectXD3D* d3d); +gboolean directx_d3d_notify_device_reset(const DirectXD3D* d3d); +gboolean directx_d3d_release(const DirectXD3D* d3d); + +G_END_DECLS + +#endif /* __DIRECTX_D3D_H__ */ diff --git a/sys/d3dvideosink/directx/directx.h b/sys/d3dvideosink/directx/directx.h new file mode 100644 index 0000000000..40c33dda60 --- /dev/null +++ b/sys/d3dvideosink/directx/directx.h @@ -0,0 +1,33 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX_H__ +#define __DIRECTX_DIRECTX_H__ + +#include "dx.h" +#include "d3d.h" + +/* TODO: Remove these headers -- they should not be publically distributed. */ +/* They're included for now only for expediancy in getting d3dvideosink */ +/* out the door. */ +#include "directx9/dx9.h" +#include "directx10/dx10.h" +#include "directx11/dx11.h" + +#endif /* __DIRECTX_DIRECTX_H__ */ diff --git a/sys/d3dvideosink/directx/directx10/dx10.c b/sys/d3dvideosink/directx/directx10/dx10.c new file mode 100644 index 0000000000..f902db4d5b --- /dev/null +++ b/sys/d3dvideosink/directx/directx10/dx10.c @@ -0,0 +1,27 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#include "dx10.h" + +void +dx10_init (const DirectXAPI * api) +{ + DIRECTX_DEBUG ("Initializing DirectX10 API"); + INITIALIZE_DIRECTX_D3D_API (DIRECTX_10, api); +} diff --git a/sys/d3dvideosink/directx/directx10/dx10.h b/sys/d3dvideosink/directx/directx10/dx10.h new file mode 100644 index 0000000000..a0965eb67f --- /dev/null +++ b/sys/d3dvideosink/directx/directx10/dx10.h @@ -0,0 +1,39 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX10_DX10_H__ +#define __DIRECTX_DIRECTX10_DX10_H__ + +#include "../dx.h" + +#include "dx10_d3d.h" + +/* Function declarations */ +void dx10_init(const DirectXAPI* api); + +DIRECTX_API( + DIRECTX_10, + dx10_init, + "d3d10", + "D3D10CreateDevice", + "DirectX10Description", + "DirectX 10.0" +) + +#endif /* __DIRECTX_DIRECTX10_DX10_H__ */ diff --git a/sys/d3dvideosink/directx/directx10/dx10_d3d.c b/sys/d3dvideosink/directx/directx10/dx10_d3d.c new file mode 100644 index 0000000000..04a0c33892 --- /dev/null +++ b/sys/d3dvideosink/directx/directx10/dx10_d3d.c @@ -0,0 +1,77 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#define CINTERFACE +#define D3D10_IGNORE_SDK_LAYERS + +#include +#include + +#include "dx10_d3d.h" + +void +dx10_d3d_init (DirectXAPIComponent * component, gpointer data) +{ + DIRECTX_DEBUG ("Initializing Direct3D"); + DIRECTX_OPEN_COMPONENT_MODULE (component, "d3d10"); + DIRECTX_DEBUG ("Completed Initializing Direct3D"); + + DIRECTX_DEBUG ("Setting Direct3D dispatch table"); + DIRECTX_OPEN_COMPONENT_SYMBOL (component, D3D10DispatchTable, + D3D10CreateDevice); + + //{ + // ID3D10Device* pDevice = NULL; + // DIRECTX_DEBUG("Calling D3D10CreateDevice"); + // DX10_D3D_COMPONENT_CALL_FUNC(component, D3D10CreateDevice, NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &pDevice); + // DIRECTX_DEBUG("Releasing D3D10 device"); + // ID3D10Device_Release(pDevice); + // DIRECTX_DEBUG("Released D3D10 device"); + //} +} + +DirectXD3D * +dx10_d3d_create (const DirectXAPI * api) +{ + return NULL; +} + +gboolean +dx10_d3d_resize (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx10_d3d_device_lost (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx10_d3d_notify_device_reset (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx10_d3d_release (const DirectXD3D * d3d) +{ + return TRUE; +} diff --git a/sys/d3dvideosink/directx/directx10/dx10_d3d.h b/sys/d3dvideosink/directx/directx10/dx10_d3d.h new file mode 100644 index 0000000000..a91c4f684b --- /dev/null +++ b/sys/d3dvideosink/directx/directx10/dx10_d3d.h @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX10_DX10_D3D_H__ +#define __DIRECTX_DIRECTX10_DX10_D3D_H__ + +#include + +#include "../d3d.h" + +#define DX10_D3D_API_CALL_FUNC(api, func_name, ...) (DIRECTX_CALL_COMPONENT_SYMBOL(DIRECTX_D3D(api), D3D10DispatchTable, func_name, __VA_ARGS__)) +#define DX10_D3D_COMPONENT_CALL_FUNC(component, func_name, ...) (DIRECTX_CALL_COMPONENT_SYMBOL(component, D3D10DispatchTable, func_name, __VA_ARGS__)) + +/* Structs */ +typedef struct _D3D10 D3D10; +typedef struct _D3D10DispatchTable D3D10DispatchTable; + +/* Functions */ +/* Courtesy http://code.google.com/p/theaimworldeditor/source/browse/trunk/DXUT/Core/DXUTmisc.cpp */ +typedef HRESULT (WINAPI *LPD3D10CREATEDEVICE)(gpointer /* IDXGIAdapter* */, UINT /* D3D10_DRIVER_TYPE */, HMODULE, UINT, UINT32, gpointer* /* ID3D10Device** */ ); + +struct _D3D10DispatchTable +{ + LPD3D10CREATEDEVICE D3D10CreateDevice; +}; + +/* Global data */ +struct _D3D10 +{ + D3D10DispatchTable vtable; +}; + +/* Global vars */ +static D3D10 dx10_d3d; + +/* Function declarations */ + +void dx10_d3d_init(DirectXAPIComponent* component, gpointer data); +DirectXD3D* dx10_d3d_create(const DirectXAPI* api); +gboolean dx10_d3d_resize(const DirectXD3D* d3d); +gboolean dx10_d3d_device_lost(const DirectXD3D* d3d); +gboolean dx10_d3d_notify_device_reset(const DirectXD3D* d3d); +gboolean dx10_d3d_release(const DirectXD3D* d3d); + +DIRECTX_D3D_API( + DIRECTX_10, + dx10_d3d.vtable, + dx10_d3d_init, + dx10_d3d_create, + dx10_d3d_resize, + dx10_d3d_device_lost, + dx10_d3d_notify_device_reset, + dx10_d3d_release +) + +#endif /* __DIRECTX_DIRECTX10_DX10_D3D_H__ */ diff --git a/sys/d3dvideosink/directx/directx11/dx11.c b/sys/d3dvideosink/directx/directx11/dx11.c new file mode 100644 index 0000000000..2cfe187460 --- /dev/null +++ b/sys/d3dvideosink/directx/directx11/dx11.c @@ -0,0 +1,27 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#include "dx11.h" + +void +dx11_init (const DirectXAPI * api) +{ + DIRECTX_DEBUG ("Initializing DirectX11 API"); + INITIALIZE_DIRECTX_D3D_API (DIRECTX_11, api); +} diff --git a/sys/d3dvideosink/directx/directx11/dx11.h b/sys/d3dvideosink/directx/directx11/dx11.h new file mode 100644 index 0000000000..3df04a1a27 --- /dev/null +++ b/sys/d3dvideosink/directx/directx11/dx11.h @@ -0,0 +1,39 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX11_DX11_H__ +#define __DIRECTX_DIRECTX11_DX11_H__ + +#include "../dx.h" + +#include "dx11_d3d.h" + +/* Function declarations */ +void dx11_init(const DirectXAPI* api); + +DIRECTX_API( + DIRECTX_11, + dx11_init, + "d3d11", + "D3D11CreateDevice", + "DirectX11Description", + "DirectX 11.0" +) + +#endif /* __DIRECTX_DIRECTX10_DX10_H__ */ diff --git a/sys/d3dvideosink/directx/directx11/dx11_d3d.c b/sys/d3dvideosink/directx/directx11/dx11_d3d.c new file mode 100644 index 0000000000..2b62b9cc7a --- /dev/null +++ b/sys/d3dvideosink/directx/directx11/dx11_d3d.c @@ -0,0 +1,75 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#define CINTERFACE + +#include +//#include + +#include "dx11_d3d.h" + +void +dx11_d3d_init (DirectXAPIComponent * component, gpointer data) +{ + DIRECTX_DEBUG ("Initializing Direct3D"); + DIRECTX_OPEN_COMPONENT_MODULE (component, "d3d11"); + DIRECTX_DEBUG ("Completed Initializing Direct3D"); + + DIRECTX_DEBUG ("Setting Direct3D dispatch table"); + //DIRECTX_OPEN_COMPONENT_SYMBOL(component, D3D11DispatchTable, D3D11CreateDevice); + + //{ + // ID3D11Device* pDevice = NULL; + // DIRECTX_DEBUG("Calling D3D11CreateDevice"); + // DX11_D3D_COMPONENT_CALL_FUNC(component, D3D11CreateDevice, NULL, D3D11_DRIVER_TYPE_HARDWARE, NULL, 0, D3D11_SDK_VERSION, &pDevice); + // DIRECTX_DEBUG("Releasing D3D11 device"); + // ID3D11Device_Release(pDevice); + // DIRECTX_DEBUG("Released D3D11 device"); + //} +} + +DirectXD3D * +dx11_d3d_create (const DirectXAPI * api) +{ + return NULL; +} + +gboolean +dx11_d3d_resize (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx11_d3d_device_lost (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx11_d3d_notify_device_reset (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx11_d3d_release (const DirectXD3D * d3d) +{ + return TRUE; +} diff --git a/sys/d3dvideosink/directx/directx11/dx11_d3d.h b/sys/d3dvideosink/directx/directx11/dx11_d3d.h new file mode 100644 index 0000000000..6eb158ba9c --- /dev/null +++ b/sys/d3dvideosink/directx/directx11/dx11_d3d.h @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX11_DX11_D3D_H__ +#define __DIRECTX_DIRECTX11_DX11_D3D_H__ + +#include + +#include "../d3d.h" + +#define DX11_D3D_API_CALL_FUNC(api, func_name, ...) (DIRECTX_CALL_COMPONENT_SYMBOL(DIRECTX_D3D(api), D3D11DispatchTable, func_name, __VA_ARGS__)) +#define DX11_D3D_COMPONENT_CALL_FUNC(component, func_name, ...) (DIRECTX_CALL_COMPONENT_SYMBOL(component, D3D11DispatchTable, func_name, __VA_ARGS__)) + +/* Structs */ +typedef struct _D3D11 D3D11; +typedef struct _D3D11DispatchTable D3D11DispatchTable; + +/* Functions */ +/* Courtesy http://code.google.com/p/theaimworldeditor/source/browse/trunk/DXUT/Core/DXUTmisc.cpp */ +typedef HRESULT (WINAPI *LPD3D11CREATEDEVICE)(gpointer /* IDXGIAdapter* */, UINT /* D3D11_DRIVER_TYPE */, HMODULE, UINT, UINT32, gpointer* /* ID3D11Device** */ ); + +struct _D3D11DispatchTable +{ + LPD3D11CREATEDEVICE D3D11CreateDevice; +}; + +/* Global data */ +struct _D3D11 +{ + D3D11DispatchTable vtable; +}; + +/* Global vars */ +static D3D11 dx11_d3d; + +/* Function declarations */ + +void dx11_d3d_init(DirectXAPIComponent* component, gpointer data); +DirectXD3D* dx11_d3d_create(const DirectXAPI* api); +gboolean dx11_d3d_resize(const DirectXD3D* d3d); +gboolean dx11_d3d_device_lost(const DirectXD3D* d3d); +gboolean dx11_d3d_notify_device_reset(const DirectXD3D* d3d); +gboolean dx11_d3d_release(const DirectXD3D* d3d); + +DIRECTX_D3D_API( + DIRECTX_11, + dx11_d3d.vtable, + dx11_d3d_init, + dx11_d3d_create, + dx11_d3d_resize, + dx11_d3d_device_lost, + dx11_d3d_notify_device_reset, + dx11_d3d_release +) + +#endif /* __DIRECTX_DIRECTX11_DX11_D3D_H__ */ diff --git a/sys/d3dvideosink/directx/directx9/dx9.c b/sys/d3dvideosink/directx/directx9/dx9.c new file mode 100644 index 0000000000..6d39afa996 --- /dev/null +++ b/sys/d3dvideosink/directx/directx9/dx9.c @@ -0,0 +1,27 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#include "dx9.h" + +void +dx9_init (const DirectXAPI * api) +{ + DIRECTX_DEBUG ("Initializing DirectX9 API"); + INITIALIZE_DIRECTX_D3D_API (DIRECTX_9, api); +} diff --git a/sys/d3dvideosink/directx/directx9/dx9.h b/sys/d3dvideosink/directx/directx9/dx9.h new file mode 100644 index 0000000000..6fb64026ae --- /dev/null +++ b/sys/d3dvideosink/directx/directx9/dx9.h @@ -0,0 +1,38 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX9_DX10_H__ +#define __DIRECTX_DIRECTX9_DX10_H__ + +#include "../dx.h" + +#include "dx9_d3d.h" + +void dx9_init(const DirectXAPI* api); + +DIRECTX_API( + DIRECTX_9, + dx9_init, + "d3d9", + "Direct3DCreate9", + "DirectX9Description", + "DirectX 9.0" +) + +#endif /* __DIRECTX_DIRECTX9_DX10_H__ */ diff --git a/sys/d3dvideosink/directx/directx9/dx9_d3d.c b/sys/d3dvideosink/directx/directx9/dx9_d3d.c new file mode 100644 index 0000000000..5b50aba0b4 --- /dev/null +++ b/sys/d3dvideosink/directx/directx9/dx9_d3d.c @@ -0,0 +1,73 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#include +#include + +#include "dx9_d3d.h" + +void +dx9_d3d_init (DirectXAPIComponent * component, gpointer data) +{ + DIRECTX_DEBUG ("Initializing Direct3D"); + DIRECTX_OPEN_COMPONENT_MODULE (component, "d3d9"); + + DIRECTX_DEBUG ("Setting Direct3D dispatch table"); + DIRECTX_OPEN_COMPONENT_SYMBOL (component, D3D9DispatchTable, Direct3DCreate9); + + //{ + // IDirect3D9* blah; + // DIRECTX_DEBUG("CALLING CREATE9!"); + // //blah = DX9_CALL_FUNC(data, Direct3DCreate9, D3D_SDK_VERSION); + // blah = DX9_D3D_COMPONENT_CALL_FUNC(component, Direct3DCreate9, D3D_SDK_VERSION); + // DIRECTX_DEBUG("RELEASING CREATE9!"); + // IDirect3D9_Release(blah); + // DIRECTX_DEBUG("RELEASED CREATE9!"); + //} +} + +DirectXD3D * +dx9_d3d_create (const DirectXAPI * api) +{ + return NULL; +} + +gboolean +dx9_d3d_resize (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx9_d3d_device_lost (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx9_d3d_notify_device_reset (const DirectXD3D * d3d) +{ + return TRUE; +} + +gboolean +dx9_d3d_release (const DirectXD3D * d3d) +{ + return TRUE; +} diff --git a/sys/d3dvideosink/directx/directx9/dx9_d3d.h b/sys/d3dvideosink/directx/directx9/dx9_d3d.h new file mode 100644 index 0000000000..28e27fe1eb --- /dev/null +++ b/sys/d3dvideosink/directx/directx9/dx9_d3d.h @@ -0,0 +1,71 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DIRECTX9_DX9_D3D_H__ +#define __DIRECTX_DIRECTX9_DX9_D3D_H__ + +#include + +#include "../d3d.h" + +#define DX9_D3D_API_CALL_FUNC(api, func_name, ...) (DIRECTX_CALL_COMPONENT_SYMBOL(DIRECTX_D3D(api), D3D9DispatchTable, func_name, __VA_ARGS__)) +#define DX9_D3D_COMPONENT_CALL_FUNC(component, func_name, ...) (DIRECTX_CALL_COMPONENT_SYMBOL(component, D3D9DispatchTable, func_name, __VA_ARGS__)) + +/* Structs */ +typedef struct _D3D9 D3D9; +typedef struct _D3D9DispatchTable D3D9DispatchTable; + +/* Functions */ +typedef gpointer /* IDirect3D9* */ (WINAPI *LPDIRECT3DCREATE9) (UINT); + +struct _D3D9DispatchTable +{ + LPDIRECT3DCREATE9 Direct3DCreate9; +}; + +/* Global data */ +struct _D3D9 +{ + D3D9DispatchTable vtable; +}; + +/* Global vars */ +static D3D9 dx9_d3d; + +/* Function declarations */ + +void dx9_d3d_init(DirectXAPIComponent* component, gpointer data); +DirectXD3D* dx9_d3d_create(const DirectXAPI* api); +gboolean dx9_d3d_resize(const DirectXD3D* d3d); +gboolean dx9_d3d_device_lost(const DirectXD3D* d3d); +gboolean dx9_d3d_notify_device_reset(const DirectXD3D* d3d); +gboolean dx9_d3d_release(const DirectXD3D* d3d); + +DIRECTX_D3D_API( + DIRECTX_9, + dx9_d3d.vtable, + dx9_d3d_init, + dx9_d3d_create, + dx9_d3d_resize, + dx9_d3d_device_lost, + dx9_d3d_notify_device_reset, + dx9_d3d_release +) + +#endif /* __DIRECTX_DIRECTX9_DX9_D3D_H__ */ diff --git a/sys/d3dvideosink/directx/dx.c b/sys/d3dvideosink/directx/dx.c new file mode 100644 index 0000000000..b52e51f564 --- /dev/null +++ b/sys/d3dvideosink/directx/dx.c @@ -0,0 +1,282 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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. + */ + +#include +#include +#include + +#include "dx.h" +#include "directx9/dx9.h" +#include "directx10/dx10.h" +#include "directx11/dx11.h" + + + +static void +init_supported_apis (void) +{ + /* Gather information we'll need about each version of DirectX. */ + /* Insert in reverse order of desired priority due to the g_list_prepend() call in directx_determine_best_available_api(). */ + INITIALIZE_SUPPORTED_DIRECTX_API (DIRECTX_9); + /* TODO: Add DirectX 10 support. */ + /*INITIALIZE_SUPPORTED_DIRECTX_API(DIRECTX_10); */ + /* TODO: Add DirectX 11 support. */ + /*INITIALIZE_SUPPORTED_DIRECTX_API(DIRECTX_11); */ +} + + + +/* Function declarations */ +static DirectXAPI *directx_determine_best_available_api (void); + +/* Mutex macros */ +#define DIRECTX_LOCK g_static_rec_mutex_lock (&dx_lock); +#define DIRECTX_UNLOCK g_static_rec_mutex_unlock (&dx_lock); + +typedef struct _DirectXInfo DirectXInfo; +struct _DirectXInfo +{ + gboolean initialized; + gboolean supported; + + DirectXInitParams *init_params; + DirectXAPI *best_api; + GList *supported_api_list; + gint32 supported_api_count; +}; + +/* Private vars */ +static DirectXInfo dx; +static GStaticRecMutex dx_lock = G_STATIC_REC_MUTEX_INIT; + +gboolean +directx_initialize (DirectXInitParams * init_params) +{ + DIRECTX_LOCK if (dx.initialized) + goto success; + + dx.init_params = NULL; + dx.init_params = init_params; + + init_supported_apis (); + + dx.best_api = directx_determine_best_available_api (); + dx.supported = (dx.best_api != NULL + && !DIRECTX_VERSION_IS_UNKNOWN (dx.best_api->version)); + dx.initialized = TRUE; + +success: + DIRECTX_UNLOCK return TRUE; +} + +gboolean +directx_api_initialize (DirectXAPI * api) +{ + if (!api) + return FALSE; + + DIRECTX_LOCK if (!directx_is_initialized ()) + goto error; + + if (api->initialized) + goto success; + + /* API init */ + api->initialize (api); + + /* Component initialization */ + DIRECTX_COMPONENT_INIT (DIRECTX_D3D (api)); + DIRECTX_COMPONENT_INIT (DIRECTX_DINPUT (api)); + DIRECTX_COMPONENT_INIT (DIRECTX_DSOUND (api)); + DIRECTX_COMPONENT_INIT (DIRECTX_DWRITE (api)); + DIRECTX_COMPONENT_INIT (DIRECTX_D2D (api)); + DIRECTX_COMPONENT_INIT (DIRECTX_DCOMPUTE (api)); + + /* All done */ + api->initialized = TRUE; + +success: + DIRECTX_UNLOCK return TRUE; +error: + DIRECTX_UNLOCK return FALSE; +} + +gboolean +directx_initialize_best_available_api (void) +{ + return directx_api_initialize (directx_get_best_available_api ()); +} + +gboolean +directx_is_initialized (void) +{ + gboolean initialized = FALSE; + + DIRECTX_LOCK initialized = dx.initialized; + DIRECTX_UNLOCK return initialized; +} + +gboolean +directx_api_is_initialized (const DirectXAPI * api) +{ + if (!api) + return FALSE; + { + gboolean initialized; + + DIRECTX_LOCK initialized = api->initialized; + DIRECTX_UNLOCK return initialized; + } +} + +gboolean +directx_best_available_api_is_initialized (void) +{ + return directx_api_is_initialized (directx_get_best_available_api ()); +} + +gboolean +directx_is_supported (void) +{ + return dx.supported; +} + +GList * +directx_get_supported_apis (void) +{ + return dx.supported_api_list; +} + +gint32 +directx_get_supported_api_count (void) +{ + return dx.supported_api_count; +} + +DirectXAPI * +directx_get_best_available_api (void) +{ + return dx.best_api; +} + +void +directx_log_debug (const gchar * file, const gchar * function, gint line, + const gchar * format, ...) +{ + if (!dx.init_params || !dx.init_params->log_debug) + return; + { + va_list args; + va_start (args, format); + dx.init_params->log_debug (file, function, line, format, args); + va_end (args); + } +} + +void +directx_log_warning (const gchar * file, const gchar * function, gint line, + const gchar * format, ...) +{ + if (!dx.init_params || !dx.init_params->log_warning) + return; + { + va_list args; + va_start (args, format); + dx.init_params->log_warning (file, function, line, format, args); + va_end (args); + } +} + +void +directx_log_error (const gchar * file, const gchar * function, gint line, + const gchar * format, ...) +{ + if (!dx.init_params || !dx.init_params->log_error) + return; + { + va_list args; + va_start (args, format); + dx.init_params->log_error (file, function, line, format, args); + va_end (args); + } +} + +/* This should only be called through use of the DIRECTX_API() macro. It should never be called directly. */ +gboolean +directx_add_supported_api (DirectXAPI * api) +{ + if (!api) + return FALSE; + + DIRECTX_LOCK { + + /* Add to our GList containing all of our supported APIs. */ + /* GLists are doubly-linked lists and calling prepend() prevents it from having to traverse the entire list just to add one item. */ + dx.supported_api_list = g_list_prepend (dx.supported_api_list, api); + dx.supported_api_count++; + + } +/*success:*/ + DIRECTX_UNLOCK return TRUE; +} + +static DirectXAPI * +directx_determine_best_available_api (void) +{ + if (!g_module_supported ()) + return NULL; + + { + GList *item; + GModule *lib; + DirectXAPI *dxlib = NULL; + + DIRECTX_LOCK { + /* Search supported APIs (DirectX9, DirectX10, etc.) looking for the first one that works. */ + DIRECTX_DEBUG + ("Searching supported DirectX APIs for the best (most recent) one available"); + for (item = g_list_first (dx.supported_api_list); item; item = item->next) { + if ((dxlib = (DirectXAPI *) item->data) == NULL) + continue; + + DIRECTX_DEBUG ("Determining support for %s", dxlib->description); + DIRECTX_DEBUG ("Searching for module \"%s\" with the symbol \"%s\"", + dxlib->module_test, dxlib->symbol_test); + + /* Can we locate and open a Direct3D library (e.g. d3d9.dll or d3d10.dll)? */ + if ((lib = + g_module_open (dxlib->module_test, + G_MODULE_BIND_LAZY)) != NULL) { + /* Look for a symbol/function (e.g. "Direct3DCreate9") in the module and if it exists, we found one! */ + gpointer symbol; + if (g_module_symbol (lib, dxlib->symbol_test, &symbol)) { + g_module_close (lib); + DIRECTX_DEBUG ("Selected %s", dxlib->description); + goto done; + } + /* Ensure we don't have a mem leak. */ + g_module_close (lib); + } + } + + } + done: + DIRECTX_UNLOCK return dxlib; + } +} diff --git a/sys/d3dvideosink/directx/dx.h b/sys/d3dvideosink/directx/dx.h new file mode 100644 index 0000000000..17c9b32508 --- /dev/null +++ b/sys/d3dvideosink/directx/dx.h @@ -0,0 +1,265 @@ +/* GStreamer + * Copyright (C) 2011 David Hoyt + * + * 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 __DIRECTX_DX_H__ +#define __DIRECTX_DX_H__ + +#include +#include + +G_BEGIN_DECLS + +#define WM_DIRECTX WM_USER + 500 + +#define DIRECTX_VERSION_UNKNOWN 0 + +#define DIRECTX_VERSION_ENCODE_FULL(major, minor, micro) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((micro) * 1)) + +#define DIRECTX_VERSION_ENCODE(major) \ + DIRECTX_VERSION_ENCODE_FULL(major, 0, 0) + +typedef enum +{ + DIRECTX_UNKNOWN = DIRECTX_VERSION_UNKNOWN, + DIRECTX_9 = DIRECTX_VERSION_ENCODE(9), + DIRECTX_10 = DIRECTX_VERSION_ENCODE(10), + DIRECTX_10_1 = DIRECTX_VERSION_ENCODE_FULL(10, 1, 0), + DIRECTX_11 = DIRECTX_VERSION_ENCODE(11) +} DirectXVersion; + +#define DIRECTX_API(version, initialization_function, module_test, symbol_test, i18n_key, description) \ + static DirectXAPIComponent DIRECTX_ ## version ## _DIRECT3D_COMPONENT = { \ + NULL /*api*/ \ + , FALSE /*initialized*/ \ + , NULL /*initialize*/ \ + , NULL /*module*/ \ + , NULL /*module_name*/ \ + , NULL /*private_data*/ \ + }; \ + static DirectXAPIComponent DIRECTX_ ## version ## _DIRECTINPUT_COMPONENT = { \ + NULL /*api*/ \ + , FALSE /*initialized*/ \ + , NULL /*initialize*/ \ + , NULL /*module*/ \ + , NULL /*module_name*/ \ + , NULL /*private_data*/ \ + }; \ + static DirectXAPIComponent DIRECTX_ ## version ## _DIRECTSOUND_COMPONENT = { \ + NULL /*api*/ \ + , FALSE /*initialized*/ \ + , NULL /*initialize*/ \ + , NULL /*module*/ \ + , NULL /*module_name*/ \ + , NULL /*private_data*/ \ + }; \ + static DirectXAPIComponent DIRECTX_ ## version ## _DIRECTWRITE_COMPONENT = { \ + NULL /*api*/ \ + , FALSE /*initialized*/ \ + , NULL /*initialize*/ \ + , NULL /*module*/ \ + , NULL /*module_name*/ \ + , NULL /*private_data*/ \ + }; \ + static DirectXAPIComponent DIRECTX_ ## version ## _DIRECT2D_COMPONENT = { \ + NULL /*api*/ \ + , FALSE /*initialized*/ \ + , NULL /*initialize*/ \ + , NULL /*module*/ \ + , NULL /*module_name*/ \ + , NULL /*private_data*/ \ + }; \ + static DirectXAPIComponent DIRECTX_ ## version ## _DIRECTCOMPUTE_COMPONENT = { \ + NULL /*api*/ \ + , FALSE /*initialized*/ \ + , NULL /*initialize*/ \ + , NULL /*module*/ \ + , NULL /*module_name*/ \ + , NULL /*private_data*/ \ + }; \ + static DirectXAPI DIRECTX_ ## version ## _API = { \ + version \ + , module_test "." G_MODULE_SUFFIX \ + , symbol_test \ + , i18n_key \ + , description \ + , FALSE \ + , initialization_function \ + , &DIRECTX_ ## version ## _DIRECT3D_COMPONENT \ + , &DIRECTX_ ## version ## _DIRECTINPUT_COMPONENT \ + , &DIRECTX_ ## version ## _DIRECTSOUND_COMPONENT \ + , &DIRECTX_ ## version ## _DIRECTWRITE_COMPONENT \ + , &DIRECTX_ ## version ## _DIRECT2D_COMPONENT \ + , &DIRECTX_ ## version ## _DIRECTCOMPUTE_COMPONENT \ + , {NULL, NULL, NULL} /*reserved*/ \ + }; \ + static void init_directx_ ## version ## _supported_api(void) { \ + DirectXAPI* api; \ + api = &DIRECTX_ ## version ## _API; \ + api->d3d->api = api; \ + api->dinput->api = api; \ + api->dsound->api = api; \ + api->dwrite->api = api; \ + api->d2d->api = api; \ + api->dcompute->api = api; \ + directx_add_supported_api(api); \ + } + +#define INITIALIZE_SUPPORTED_DIRECTX_API(version) \ + init_directx_ ## version ## _supported_api(); + +#define DIRECTX_COMPONENT_INIT(component) \ + { \ + if (component != NULL && component->initialize != NULL && !component->initialized) { \ + component->initialize(component, DIRECTX_COMPONENT_DATA(component)); \ + } \ + } + +#define DIRECTX_OPEN_COMPONENT_MODULE(component, component_module_name) \ + { \ + GModule* lib; \ + if (component && component->module == NULL && (lib = g_module_open(component_module_name "." G_MODULE_SUFFIX, G_MODULE_BIND_LAZY)) != NULL) { \ + component->module_name = component_module_name "." G_MODULE_SUFFIX; \ + component->module = lib; \ + } \ + } + +#define DIRECTX_OPEN_COMPONENT_SYMBOL(component, dispatch_table_type, component_symbol_name) \ + { \ + gpointer symbol; \ + if (component && component->module && g_module_symbol(component->module, #component_symbol_name, &symbol)) { \ + ((dispatch_table_type*)component->vtable)->component_symbol_name = symbol; \ + } \ + } + +#define DIRECTX_CALL_COMPONENT_SYMBOL(component, dispatch_table_type, component_symbol_name, ...) \ + (((dispatch_table_type*)component->vtable)->component_symbol_name(__VA_ARGS__)) + + +/* Borrowed from GST_FUNCTION */ +#ifndef DIRECTX_FUNCTION +#if defined (__GNUC__) || (defined (_MSC_VER) && _MSC_VER >= 1300) +# define DIRECTX_FUNCTION ((const char*) (__FUNCTION__)) +#elif defined (__STDC__) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define DIRECTX_FUNCTION ((const char*) (__func__)) +#else +# define DIRECTX_FUNCTION ((const char*) ("???")) +#endif +#endif + +#define DIRECTX_DEBUG(...) (directx_log_debug(__FILE__, DIRECTX_FUNCTION, __LINE__, ##__VA_ARGS__)) +#define DIRECTX_WARNING(...) (directx_log_warning(__FILE__, DIRECTX_FUNCTION, __LINE__, ##__VA_ARGS__)) +#define DIRECTX_ERROR(...) (directx_log_error(__FILE__, DIRECTX_FUNCTION, __LINE__, ##__VA_ARGS__)) + +#define DIRECTX_COMPONENT_API(component) (component->api) +#define DIRECTX_COMPONENT_DATA(component) (component->private_data) +#define DIRECTX_SET_COMPONENT_DATA(component, data) (component->private_data = data) +#define DIRECTX_SET_COMPONENT_INIT(component, init_function) (component->initialize = init_function) +#define DIRECTX_SET_COMPONENT_DISPATCH_TABLE(component, dispatch_table) (component->vtable = dispatch_table) +#define DIRECTX_VERSION_IS_UNKNOWN(version) (version == DIRECTX_VERSION_UNKNOWN) +#define DIRECTX_SUPPORTED_API_IS_LAST(lib) (lib == NULL || lib->version == DIRECTX_VERSION_UNKNOWN || lib->module_name == NULL) + +typedef struct _DirectXInitParams DirectXInitParams; +typedef struct _DirectXAPI DirectXAPI; +typedef struct _DirectXAPIComponent DirectXAPIComponent; + +/* Function pointers */ +typedef void (*DirectXInitializationFunction) (const DirectXAPI* api); +typedef void (*DirectXLogFunction) (const gchar* file, const gchar* function, gint line, const gchar* format, va_list args); /* vprintf-style logging function */ + +struct _DirectXInitParams +{ + DirectXLogFunction log_debug; + DirectXLogFunction log_warning; + DirectXLogFunction log_error; +}; + +struct _DirectXAPI +{ + gint version; + const gchar* module_test; + const gchar* symbol_test; + const gchar* i18n_key; + const gchar* description; + gboolean initialized; + DirectXInitializationFunction initialize; + DirectXAPIComponent* d3d; + DirectXAPIComponent* dinput; + DirectXAPIComponent* dsound; + DirectXAPIComponent* dwrite; + DirectXAPIComponent* d2d; + DirectXAPIComponent* dcompute; + gpointer reserved[3]; +}; + +#define DIRECTX_D3D(api) (api->d3d) +#define DIRECTX_DINPUT(api) (api->dinput) +#define DIRECTX_DSOUND(api) (api->dsound) +#define DIRECTX_DWRITE(api) (api->dwrite) +#define DIRECTX_D2D(api) (api->d2d) +#define DIRECTX_DCOMPUTE(api) (api->dcompute) + +#define DIRECTX_D3D_COMPONENT_DATA(api) (DIRECTX_COMPONENT_DATA(DIRECTX_D3D(api))) +#define DIRECTX_DINPUT_COMPONENT_DATA(api) (DIRECTX_COMPONENT_DATA(DIRECTX_DINPUT(api))) +#define DIRECTX_DSOUND_COMPONENT_DATA(api) (DIRECTX_COMPONENT_DATA(DIRECTX_DSOUND(api))) +#define DIRECTX_DWRITE_COMPONENT_DATA(api) (DIRECTX_COMPONENT_DATA(DIRECTX_DWRITE(api))) +#define DIRECTX_D2D_COMPONENT_DATA(api) (DIRECTX_COMPONENT_DATA(DIRECTX_D2D(api))) +#define DIRECTX_DCOMPUTE_COMPONENT_DATA(api) (DIRECTX_COMPONENT_DATA(DIRECTX_DCOMPUTE(api))) + +/* DirectX component function table */ +typedef void (*DirectXComponentInitializeFunction) (DirectXAPIComponent* d3d, gpointer data); +struct _DirectXAPIComponent +{ + DirectXAPI* api; + gboolean initialized; + DirectXComponentInitializeFunction initialize; + + GModule* module; + const gchar* module_name; + + gpointer vtable; + + gpointer private_data; +}; + +gboolean directx_initialize (DirectXInitParams* init_params); +gboolean directx_is_initialized (void); + +gboolean directx_is_supported (void); + +void directx_log_debug(const gchar* file, const gchar* function, gint line, const gchar * format, ...); +void directx_log_warning(const gchar* file, const gchar* function, gint line, const gchar * format, ...); +void directx_log_error(const gchar* file, const gchar* function, gint line, const gchar * format, ...); + +GList* directx_get_supported_apis (void); +gint32 directx_get_supported_api_count (void); +gboolean directx_add_supported_api (DirectXAPI* api); + +DirectXAPI* directx_get_best_available_api (void); +gboolean directx_initialize_best_available_api (void); +gboolean directx_best_available_api_is_initialized (void); + +gboolean directx_api_initialize (DirectXAPI* api); +gboolean directx_api_is_initialized (const DirectXAPI* api); + +G_END_DECLS + +#endif /* __DIRECTX_DX_H__ */