summaryrefslogtreecommitdiffstats
path: root/sys/directdraw/gstdirectdrawsink.c
diff options
context:
space:
mode:
authorDave Robillard <dave@drobilla.net>2009-05-26 19:10:44 -0400
committerDave Robillard <dave@drobilla.net>2009-05-26 19:10:44 -0400
commitb75a26657febaf86c4137b4d41c068926325e316 (patch)
tree65c161824169ac09bf8418244937aec6ab77a270 /sys/directdraw/gstdirectdrawsink.c
parent4e1d3bba9c21cb8bbfe70ffed953a8385fb7314d (diff)
parent8f70498c898a65d0938e3e104e91662ff5b693c3 (diff)
downloadgst-plugins-bad-b75a26657febaf86c4137b4d41c068926325e316.tar.gz
gst-plugins-bad-b75a26657febaf86c4137b4d41c068926325e316.tar.bz2
gst-plugins-bad-b75a26657febaf86c4137b4d41c068926325e316.zip
Merge branch 'master' of git://anongit.freedesktop.org/gstreamer/gst-plugins-bad into fdo
Diffstat (limited to 'sys/directdraw/gstdirectdrawsink.c')
-rw-r--r--sys/directdraw/gstdirectdrawsink.c1945
1 files changed, 1945 insertions, 0 deletions
diff --git a/sys/directdraw/gstdirectdrawsink.c b/sys/directdraw/gstdirectdrawsink.c
new file mode 100644
index 00000000..144f6595
--- /dev/null
+++ b/sys/directdraw/gstdirectdrawsink.c
@@ -0,0 +1,1945 @@
+/* GStreamer
+ * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
+ * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * The development of this code was made possible due to the involvement
+ * of Pioneers of the Inevitable, the creators of the Songbird Music player
+ *
+ */
+
+/**
+ * SECTION:element-directdrawsink
+ *
+ * DirectdrawSink renders video RGB frames to any win32 window. This element
+ * can receive a window ID from the application through the #XOverlay interface
+ * and will then render video frames in this window.
+ * If no Window ID was provided by the application, the element will create its
+ * own internal window and render into it.
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch -v videotestsrc ! directdrawsink
+ * ]| a simple pipeline to test the sink
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstdirectdrawsink.h"
+
+GST_DEBUG_CATEGORY_STATIC (directdrawsink_debug);
+#define GST_CAT_DEFAULT directdrawsink_debug
+
+/* elementfactory information */
+static const GstElementDetails gst_directdraw_sink_details =
+GST_ELEMENT_DETAILS ("Direct Draw Video Sink",
+ "Sink/Video",
+ "Output to a video card via Direct Draw",
+ "Sebastien Moutte <sebastien@moutte.net>");
+
+static void gst_directdraw_sink_init_interfaces (GType type);
+
+GST_BOILERPLATE_FULL (GstDirectDrawSink, gst_directdraw_sink, GstVideoSink,
+ GST_TYPE_VIDEO_SINK, gst_directdraw_sink_init_interfaces);
+
+static void gst_directdraw_sink_finalize (GObject * object);
+static void gst_directdraw_sink_set_property (GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_directdraw_sink_get_property (GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+static GstCaps *gst_directdraw_sink_get_caps (GstBaseSink * bsink);
+static gboolean gst_directdraw_sink_set_caps (GstBaseSink * bsink,
+ GstCaps * caps);
+static GstStateChangeReturn gst_directdraw_sink_change_state (GstElement *
+ element, GstStateChange transition);
+static GstFlowReturn gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink,
+ guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+static void gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
+ GstClockTime * start, GstClockTime * end);
+static GstFlowReturn gst_directdraw_sink_show_frame (GstBaseSink * bsink,
+ GstBuffer * buf);
+
+/* utils */
+static gboolean gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink);
+static gboolean gst_directdraw_sink_create_default_window (GstDirectDrawSink *
+ ddrawsink);
+static gboolean gst_directdraw_sink_check_primary_surface (GstDirectDrawSink *
+ ddrawsink);
+static gboolean gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink *
+ ddrawsink);
+static GstCaps *gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink *
+ ddrawsink);
+static GstCaps
+ * gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc);
+static void gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink);
+static void gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink *
+ ddrawsink);
+static int gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat);
+static gboolean gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink *
+ ddrawsink, GstCaps * caps, DDPIXELFORMAT * pPixelFormat);
+static void gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink,
+ RECT src, RECT dst, RECT * result);
+char *DDErrorString (HRESULT hr);
+
+/* surfaces management functions */
+static void gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
+ GstDDrawSurface * surface);
+static GstDDrawSurface *gst_directdraw_sink_surface_create (GstDirectDrawSink *
+ ddrawsink, GstCaps * caps, size_t size);
+static gboolean gst_directdraw_sink_surface_check (GstDirectDrawSink *
+ ddrawsink, GstDDrawSurface * surface);
+
+static GstStaticPadTemplate directdrawsink_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw-rgb, "
+ "framerate = (fraction) [ 0, MAX ], "
+ "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
+ );
+
+enum
+{
+ PROP_0,
+ PROP_KEEP_ASPECT_RATIO
+};
+
+/* XOverlay interface implementation */
+static gboolean
+gst_directdraw_sink_interface_supported (GstImplementsInterface * iface,
+ GType type)
+{
+ g_assert (type == GST_TYPE_X_OVERLAY);
+ return TRUE;
+}
+
+static void
+gst_directdraw_sink_interface_init (GstImplementsInterfaceClass * klass)
+{
+ klass->supported = gst_directdraw_sink_interface_supported;
+}
+
+static void
+gst_directdraw_sink_set_window_id (GstXOverlay * overlay, ULONG window_id)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
+
+ GST_OBJECT_LOCK (ddrawsink);
+ /* check if we are already using this window id */
+ if (ddrawsink->video_window == (HWND) window_id) {
+ GST_OBJECT_UNLOCK (ddrawsink);
+ return;
+ }
+
+ if (window_id) {
+ HRESULT hres;
+
+ /* If we had an internal window, close it first */
+ if (ddrawsink->video_window && ddrawsink->our_video_window) {
+ /* Trick to let the event thread know that it has to die silently */
+ ddrawsink->our_video_window = FALSE;
+ /* Post quit message and wait for our event window thread */
+ PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
+ }
+
+ ddrawsink->video_window = (HWND) window_id;
+ ddrawsink->our_video_window = FALSE;
+ if (ddrawsink->setup) {
+ /* update the clipper object with the new window */
+ hres = IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0,
+ ddrawsink->video_window);
+ }
+ }
+ /* FIXME: Handle the case where window_id is 0 and we want the sink to
+ * create a new window when playback was already started (after set_caps) */
+ GST_OBJECT_UNLOCK (ddrawsink);
+}
+
+static void
+gst_directdraw_sink_expose (GstXOverlay * overlay)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
+
+ gst_directdraw_sink_show_frame (GST_BASE_SINK (ddrawsink), NULL);
+}
+
+static void
+gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
+{
+ iface->set_xwindow_id = gst_directdraw_sink_set_window_id;
+ iface->expose = gst_directdraw_sink_expose;
+}
+
+static void
+gst_directdraw_sink_init_interfaces (GType type)
+{
+ static const GInterfaceInfo iface_info = {
+ (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
+ NULL,
+ NULL,
+ };
+
+ static const GInterfaceInfo xoverlay_info = {
+ (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_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);
+}
+
+/* Subclass of GstBuffer which manages buffer_pool surfaces lifetime */
+static void gst_ddrawsurface_finalize (GstMiniObject * mini_object);
+static GstBufferClass *ddrawsurface_parent_class = NULL;
+
+static void
+gst_ddrawsurface_init (GstDDrawSurface * surface, gpointer g_class)
+{
+ surface->surface = NULL;
+ surface->width = 0;
+ surface->height = 0;
+ surface->ddrawsink = NULL;
+ surface->locked = FALSE;
+ surface->system_memory = FALSE;
+ memset (&surface->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
+}
+
+static void
+gst_ddrawsurface_class_init (gpointer g_class, gpointer class_data)
+{
+ GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
+
+ ddrawsurface_parent_class = g_type_class_peek_parent (g_class);
+
+ mini_object_class->finalize = GST_DEBUG_FUNCPTR (gst_ddrawsurface_finalize);
+}
+
+GType
+gst_ddrawsurface_get_type (void)
+{
+ static GType _gst_ddrawsurface_type;
+
+ if (G_UNLIKELY (_gst_ddrawsurface_type == 0)) {
+ static const GTypeInfo ddrawsurface_info = {
+ sizeof (GstBufferClass),
+ NULL,
+ NULL,
+ gst_ddrawsurface_class_init,
+ NULL,
+ NULL,
+ sizeof (GstDDrawSurface),
+ 0,
+ (GInstanceInitFunc) gst_ddrawsurface_init,
+ NULL
+ };
+ _gst_ddrawsurface_type = g_type_register_static (GST_TYPE_BUFFER,
+ "GstDDrawSurface", &ddrawsurface_info, 0);
+ }
+ return _gst_ddrawsurface_type;
+}
+
+static void
+gst_ddrawsurface_finalize (GstMiniObject * mini_object)
+{
+ GstDirectDrawSink *ddrawsink = NULL;
+ GstDDrawSurface *surface;
+
+ surface = (GstDDrawSurface *) mini_object;
+
+ ddrawsink = surface->ddrawsink;
+ if (!ddrawsink)
+ goto no_sink;
+
+ /* If our geometry changed we can't reuse that image. */
+ if ((surface->width != ddrawsink->video_width) ||
+ (surface->height != ddrawsink->video_height) ||
+ (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
+ sizeof (DDPIXELFORMAT)) != 0 ||
+ !gst_directdraw_sink_surface_check (ddrawsink, surface))
+ ) {
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "destroy image as its size changed %dx%d vs current %dx%d",
+ surface->width, surface->height, ddrawsink->video_width,
+ ddrawsink->video_height);
+ gst_directdraw_sink_surface_destroy (ddrawsink, surface);
+ GST_MINI_OBJECT_CLASS (ddrawsurface_parent_class)->finalize (mini_object);
+ } else {
+ /* In that case we can reuse the image and add it to our image pool. */
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "recycling image in pool");
+
+ /* need to increment the refcount again to recycle */
+ gst_buffer_ref (GST_BUFFER (surface));
+
+ g_mutex_lock (ddrawsink->pool_lock);
+ ddrawsink->buffer_pool = g_slist_prepend (ddrawsink->buffer_pool, surface);
+ g_mutex_unlock (ddrawsink->pool_lock);
+ }
+
+ return;
+
+no_sink:
+ GST_CAT_WARNING (directdrawsink_debug, "no sink found");
+ GST_MINI_OBJECT_CLASS (ddrawsurface_parent_class)->finalize (mini_object);
+ return;
+}
+
+/************************************************************************/
+/* Directdraw sink functions */
+/************************************************************************/
+static void
+gst_directdraw_sink_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (element_class, &gst_directdraw_sink_details);
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&directdrawsink_sink_factory));
+}
+
+static void
+gst_directdraw_sink_class_init (GstDirectDrawSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ GST_DEBUG_CATEGORY_INIT (directdrawsink_debug, "directdrawsink", 0,
+ "Directdraw sink");
+
+ gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directdraw_sink_finalize);
+ gobject_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_property);
+ gobject_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_property);
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_change_state);
+ gstbasesink_class->get_caps =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_caps);
+ gstbasesink_class->set_caps =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_caps);
+ gstbasesink_class->preroll =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
+ gstbasesink_class->render =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
+ gstbasesink_class->get_times =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_times);
+ gstbasesink_class->buffer_alloc =
+ GST_DEBUG_FUNCPTR (gst_directdraw_sink_buffer_alloc);
+
+ /* install properties */
+ /* setup aspect ratio mode */
+ 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,
+ G_PARAM_READWRITE));
+}
+
+static void
+gst_directdraw_sink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
+
+ switch (prop_id) {
+ case PROP_KEEP_ASPECT_RATIO:
+ ddrawsink->keep_aspect_ratio = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_directdraw_sink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
+
+ switch (prop_id) {
+ case PROP_KEEP_ASPECT_RATIO:
+ g_value_set_boolean (value, ddrawsink->keep_aspect_ratio);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_directdraw_sink_finalize (GObject * object)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
+
+ if (ddrawsink->pool_lock) {
+ g_mutex_free (ddrawsink->pool_lock);
+ ddrawsink->pool_lock = NULL;
+ }
+ if (ddrawsink->caps) {
+ gst_caps_unref (ddrawsink->caps);
+ ddrawsink->caps = NULL;
+ }
+ if (ddrawsink->setup) {
+ gst_directdraw_sink_cleanup (ddrawsink);
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_directdraw_sink_init (GstDirectDrawSink * ddrawsink,
+ GstDirectDrawSinkClass * g_class)
+{
+ /*init members variables */
+ ddrawsink->ddraw_object = NULL;
+ ddrawsink->primary_surface = NULL;
+ ddrawsink->offscreen_surface = NULL;
+ ddrawsink->clipper = NULL;
+ ddrawsink->video_window = NULL;
+ ddrawsink->our_video_window = TRUE;
+ ddrawsink->last_buffer = NULL;
+ ddrawsink->caps = NULL;
+ ddrawsink->window_thread = NULL;
+ ddrawsink->setup = FALSE;
+ ddrawsink->buffer_pool = NULL;
+ ddrawsink->keep_aspect_ratio = FALSE;
+ ddrawsink->pool_lock = g_mutex_new ();
+ ddrawsink->can_blit_between_colorspace = TRUE;
+ ddrawsink->must_recreate_offscreen = FALSE;
+ memset (&ddrawsink->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
+
+ /*video default values */
+ ddrawsink->video_height = 0;
+ ddrawsink->video_width = 0;
+ ddrawsink->fps_n = 0;
+ ddrawsink->fps_d = 0;
+}
+
+static GstCaps *
+gst_directdraw_sink_get_caps (GstBaseSink * bsink)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+ GstCaps *caps = NULL;
+
+ if (!ddrawsink->setup) {
+ caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
+ (ddrawsink)));
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "getcaps called and we are not setup yet, " "returning template %"
+ GST_PTR_FORMAT, caps);
+ } else {
+ caps = gst_caps_ref (ddrawsink->caps);
+ }
+
+ return caps;
+}
+
+static gboolean
+gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+ GstStructure *structure = NULL;
+ gboolean ret;
+ const GValue *fps;
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (!structure)
+ return FALSE;
+
+ ret = gst_structure_get_int (structure, "width", &ddrawsink->video_width);
+ ret &= gst_structure_get_int (structure, "height", &ddrawsink->video_height);
+ fps = gst_structure_get_value (structure, "framerate");
+ ret &= (fps != NULL);
+ ret &=
+ gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
+ &ddrawsink->dd_pixel_format);
+ if (!ret) {
+ GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
+ ("Failed to get caps properties from caps"), (NULL));
+ return FALSE;
+ }
+
+ ddrawsink->fps_n = gst_value_get_fraction_numerator (fps);
+ ddrawsink->fps_d = gst_value_get_fraction_denominator (fps);
+
+ /* Notify application to set window id now */
+ if (!ddrawsink->video_window) {
+ gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ddrawsink));
+ }
+
+ /* If we still don't have a window at that stage we create our own */
+ if (!ddrawsink->video_window) {
+ gst_directdraw_sink_create_default_window (ddrawsink);
+ }
+
+ /* if we are rendering to our own window, resize it to video size */
+ if (ddrawsink->video_window && ddrawsink->our_video_window) {
+ SetWindowPos (ddrawsink->video_window, NULL,
+ 0, 0, ddrawsink->video_width + (GetSystemMetrics (SM_CXSIZEFRAME) * 2),
+ ddrawsink->video_height + GetSystemMetrics (SM_CYCAPTION) +
+ (GetSystemMetrics (SM_CYSIZEFRAME) * 2), SWP_SHOWWINDOW | SWP_NOMOVE);
+ }
+
+ /* release the surface, we have to recreate it! */
+ if (ddrawsink->offscreen_surface) {
+ IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+ ddrawsink->offscreen_surface = NULL;
+ }
+
+ /* create an offscreen surface with the caps */
+ ret = gst_directdraw_sink_check_offscreen_surface (ddrawsink);
+ if (!ret) {
+ GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
+ ("Can't create a directdraw offscreen surface with the input caps"),
+ (NULL));
+ }
+
+ return ret;
+}
+
+static GstStateChangeReturn
+gst_directdraw_sink_change_state (GstElement * element,
+ GstStateChange transition)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ if (!gst_directdraw_sink_setup_ddraw (ddrawsink)) {
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto beach;
+ }
+
+ if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink))) {
+ ret = GST_STATE_CHANGE_FAILURE;
+ goto beach;
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ ddrawsink->fps_n = 0;
+ ddrawsink->fps_d = 1;
+ ddrawsink->video_width = 0;
+ ddrawsink->video_height = 0;
+ if (ddrawsink->buffer_pool)
+ gst_directdraw_sink_bufferpool_clear (ddrawsink);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ if (ddrawsink->setup)
+ gst_directdraw_sink_cleanup (ddrawsink);
+ break;
+ default:
+ break;
+ }
+
+beach:
+ return ret;
+}
+
+static GstFlowReturn
+gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
+ guint size, GstCaps * caps, GstBuffer ** buf)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+ GstStructure *structure;
+ gint width, height;
+ GstDDrawSurface *surface = NULL;
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstCaps *buffer_caps = caps;
+ gboolean buffercaps_unref = FALSE;
+
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "a buffer of %u bytes was requested", size);
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (!gst_structure_get_int (structure, "width", &width) ||
+ !gst_structure_get_int (structure, "height", &height)) {
+ GST_WARNING_OBJECT (ddrawsink, "invalid caps for buffer allocation %"
+ GST_PTR_FORMAT, caps);
+ return GST_FLOW_UNEXPECTED;
+ }
+
+ g_mutex_lock (ddrawsink->pool_lock);
+
+ /* Inspect our buffer pool */
+ while (ddrawsink->buffer_pool) {
+ surface = (GstDDrawSurface *) ddrawsink->buffer_pool->data;
+ if (surface) {
+ /* Removing from the pool */
+ ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
+ ddrawsink->buffer_pool);
+
+ /* If the surface is invalid for our need, destroy */
+ if ((surface->width != width) ||
+ (surface->height != height) ||
+ (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
+ sizeof (DDPIXELFORMAT)) ||
+ !gst_directdraw_sink_surface_check (ddrawsink, surface))
+ ) {
+ gst_directdraw_sink_surface_destroy (ddrawsink, surface);
+ gst_buffer_unref (GST_BUFFER_CAST (surface));
+ surface = NULL;
+ } else {
+ /* We found a suitable surface */
+ break;
+ }
+ }
+ }
+
+ if (!ddrawsink->can_blit_between_colorspace) {
+ /* Hardware doesn't support blit from one colorspace to another.
+ * Check if the colorspace of the current display mode has changed since
+ * the last negociation. If it's the case, we will have to renegociate
+ */
+ guint depth;
+ HRESULT hres;
+ DDSURFACEDESC2 surface_desc;
+ DDSURFACEDESC2 *sd;
+
+ if (!gst_structure_get_int (structure, "depth", (gint *) & depth)) {
+ GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+ "Can't get depth from buffer_alloc caps");
+ return GST_FLOW_ERROR;
+ }
+ surface_desc.dwSize = sizeof (surface_desc);
+ sd = &surface_desc;
+ hres =
+ IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
+ (DDSURFACEDESC *) sd);
+ if (hres != DD_OK) {
+ GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+ "Can't get current display mode (error=%ld)", (glong) hres);
+ return GST_FLOW_ERROR;
+ }
+
+ if (depth != gst_directdraw_sink_get_depth (&surface_desc.ddpfPixelFormat)) {
+ GstCaps *copy_caps = NULL;
+ GstStructure *copy_structure = NULL;
+ GstCaps *display_caps = NULL;
+ GstStructure *display_structure = NULL;
+
+ /* make a copy of the original caps */
+ copy_caps = gst_caps_copy (caps);
+ copy_structure = gst_caps_get_structure (copy_caps, 0);
+
+ display_caps =
+ gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
+ if (display_caps) {
+ display_structure = gst_caps_get_structure (display_caps, 0);
+ if (display_structure) {
+ gint bpp, endianness, red_mask, green_mask, blue_mask;
+
+ /* get new display mode properties */
+ gst_structure_get_int (display_structure, "depth", (gint *) & depth);
+ gst_structure_get_int (display_structure, "bpp", &bpp);
+ gst_structure_get_int (display_structure, "endianness", &endianness);
+ gst_structure_get_int (display_structure, "red_mask", &red_mask);
+ gst_structure_get_int (display_structure, "green_mask", &green_mask);
+ gst_structure_get_int (display_structure, "blue_mask", &blue_mask);
+
+ /* apply the new display mode changes to the previous caps */
+ gst_structure_set (copy_structure,
+ "bpp", G_TYPE_INT, bpp,
+ "depth", G_TYPE_INT, depth,
+ "endianness", G_TYPE_INT, endianness,
+ "red_mask", G_TYPE_INT, red_mask,
+ "green_mask", G_TYPE_INT, green_mask,
+ "blue_mask", G_TYPE_INT, blue_mask, NULL);
+
+ if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ddrawsink),
+ copy_caps)) {
+ buffer_caps = copy_caps;
+ buffercaps_unref = TRUE;
+ /* update buffer size needed to store video frames according to new caps */
+ size = width * height * (bpp / 8);
+
+ /* update our member pixel format */
+ gst_ddrawvideosink_get_format_from_caps (ddrawsink, buffer_caps,
+ &ddrawsink->dd_pixel_format);
+ ddrawsink->must_recreate_offscreen = TRUE;
+
+ GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+ " desired caps %s \n\n new caps %s", gst_caps_to_string (caps),
+ gst_caps_to_string (buffer_caps));
+ } else {
+ GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+ "peer refused caps re-negociation "
+ "and we can't render with the current caps.");
+ ret = GST_FLOW_ERROR;
+ }
+ }
+ gst_caps_unref (display_caps);
+ }
+
+ if (!buffercaps_unref)
+ gst_caps_unref (copy_caps);
+ }
+ }
+
+ /* We haven't found anything, creating a new one */
+ if (!surface) {
+ surface = gst_directdraw_sink_surface_create (ddrawsink, buffer_caps, size);
+ }
+
+ /* Now we should have a surface, set appropriate caps on it */
+ if (surface) {
+ GST_BUFFER_FLAGS (GST_BUFFER (surface)) = 0;
+ gst_buffer_set_caps (GST_BUFFER (surface), buffer_caps);
+ }
+
+ g_mutex_unlock (ddrawsink->pool_lock);
+
+ *buf = GST_BUFFER (surface);
+
+ if (buffercaps_unref)
+ gst_caps_unref (buffer_caps);
+
+ return ret;
+}
+
+static void
+gst_directdraw_sink_draw_borders (GstDirectDrawSink * ddrawsink, RECT dst_rect)
+{
+ RECT win_rect, fill_rect;
+ POINT win_point;
+ HDC hdc;
+
+ g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
+
+ /* Get the target window rect */
+ win_point.x = 0;
+ win_point.y = 0;
+ ClientToScreen (ddrawsink->video_window, &win_point);
+ GetClientRect (ddrawsink->video_window, &win_rect);
+ OffsetRect (&win_rect, win_point.x, win_point.y);
+
+ /* We acquire a drawing context */
+ if (IDirectDrawSurface7_GetDC (ddrawsink->primary_surface, &hdc) == DD_OK) {
+ HBRUSH brush = CreateSolidBrush (RGB (0, 0, 0));
+
+ /* Left border */
+ if (dst_rect.left > win_rect.left) {
+ fill_rect.left = win_rect.left;
+ fill_rect.top = win_rect.top;
+ fill_rect.bottom = win_rect.bottom;
+ fill_rect.right = dst_rect.left;
+ FillRect (hdc, &fill_rect, brush);
+ }
+ /* Right border */
+ if (dst_rect.right < win_rect.right) {
+ fill_rect.top = win_rect.top;
+ fill_rect.left = dst_rect.right;
+ fill_rect.bottom = win_rect.bottom;
+ fill_rect.right = win_rect.right;
+ FillRect (hdc, &fill_rect, brush);
+ }
+ /* Top border */
+ if (dst_rect.top > win_rect.top) {
+ fill_rect.top = win_rect.top;
+ fill_rect.left = win_rect.left;
+ fill_rect.right = win_rect.right;
+ fill_rect.bottom = dst_rect.top;
+ FillRect (hdc, &fill_rect, brush);
+ }
+ /* Bottom border */
+ if (dst_rect.bottom < win_rect.bottom) {
+ fill_rect.top = dst_rect.bottom;
+ fill_rect.left = win_rect.left;
+ fill_rect.right = win_rect.right;
+ fill_rect.bottom = win_rect.bottom;
+ FillRect (hdc, &fill_rect, brush);
+ }
+ DeleteObject (brush);
+ IDirectDrawSurface7_ReleaseDC (ddrawsink->primary_surface, hdc);
+ }
+}
+
+static GstFlowReturn
+gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
+{
+ GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+ HRESULT hRes;
+ RECT destsurf_rect, src_rect;
+ POINT dest_surf_point;
+
+ if (buf) {
+ /* save a reference to the input buffer */
+ gst_buffer_ref (buf);
+ if (ddrawsink->last_buffer != NULL)
+ gst_buffer_unref (ddrawsink->last_buffer);
+ ddrawsink->last_buffer = buf;
+ } else {
+ /* use last buffer */
+ buf = ddrawsink->last_buffer;
+ }
+
+ if (buf == NULL) {
+ GST_ERROR_OBJECT (ddrawsink, "No buffer to render.");
+ return GST_FLOW_ERROR;
+ } else if (!ddrawsink->video_window) {
+ GST_WARNING_OBJECT (ddrawsink, "No video window to render to.");
+ return GST_FLOW_ERROR;
+ }
+
+ /* get the video window position */
+ GST_OBJECT_LOCK (ddrawsink);
+ if (G_UNLIKELY (!ddrawsink->video_window)) {
+ GST_OBJECT_UNLOCK (ddrawsink);
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "gst_directdraw_sink_show_frame our video window disappeared");
+ GST_ELEMENT_ERROR (ddrawsink, RESOURCE, NOT_FOUND,
+ ("Output window was closed"), (NULL));
+ return GST_FLOW_ERROR;
+ }
+ dest_surf_point.x = 0;
+ dest_surf_point.y = 0;
+ ClientToScreen (ddrawsink->video_window, &dest_surf_point);
+ GetClientRect (ddrawsink->video_window, &destsurf_rect);
+ OffsetRect (&destsurf_rect, dest_surf_point.x, dest_surf_point.y);
+
+ if (ddrawsink->keep_aspect_ratio) {
+ /* center image to dest image keeping aspect ratio */
+ src_rect.top = 0;
+ src_rect.left = 0;
+ src_rect.bottom = ddrawsink->video_height;
+ src_rect.right = ddrawsink->video_width;
+ gst_directdraw_sink_center_rect (ddrawsink, src_rect, destsurf_rect,
+ &destsurf_rect);
+ gst_directdraw_sink_draw_borders (ddrawsink, destsurf_rect);
+ }
+ GST_OBJECT_UNLOCK (ddrawsink);
+
+ if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
+ IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+ ddrawsink->offscreen_surface = NULL;
+ }
+
+ /* check for surfaces lost */
+ if (!gst_directdraw_sink_check_primary_surface (ddrawsink) ||
+ !gst_directdraw_sink_check_offscreen_surface (ddrawsink)) {
+ return GST_FLOW_ERROR;
+ }
+
+ if (!GST_IS_DDRAWSURFACE (buf) ||
+ ((GST_IS_DDRAWSURFACE (buf)) && (GST_BUFFER (buf)->malloc_data))) {
+ /* We are receiving a system memory buffer so we will copy
+ to the memory of our offscreen surface and next blit this surface
+ on the primary surface */
+ LPBYTE data = NULL;
+ guint src_pitch, line;
+ DDSURFACEDESC2 surf_desc;
+ DDSURFACEDESC2 *sd;
+
+ ZeroMemory (&surf_desc, sizeof (surf_desc));
+ surf_desc.dwSize = sizeof (surf_desc);
+ sd = &surf_desc;
+
+ /* Lock the surface */
+ hRes =
+ IDirectDrawSurface7_Lock (ddrawsink->offscreen_surface, NULL,
+ (DDSURFACEDESC *) sd, DDLOCK_WAIT, NULL);
+ if (hRes != DD_OK) {
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "gst_directdraw_sink_show_frame failed locking surface %s",
+ DDErrorString (hRes));
+
+ if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK)
+ return GST_FLOW_OK;
+ else
+ return GST_FLOW_ERROR;
+ }
+
+ /* Write each line respecting the destination surface pitch */
+ data = surf_desc.lpSurface;
+ src_pitch = GST_BUFFER_SIZE (buf) / ddrawsink->video_height;
+ for (line = 0; line < surf_desc.dwHeight; line++) {
+ memcpy (data, GST_BUFFER_DATA (buf) + (line * src_pitch), src_pitch);
+ data += surf_desc.lPitch;
+ }
+
+ /* Unlock the surface */
+ hRes = IDirectDrawSurface7_Unlock (ddrawsink->offscreen_surface, NULL);
+ if (hRes != DD_OK) {
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "gst_directdraw_sink_show_frame failed unlocking surface %s",
+ DDErrorString (hRes));
+ return GST_FLOW_ERROR;
+ }
+
+ /* blit to primary surface ( Blt will scale the video the dest rect surface
+ * if needed */
+ hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
+ ddrawsink->offscreen_surface, NULL, DDBLT_WAIT, NULL);
+ if (hRes != DD_OK) /* FIXME: Is it really safe to continue past here ? */
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "IDirectDrawSurface7_Blt (object's offscreen surface) " "returned %s",
+ DDErrorString (hRes));
+
+ } else {
+ /* We are receiving a directdraw surface (previously returned by our buffer
+ * pool so we will simply blit it on the primary surface */
+ GstDDrawSurface *surface = NULL;
+
+ surface = GST_DDRAWSURFACE (buf);
+
+ /* Unlocking surface before blit */
+ IDirectDrawSurface7_Unlock (surface->surface, NULL);
+ surface->locked = FALSE;
+
+ /* blit to our primary surface */
+ hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
+ surface->surface, NULL, DDBLT_WAIT, NULL);
+ if (hRes != DD_OK) /* FIXME: Is it really safe to continue past here ? */
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
+ "returned %s", DDErrorString (hRes));
+ }
+
+ return GST_FLOW_OK;
+}
+
+static void
+gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
+ GstClockTime * start, GstClockTime * end)
+{
+ GstDirectDrawSink *ddrawsink;
+
+ ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+ *start = GST_BUFFER_TIMESTAMP (buf);
+ if (GST_BUFFER_DURATION_IS_VALID (buf)) {
+ *end = *start + GST_BUFFER_DURATION (buf);
+ } else {
+ if (ddrawsink->fps_n > 0) {
+ *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
+ }
+ }
+ }
+}
+
+/* Utility functions */
+
+/* this function fill a DDPIXELFORMAT using Gstreamer caps */
+static gboolean
+gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
+ GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
+{
+ GstStructure *structure = NULL;
+ gboolean ret = TRUE;
+
+ /* check params */
+ g_return_val_if_fail (pPixelFormat, FALSE);
+ g_return_val_if_fail (caps, FALSE);
+
+ /* init structure */
+ memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
+ pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
+
+ if (!(structure = gst_caps_get_structure (caps, 0))) {
+ GST_CAT_ERROR_OBJECT (directdrawsink_debug, ddrawsink,
+ "can't get structure pointer from caps");
+ return FALSE;
+ }
+
+ if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
+ gint depth, bitcount, bitmask, endianness;
+
+ pPixelFormat->dwFlags = DDPF_RGB;
+ ret &= gst_structure_get_int (structure, "bpp", &bitcount);
+ pPixelFormat->dwRGBBitCount = bitcount;
+ ret &= gst_structure_get_int (structure, "depth", &depth);
+ ret &= gst_structure_get_int (structure, "red_mask", &bitmask);
+ pPixelFormat->dwRBitMask = bitmask;
+ ret &= gst_structure_get_int (structure, "green_mask", &bitmask);
+ pPixelFormat->dwGBitMask = bitmask;
+ ret &= gst_structure_get_int (structure, "blue_mask", &bitmask);
+ pPixelFormat->dwBBitMask = bitmask;
+
+ gst_structure_get_int (structure, "endianness", &endianness);
+ if (endianness == G_BIG_ENDIAN) {
+ endianness = G_LITTLE_ENDIAN;
+ pPixelFormat->dwRBitMask = GUINT32_TO_BE (pPixelFormat->dwRBitMask);
+ pPixelFormat->dwGBitMask = GUINT32_TO_BE (pPixelFormat->dwGBitMask);
+ pPixelFormat->dwBBitMask = GUINT32_TO_BE (pPixelFormat->dwBBitMask);
+ }
+ } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
+ guint32 fourcc;
+
+ pPixelFormat->dwFlags = DDPF_FOURCC;
+ ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
+ pPixelFormat->dwFourCC = fourcc;
+ } else {
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "unknown caps name received %" GST_PTR_FORMAT, caps);
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+/* This function centers the RECT of source surface to
+a dest surface and set the result RECT into result */
+static void
+gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
+ RECT dst, RECT * result)
+{
+ gdouble src_ratio, dst_ratio;
+ long src_width = src.right;
+ long src_height = src.bottom;
+ long dst_width = dst.right - dst.left;
+ long dst_heigth = dst.bottom - dst.top;
+ long result_width = 0, result_height = 0;
+
+ g_return_if_fail (result != NULL);
+
+ src_ratio = (gdouble) src_width / src_height;
+ dst_ratio = (gdouble) dst_width / dst_heigth;
+
+ if (src_ratio > dst_ratio) {
+ /* new height */
+ result_height = (long) (dst_width / src_ratio);
+
+ result->left = dst.left;
+ result->right = dst.right;
+ result->top = dst.top + (dst_heigth - result_height) / 2;
+ result->bottom = result->top + result_height;
+
+ } else if (src_ratio < dst_ratio) {
+ /* new width */
+ result_width = (long) (dst_heigth * src_ratio);
+
+ result->top = dst.top;
+ result->bottom = dst.bottom;
+ result->left = dst.left + (dst_width - result_width) / 2;
+ result->right = result->left + result_width;
+
+ } else {
+ /* same ratio */
+ memcpy (result, &dst, sizeof (RECT));
+ }
+
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "source is %ldx%ld dest is %ldx%ld, result is %ldx%ld with x,y %ldx%ld",
+ src_width, src_height, dst_width, dst_heigth,
+ result->right - result->left, result->bottom - result->top, result->left,
+ result->right);
+}
+
+/**
+ * Get DirectDraw error message.
+ * @hr: HRESULT code
+ * Returns: Text representation of the error.
+ */
+char *
+DDErrorString (HRESULT hr)
+{
+ switch (hr) {
+ case DDERR_ALREADYINITIALIZED:
+ return "DDERR_ALREADYINITIALIZED";
+ case DDERR_CANNOTATTACHSURFACE:
+ return "DDERR_CANNOTATTACHSURFACE";
+ case DDERR_CANNOTDETACHSURFACE:
+ return "DDERR_CANNOTDETACHSURFACE";
+ case DDERR_CURRENTLYNOTAVAIL:
+ return "DDERR_CURRENTLYNOTAVAIL";
+ case DDERR_EXCEPTION:
+ return "DDERR_EXCEPTION";
+ case DDERR_GENERIC:
+ return "DDERR_GENERIC";
+ case DDERR_HEIGHTALIGN:
+ return "DDERR_HEIGHTALIGN";
+ case DDERR_INCOMPATIBLEPRIMARY:
+ return "DDERR_INCOMPATIBLEPRIMARY";
+ case DDERR_INVALIDCAPS:
+ return "DDERR_INVALIDCAPS";
+ case DDERR_INVALIDCLIPLIST:
+ return "DDERR_INVALIDCLIPLIST";
+ case DDERR_INVALIDMODE:
+ return "DDERR_INVALIDMODE";
+ case DDERR_INVALIDOBJECT:
+ return "DDERR_INVALIDOBJECT";
+ case DDERR_INVALIDPARAMS:
+ return "DDERR_INVALIDPARAMS";
+ case DDERR_INVALIDPIXELFORMAT:
+ return "DDERR_INVALIDPIXELFORMAT";
+ case DDERR_INVALIDRECT:
+ return "DDERR_INVALIDRECT";
+ case DDERR_LOCKEDSURFACES:
+ return "DDERR_LOCKEDSURFACES";
+ case DDERR_NO3D:
+ return "DDERR_NO3D";
+ case DDERR_NOALPHAHW:
+ return "DDERR_NOALPHAHW";
+ case DDERR_NOCLIPLIST:
+ return "DDERR_NOCLIPLIST";
+ case DDERR_NOCOLORCONVHW:
+ return "DDERR_NOCOLORCONVHW";
+ case DDERR_NOCOOPERATIVELEVELSET:
+ return "DDERR_NOCOOPERATIVELEVELSET";
+ case DDERR_NOCOLORKEY:
+ return "DDERR_NOCOLORKEY";
+ case DDERR_NOCOLORKEYHW:
+ return "DDERR_NOCOLORKEYHW";
+ case DDERR_NODIRECTDRAWSUPPORT:
+ return "DDERR_NODIRECTDRAWSUPPORT";
+ case DDERR_NOEXCLUSIVEMODE:
+ return "DDERR_NOEXCLUSIVEMODE";
+ case DDERR_NOFLIPHW:
+ return "DDERR_NOFLIPHW";
+ case DDERR_NOGDI:
+ return "DDERR_NOGDI";
+ case DDERR_NOMIRRORHW:
+ return "DDERR_NOMIRRORHW";
+ case DDERR_NOTFOUND:
+ return "DDERR_NOTFOUND";
+ case DDERR_NOOVERLAYHW:
+ return "DDERR_NOOVERLAYHW";
+ case DDERR_NORASTEROPHW:
+ return "DDERR_NORASTEROPHW";
+ case DDERR_NOROTATIONHW:
+ return "DDERR_NOROTATIONHW";
+ case DDERR_NOSTRETCHHW:
+ return "DDERR_NOSTRETCHHW";
+ case DDERR_NOT4BITCOLOR:
+ return "DDERR_NOT4BITCOLOR";
+ case DDERR_NOT4BITCOLORINDEX:
+ return "DDERR_NOT4BITCOLORINDEX";
+ case DDERR_NOT8BITCOLOR:
+ return "DDERR_NOT8BITCOLOR";
+ case DDERR_NOTEXTUREHW:
+ return "DDERR_NOTEXTUREHW";
+ case DDERR_NOVSYNCHW:
+ return "DDERR_NOVSYNCHW";
+ case DDERR_NOZBUFFERHW:
+ return "DDERR_NOZBUFFERHW";
+ case DDERR_NOZOVERLAYHW:
+ return "DDERR_NOZOVERLAYHW";
+ case DDERR_OUTOFCAPS:
+ return "DDERR_OUTOFCAPS";
+ case DDERR_OUTOFMEMORY:
+ return "DDERR_OUTOFMEMORY";
+ case DDERR_OUTOFVIDEOMEMORY:
+ return "DDERR_OUTOFVIDEOMEMORY";
+ case DDERR_OVERLAYCANTCLIP:
+ return "DDERR_OVERLAYCANTCLIP";
+ case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
+ return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE";
+ case DDERR_PALETTEBUSY:
+ return "DDERR_PALETTEBUSY";
+ case DDERR_COLORKEYNOTSET:
+ return "DDERR_COLORKEYNOTSET";
+ case DDERR_SURFACEALREADYATTACHED:
+ return "DDERR_SURFACEALREADYATTACHED";
+ case DDERR_SURFACEALREADYDEPENDENT:
+ return "DDERR_SURFACEALREADYDEPENDENT";
+ case DDERR_SURFACEBUSY:
+ return "DDERR_SURFACEBUSY";
+ case DDERR_CANTLOCKSURFACE:
+ return "DDERR_CANTLOCKSURFACE";
+ case DDERR_SURFACEISOBSCURED:
+ return "DDERR_SURFACEISOBSCURED";
+ case DDERR_SURFACELOST:
+ return "DDERR_SURFACELOST";
+ case DDERR_SURFACENOTATTACHED:
+ return "DDERR_SURFACENOTATTACHED";
+ case DDERR_TOOBIGHEIGHT:
+ return "DDERR_TOOBIGHEIGHT";
+ case DDERR_TOOBIGSIZE:
+ return "DDERR_TOOBIGSIZE";
+ case DDERR_TOOBIGWIDTH:
+ return "DDERR_TOOBIGWIDTH";
+ case DDERR_UNSUPPORTED:
+ return "DDERR_UNSUPPORTED";
+ case DDERR_UNSUPPORTEDFORMAT:
+ return "DDERR_UNSUPPORTEDFORMAT";
+ case DDERR_UNSUPPORTEDMASK:
+ return "DDERR_UNSUPPORTEDMASK";
+ case DDERR_VERTICALBLANKINPROGRESS:
+ return "DDERR_VERTICALBLANKINPROGRESS";
+ case DDERR_WASSTILLDRAWING:
+ return "DDERR_WASSTILLDRAWING";
+ case DDERR_XALIGN:
+ return "DDERR_XALIGN";
+ case DDERR_INVALIDDIRECTDRAWGUID:
+ return "DDERR_INVALIDDIRECTDRAWGUID";
+ case DDERR_DIRECTDRAWALREADYCREATED:
+ return "DDERR_DIRECTDRAWALREADYCREATED";
+ case DDERR_NODIRECTDRAWHW:
+ return "DDERR_NODIRECTDRAWHW";
+ case DDERR_PRIMARYSURFACEALREADYEXISTS:
+ return "DDERR_PRIMARYSURFACEALREADYEXISTS";
+ case DDERR_NOEMULATION:
+ return "DDERR_NOEMULATION";
+ case DDERR_REGIONTOOSMALL:
+ return "DDERR_REGIONTOOSMALL";
+ case DDERR_CLIPPERISUSINGHWND:
+ return "DDERR_CLIPPERISUSINGHWND";
+ case DDERR_NOCLIPPERATTACHED:
+ return "DDERR_NOCLIPPERATTACHED";
+ case DDERR_NOHWND:
+ return "DDERR_NOHWND";
+ case DDERR_HWNDSUBCLASSED:
+ return "DDERR_HWNDSUBCLASSED";
+ case DDERR_HWNDALREADYSET:
+ return "DDERR_HWNDALREADYSET";
+ case DDERR_NOPALETTEATTACHED:
+ return "DDERR_NOPALETTEATTACHED";
+ case DDERR_NOPALETTEHW:
+ return "DDERR_NOPALETTEHW";
+ case DDERR_BLTFASTCANTCLIP:
+ return "DDERR_BLTFASTCANTCLIP";
+ case DDERR_NOBLTHW:
+ return "DDERR_NOBLTHW";
+ case DDERR_NODDROPSHW:
+ return "DDERR_NODDROPSHW";
+ case DDERR_OVERLAYNOTVISIBLE:
+ return "DDERR_OVERLAYNOTVISIBLE";
+ case DDERR_NOOVERLAYDEST:
+ return "DDERR_NOOVERLAYDEST";
+ case DDERR_INVALIDPOSITION:
+ return "DDERR_INVALIDPOSITION";
+ case DDERR_NOTAOVERLAYSURFACE:
+ return "DDERR_NOTAOVERLAYSURFACE";
+ case DDERR_EXCLUSIVEMODEALREADYSET:
+ return "DDERR_EXCLUSIVEMODEALREADYSET";
+ case DDERR_NOTFLIPPABLE:
+ return "DDERR_NOTFLIPPABLE";
+ case DDERR_CANTDUPLICATE:
+ return "DDERR_CANTDUPLICATE";
+ case DDERR_NOTLOCKED:
+ return "DDERR_NOTLOCKED";
+ case DDERR_CANTCREATEDC:
+ return "DDERR_CANTCREATEDC";
+ case DDERR_NODC:
+ return "DDERR_NODC";
+ case DDERR_WRONGMODE:
+ return "DDERR_WRONGMODE";
+ case DDERR_IMPLICITLYCREATED:
+ return "DDERR_IMPLICITLYCREATED";
+ case DDERR_NOTPALETTIZED:
+ return "DDERR_NOTPALETTIZED";
+ case DDERR_UNSUPPORTEDMODE:
+ return "DDERR_UNSUPPORTEDMODE";
+ case DDERR_NOMIPMAPHW:
+ return "DDERR_NOMIPMAPHW";
+ case DDERR_INVALIDSURFACETYPE:
+ return "DDERR_INVALIDSURFACETYPE";
+ case DDERR_DCALREADYCREATED:
+ return "DDERR_DCALREADYCREATED";
+ case DDERR_CANTPAGELOCK:
+ return "DDERR_CANTPAGELOCK";
+ case DDERR_CANTPAGEUNLOCK:
+ return "DDERR_CANTPAGEUNLOCK";
+ case DDERR_NOTPAGELOCKED:
+ return "DDERR_NOTPAGELOCKED";
+ case DDERR_NOTINITIALIZED:
+ return "DDERR_NOTINITIALIZED";
+ }
+ return "Unknown Error";
+}
+
+static gboolean
+gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
+{
+ gboolean bRet = TRUE;
+ HRESULT hRes;
+
+ /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first
+ * parameter to force Directdraw to use the hardware emulation layer */
+ hRes = DirectDrawCreateEx ( /*DDCREATE_EMULATIONONLY */ 0,
+ (void **) &ddrawsink->ddraw_object, &IID_IDirectDraw7, NULL);
+ if (hRes != DD_OK || ddrawsink->ddraw_object == NULL) {
+ GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
+ ("Failed to create the DirectDraw object error=%s",
+ DDErrorString (hRes)), (NULL));
+ return FALSE;
+ }
+
+ /* set cooperative level */
+ hRes = IDirectDraw7_SetCooperativeLevel (ddrawsink->ddraw_object,
+ NULL, DDSCL_NORMAL);
+ if (hRes != DD_OK) {
+ GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
+ ("Failed to set the set the cooperative level error=%s",
+ DDErrorString (hRes)), (NULL));
+ return FALSE;
+ }
+
+ /* setup the clipper object */
+ hRes = IDirectDraw7_CreateClipper (ddrawsink->ddraw_object, 0,
+ &ddrawsink->clipper, NULL);
+
+ if (hRes == DD_OK && ddrawsink->video_window)
+ IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
+
+ /* create our primary surface */
+ if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
+ return FALSE;
+
+ /* directdraw objects are setup */
+ ddrawsink->setup = TRUE;
+
+ return bRet;
+}
+
+long FAR PASCAL
+WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_ERASEBKGND:
+ return TRUE;
+ case WM_CLOSE:
+ DestroyWindow (hWnd);
+ case WM_DESTROY:
+ PostQuitMessage (0);
+ return 0;
+ }
+
+ return DefWindowProc (hWnd, message, wParam, lParam);
+}
+
+static gpointer
+gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
+{
+ WNDCLASS WndClass;
+ MSG msg;
+
+ memset (&WndClass, 0, sizeof (WNDCLASS));
+ WndClass.style = CS_HREDRAW | CS_VREDRAW;
+ WndClass.hInstance = GetModuleHandle (NULL);
+ WndClass.lpszClassName = "GStreamer-DirectDraw";
+ WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
+ WndClass.cbClsExtra = 0;
+ WndClass.cbWndExtra = 0;
+ WndClass.lpfnWndProc = WndProc;
+ WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ RegisterClass (&WndClass);
+
+ ddrawsink->video_window = CreateWindowEx (0, "GStreamer-DirectDraw",
+ "GStreamer-DirectDraw sink default window",
+ WS_OVERLAPPEDWINDOW | WS_SIZEBOX, 0, 0, 640, 480, NULL, NULL,
+ WndClass.hInstance, NULL);
+ if (ddrawsink->video_window == NULL)
+ return NULL;
+
+ /* Set the clipper on that window */
+ IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
+
+ /* signal application we created a window */
+ gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ddrawsink),
+ (gulong) ddrawsink->video_window);
+
+ ReleaseSemaphore (ddrawsink->window_created_signal, 1, NULL);
+
+ /* start message loop processing our default window messages */
+ while (GetMessage (&msg, NULL, 0, 0) != FALSE) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+ GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+ "our window received WM_QUIT or error.");
+ /* The window could have changed, if it is not ours anymore we don't
+ * overwrite the current video window with NULL */
+ if (ddrawsink->our_video_window) {
+ GST_OBJECT_LOCK (ddrawsink);
+ ddrawsink->video_window = NULL;
+ GST_OBJECT_UNLOCK (ddrawsink);
+ }
+
+ return NULL;
+}
+
+static gboolean
+gst_directdraw_sink_create_default_window (GstDirectDrawSink * ddrawsink)
+{
+ ddrawsink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);
+ if (ddrawsink->window_created_signal == NULL)
+ return FALSE;
+
+ ddrawsink->window_thread = g_thread_create (
+ (GThreadFunc) gst_directdraw_sink_window_thread, ddrawsink, TRUE, NULL);
+
+ if (ddrawsink->window_thread == NULL)
+ goto failed;
+
+ /* wait maximum 10 seconds for windows creating */
+ if (WaitForSingleObject (ddrawsink->window_created_signal,
+ 10000) != WAIT_OBJECT_0)
+ goto failed;
+
+ CloseHandle (ddrawsink->window_created_signal);
+ return TRUE;
+
+failed:
+ CloseHandle (ddrawsink->window_created_signal);
+ GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
+ ("Error creating our default window"), (NULL));
+
+ return FALSE;
+}
+
+static gboolean
+gst_directdraw_sink_check_primary_surface (GstDirectDrawSink * ddrawsink)
+{
+ HRESULT hres;
+ DDSURFACEDESC2 dd_surface_desc;
+ DDSURFACEDESC2 *sd;
+
+ /* if our primary surface already exist, check if it's not lost */
+ if (ddrawsink->primary_surface) {
+ if (IDirectDrawSurface7_IsLost (ddrawsink->primary_surface) == DD_OK) {
+ /* no problem with our primary surface */
+ return TRUE;
+ } else {
+ /* our primary surface was lost, try to restore it */
+ if (IDirectDrawSurface7_Restore (ddrawsink->primary_surface) == DD_OK) {
+ /* restore is done */
+ GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+ "Our primary surface" " was restored after lost");
+ return TRUE;
+ } else {
+ /* failed to restore our primary surface,
+ * probably because the display mode was changed.
+ * Release this surface and recreate a new one.
+ */
+ GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+ "Our primary surface"
+ " was lost and display mode has changed. Destroy and recreate our surface.");
+ IDirectDrawSurface7_Release (ddrawsink->primary_surface);
+ ddrawsink->primary_surface = NULL;
+
+ /* also release offscreen surface */
+ IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+ ddrawsink->offscreen_surface = NULL;
+ }
+ }
+ }
+
+ /* create our primary surface */
+ memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
+ dd_surface_desc.dwSize = sizeof (dd_surface_desc);
+ dd_surface_desc.dwFlags = DDSD_CAPS;
+ dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+ sd = &dd_surface_desc;
+ hres =
+ IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
+ &ddrawsink->primary_surface, NULL);
+ if (hres != DD_OK) {
+ GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
+ ("Failed to create our primary surface error=%s", DDErrorString (hres)),
+ (NULL));
+ return FALSE;
+ }
+
+ /* attach our clipper object to the new primary surface */
+ if (ddrawsink->clipper) {
+ hres = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
+ ddrawsink->clipper);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink * ddrawsink)
+{
+ DDSURFACEDESC2 dd_surface_desc;
+ DDSURFACEDESC2 *sd;
+ HRESULT hres;
+
+ /* if our offscreen surface already exist, check if it's not lost */
+ if (ddrawsink->offscreen_surface) {
+ if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK) {
+ /* no problem with our offscreen surface */
+ return TRUE;
+ } else {
+ /* our offscreen surface was lost, try to restore it */
+ if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
+ /* restore is done */
+ GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+ "Our offscreen surface" " was restored after lost");
+ return TRUE;
+ } else {
+ /* failed to restore our offscreen surface,
+ * probably because the display mode was changed.
+ * Release this surface and recreate a new one.
+ */
+ GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+ "Our offscreen surface"
+ " was lost and display mode has changed. Destroy and recreate our surface.");
+ IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+ ddrawsink->offscreen_surface = NULL;
+ }
+ }
+ }
+
+ memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
+ dd_surface_desc.dwSize = sizeof (dd_surface_desc);
+ dd_surface_desc.dwFlags =
+ DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
+ dd_surface_desc.dwHeight = ddrawsink->video_height;
+ dd_surface_desc.dwWidth = ddrawsink->video_width;
+ memcpy (&(dd_surface_desc.ddpfPixelFormat), &ddrawsink->dd_pixel_format,
+ sizeof (DDPIXELFORMAT));
+
+ dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+ sd = &dd_surface_desc;
+ hres =
+ IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
+ &ddrawsink->offscreen_surface, NULL);
+ if (hres != DD_OK) {
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "create_ddraw_surface:CreateSurface (offscreen surface for buffer_pool) failed %s",
+ DDErrorString (hres));
+ return FALSE;
+ }
+
+ ddrawsink->must_recreate_offscreen = FALSE;
+
+ return TRUE;
+}
+
+static int
+gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat)
+{
+ gint order = 0, binary;
+
+ binary =
+ lpddpfPixelFormat->dwRBitMask | lpddpfPixelFormat->
+ dwGBitMask | lpddpfPixelFormat->dwBBitMask | lpddpfPixelFormat->
+ dwRGBAlphaBitMask;
+ while (binary != 0) {
+ if ((binary % 2) == 1)
+ order++;
+ binary = binary >> 1;
+ }
+ return order;
+}
+
+HRESULT WINAPI
+EnumModesCallback2 (LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
+{
+ GstDirectDrawSink *ddrawsink = (GstDirectDrawSink *) lpContext;
+ GstCaps *format_caps = NULL;
+ LPDDSURFACEDESC2 sd;
+
+ if (!ddrawsink || !lpDDSurfaceDesc)
+ return DDENUMRET_CANCEL;
+
+ sd = (LPDDSURFACEDESC2) lpDDSurfaceDesc;
+ if ((sd->dwFlags & DDSD_PIXELFORMAT) != DDSD_PIXELFORMAT) {
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "Display mode found with DDSD_PIXELFORMAT not set");
+ return DDENUMRET_OK;
+ }
+
+ if ((sd->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
+ return DDENUMRET_OK;
+
+ format_caps = gst_directdraw_sink_create_caps_from_surfacedesc (sd);
+
+ if (format_caps) {
+ gst_caps_append (ddrawsink->caps, format_caps);
+ }
+
+ return DDENUMRET_OK;
+}
+
+static GstCaps *
+gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc)
+{
+ GstCaps *caps = NULL;
+ gint endianness = G_LITTLE_ENDIAN;
+ gint depth;
+
+ if ((desc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
+ return NULL;
+
+ depth = gst_directdraw_sink_get_depth (&desc->ddpfPixelFormat);
+
+ if (desc->ddpfPixelFormat.dwRGBBitCount == 24 ||
+ desc->ddpfPixelFormat.dwRGBBitCount == 32) {
+ /* ffmpegcolorspace handles 24/32 bpp RGB as big-endian. */
+ endianness = G_BIG_ENDIAN;
+ desc->ddpfPixelFormat.dwRBitMask =
+ GUINT32_TO_BE (desc->ddpfPixelFormat.dwRBitMask);
+ desc->ddpfPixelFormat.dwGBitMask =
+ GUINT32_TO_BE (desc->ddpfPixelFormat.dwGBitMask);
+ desc->ddpfPixelFormat.dwBBitMask =
+ GUINT32_TO_BE (desc->ddpfPixelFormat.dwBBitMask);
+ if (desc->ddpfPixelFormat.dwRGBBitCount == 24) {
+ desc->ddpfPixelFormat.dwRBitMask >>= 8;
+ desc->ddpfPixelFormat.dwGBitMask >>= 8;
+ desc->ddpfPixelFormat.dwBBitMask >>= 8;
+ }
+ }
+
+ caps = gst_caps_new_simple ("video/x-raw-rgb",
+ "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+ "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+ "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
+ "bpp", G_TYPE_INT, desc->ddpfPixelFormat.dwRGBBitCount,
+ "depth", G_TYPE_INT, depth,
+ "endianness", G_TYPE_INT, endianness,
+ "red_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwRBitMask,
+ "green_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwGBitMask,
+ "blue_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwBBitMask, NULL);
+
+ return caps;
+}
+
+static GstCaps *
+gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
+{
+ HRESULT hRes = S_OK;
+ DDCAPS ddcaps_hardware;
+ DDCAPS ddcaps_emulation;
+ GstCaps *format_caps = NULL;
+
+ ddrawsink->caps = gst_caps_new_empty ();
+ if (!ddrawsink->caps)
+ return FALSE;
+
+ /* get hardware caps */
+ ddcaps_hardware.dwSize = sizeof (DDCAPS);
+ ddcaps_emulation.dwSize = sizeof (DDCAPS);
+ IDirectDraw7_GetCaps (ddrawsink->ddraw_object, &ddcaps_hardware,
+ &ddcaps_emulation);
+
+ /* we don't test for DDCAPS_BLTSTRETCH on the hardware as the directdraw
+ * emulation layer can do it */
+ if (!(ddcaps_hardware.dwCaps & DDCAPS_BLTFOURCC)) {
+ DDSURFACEDESC2 surface_desc;
+ DDSURFACEDESC2 *sd;
+
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "hardware doesn't support blit from one colorspace to another one. "
+ "so we will create a caps with only the current display mode");
+
+ /* save blit caps */
+ ddrawsink->can_blit_between_colorspace = FALSE;
+
+ surface_desc.dwSize = sizeof (surface_desc);
+ sd = &surface_desc;
+ hRes =
+ IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
+ (DDSURFACEDESC *) sd);
+ if (hRes != DD_OK) {
+ GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
+ ("Error getting the current display mode error=%s",
+ DDErrorString (hRes)), (NULL));
+ return NULL;
+ }
+
+ format_caps =
+ gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
+ if (format_caps) {
+ gst_caps_append (ddrawsink->caps, format_caps);
+ }
+
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
+ gst_caps_to_string (ddrawsink->caps));
+ return ddrawsink->caps;
+ }
+
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "the hardware can blit from one colorspace to another, "
+ "then enumerate the colorspace supported by the hardware");
+
+ /* save blit caps */
+ ddrawsink->can_blit_between_colorspace = TRUE;
+
+ /* enumerate display modes exposed by directdraw object
+ to know supported RGB modes */
+ hRes =
+ IDirectDraw7_EnumDisplayModes (ddrawsink->ddraw_object,
+ DDEDM_REFRESHRATES, NULL, ddrawsink, EnumModesCallback2);
+ if (hRes != DD_OK) {
+ GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
+ ("Error enumerating display modes error=%s", DDErrorString (hRes)),
+ (NULL));
+
+ return NULL;
+ }
+
+ if (gst_caps_is_empty (ddrawsink->caps)) {
+ gst_caps_unref (ddrawsink->caps);
+ ddrawsink->caps = NULL;
+ GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
+ ("No supported caps found."), (NULL));
+ return NULL;
+ }
+
+ /*GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
+ * gst_caps_to_string (ddrawsink->caps)); */
+
+ return ddrawsink->caps;
+}
+
+/* Creates miniobject and our internal surface */
+static GstDDrawSurface *
+gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
+ GstCaps * caps, size_t size)
+{
+ GstDDrawSurface *surface = NULL;
+ GstStructure *structure = NULL;
+ gint pitch;
+
+#if 0
+ HRESULT hRes;
+#endif
+ DDSURFACEDESC2 surf_desc, surf_lock_desc;
+
+ g_return_val_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink), NULL);
+
+ /*init structures */
+ memset (&surf_desc, 0, sizeof (surf_desc));
+ memset (&surf_lock_desc, 0, sizeof (surf_desc));
+ surf_desc.dwSize = sizeof (surf_desc);
+ surf_lock_desc.dwSize = sizeof (surf_lock_desc);
+
+ /*create miniobject and initialize it */
+ surface = (GstDDrawSurface *) gst_mini_object_new (GST_TYPE_DDRAWSURFACE);
+ surface->locked = FALSE;
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (!gst_structure_get_int (structure, "width", &surface->width) ||
+ !gst_structure_get_int (structure, "height", &surface->height)) {
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
+ }
+
+ pitch = GST_ROUND_UP_8 (size / surface->height);
+ if (!gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
+ &surface->dd_pixel_format)) {
+ GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+ "failed getting pixel format from caps %" GST_PTR_FORMAT, caps);
+ }
+
+ /* disable return of directdraw surface to buffer alloc because actually I
+ * have no solution to handle display mode changes. The problem is that when
+ * the display mode is changed surface's memory is freed then the upstream
+ * filter would crash trying to write to this memory. Directdraw has a system
+ * lock (DDLOCK_NOSYSLOCK to disable it) to prevent display mode changes
+ * when a surface memory is locked but we need to disable this lock to return
+ * multiple buffers (surfaces) and do not lock directdraw API calls.
+ */
+#if 0
+/* if (ddrawsink->ddraw_object) {*/
+ /* Creating an internal surface which will be used as GstBuffer, we used
+ the detected pixel format and video dimensions */
+
+ surf_desc.ddsCaps.dwCaps =
+ DDSCAPS_OFFSCREENPLAIN /* | DDSCAPS_SYSTEMMEMORY */ ;
+ surf_desc.dwFlags =
+ DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH;
+ surf_desc.dwHeight = surface->height;
+ surf_desc.dwWidth = surface->width;
+ memcpy (&(surf_desc.ddpfPixelFormat), &surface->dd_pixel_format,
+ sizeof (DDPIXELFORMAT));
+
+ hRes = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &surf_desc,
+ &surface->surface, NULL);
+ if (hRes != DD_OK) {
+ goto surface_pitch_bad;
+ }
+
+ /* Locking the surface to acquire the memory pointer.
+ Use DDLOCK_NOSYSLOCK to disable syslock which can cause a deadlock
+ if directdraw api is used while a buffer is lock */
+lock:
+ hRes = IDirectDrawSurface7_Lock (surface->surface, NULL, &surf_lock_desc,
+ DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
+ if (hRes == DDERR_SURFACELOST) {
+ IDirectDrawSurface7_Restore (surface->surface);
+ goto lock;
+ }
+ surface->locked = TRUE;
+
+ if (surf_lock_desc.lPitch != pitch) {
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "DDraw stride/pitch %ld isn't as expected value %d, let's continue allocating a system memory buffer.",
+ surf_lock_desc.lPitch, pitch);
+
+ /*Unlock the surface as we will change it to use system memory with a GStreamer compatible pitch */
+ hRes = IDirectDrawSurface_Unlock (surface->surface, NULL);
+ goto surface_pitch_bad;
+ }
+ GST_BUFFER_DATA (surface) = surf_lock_desc.lpSurface;
+ GST_BUFFER_SIZE (surface) = surf_lock_desc.lPitch * surface->height;
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "allocating a surface of %d bytes (stride=%ld)\n", size,
+ surf_lock_desc.lPitch);
+
+surface_pitch_bad:
+#else
+ GST_BUFFER (surface)->malloc_data = g_malloc (size);
+ GST_BUFFER_DATA (surface) = GST_BUFFER (surface)->malloc_data;
+ GST_BUFFER_SIZE (surface) = size;
+ surface->surface = NULL;
+ GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+ "allocating a system memory buffer of %d bytes", size);
+
+#endif
+
+ /* Keep a ref to our sink */
+ surface->ddrawsink = gst_object_ref (ddrawsink);
+
+ return surface;
+}
+
+/* We are called from the finalize method of miniobject, the object will be
+ * destroyed so we just have to clean our internal stuff */
+static void
+gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
+ GstDDrawSurface * surface)
+{
+ g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
+
+ /* Release our internal surface */
+ if (surface->surface) {
+ if (surface->locked) {
+ IDirectDrawSurface7_Unlock (surface->surface, NULL);
+ surface->locked = FALSE;
+ }
+ IDirectDrawSurface7_Release (surface->surface);
+ surface->surface = NULL;
+ }
+
+ if (GST_BUFFER (surface)->malloc_data) {
+ g_free (GST_BUFFER (surface)->malloc_data);
+ GST_BUFFER (surface)->malloc_data = NULL;
+ }
+
+ if (!surface->ddrawsink) {
+ goto no_sink;
+ }
+
+ /* Release the ref to our sink */
+ surface->ddrawsink = NULL;
+ gst_object_unref (ddrawsink);
+
+ return;
+
+no_sink:
+ GST_WARNING ("no sink found in surface");
+ return;
+}
+
+static gboolean
+gst_directdraw_sink_surface_check (GstDirectDrawSink * ddrawsink,
+ GstDDrawSurface * surface)
+{
+ if (!surface->surface)
+ return TRUE; /* system memory buffer */
+
+ if (IDirectDrawSurface7_IsLost (surface->surface) == DD_OK) {
+ /* no problem with this surface */
+ return TRUE;
+ } else {
+ /* this surface was lost, try to restore it */
+ if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
+ /* restore is done */
+ GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink, "A surface from our"
+ " bufferpool was restored after lost");
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink * ddrawsink)
+{
+ g_mutex_lock (ddrawsink->pool_lock);
+ while (ddrawsink->buffer_pool) {
+ GstDDrawSurface *surface = ddrawsink->buffer_pool->data;
+
+ ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
+ ddrawsink->buffer_pool);
+ gst_directdraw_sink_surface_destroy (ddrawsink, surface);
+ gst_buffer_unref (GST_BUFFER_CAST (surface));
+ }
+ g_mutex_unlock (ddrawsink->pool_lock);
+}
+
+static void
+gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink)
+{
+ /* Post quit message and wait for our event window thread */
+ if (ddrawsink->video_window && ddrawsink->our_video_window)
+ PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
+
+ if (ddrawsink->window_thread) {
+ g_thread_join (ddrawsink->window_thread);
+ ddrawsink->window_thread = NULL;
+ }
+
+ if (ddrawsink->buffer_pool) {
+ gst_directdraw_sink_bufferpool_clear (ddrawsink);
+ ddrawsink->buffer_pool = NULL;
+ }
+
+ if (ddrawsink->offscreen_surface) {
+ IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+ ddrawsink->offscreen_surface = NULL;
+ }
+
+ if (ddrawsink->clipper) {
+ IDirectDrawClipper_Release (ddrawsink->clipper);
+ ddrawsink->clipper = NULL;
+ }
+
+ if (ddrawsink->primary_surface) {
+ IDirectDrawSurface7_Release (ddrawsink->primary_surface);
+ ddrawsink->primary_surface = NULL;
+ }
+
+ if (ddrawsink->ddraw_object) {
+ IDirectDraw7_Release (ddrawsink->ddraw_object);
+ ddrawsink->ddraw_object = NULL;
+ }
+
+ if (ddrawsink->last_buffer) {
+ gst_buffer_unref (ddrawsink->last_buffer);
+ ddrawsink->last_buffer = NULL;
+ }
+
+ ddrawsink->setup = FALSE;
+}