blob: 99140f21250624e1c75571ef256d617c6efb74fa [file] [log] [blame]
/* 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., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, 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"
#include <gst/video/video.h>
GST_DEBUG_CATEGORY_STATIC (directdrawsink_debug);
#define GST_CAT_DEFAULT directdrawsink_debug
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);
static const char *DDErrorString (HRESULT hr);
static long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam);
/* 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)
{
if (type == GST_TYPE_X_OVERLAY)
return TRUE;
else if (type == GST_TYPE_NAVIGATION)
return TRUE;
return FALSE;
}
static void
gst_directdraw_sink_interface_init (GstImplementsInterfaceClass * klass)
{
klass->supported = gst_directdraw_sink_interface_supported;
}
static void
gst_directdraw_sink_set_window_handle (GstXOverlay * overlay,
guintptr window_handle)
{
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_handle) {
GST_OBJECT_UNLOCK (ddrawsink);
return;
}
if (window_handle) {
HRESULT hres;
RECT rect;
/* 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_handle;
ddrawsink->our_video_window = FALSE;
/* Hook WndProc and user_data */
ddrawsink->previous_user_data = (LONG_PTR) SetWindowLongPtr (
(HWND) window_handle, GWLP_USERDATA, (LONG_PTR) ddrawsink);
ddrawsink->previous_wndproc = (WNDPROC) SetWindowLongPtr (
(HWND) window_handle, GWLP_WNDPROC, (LONG_PTR) WndProc);
if (!ddrawsink->previous_wndproc)
GST_DEBUG_OBJECT (ddrawsink, "Failed to hook previous WndProc");
/* Get initial window size. If it changes, we will track it from the
* WndProc. */
GetClientRect ((HWND) window_handle, &rect);
ddrawsink->out_width = rect.right - rect.left;
ddrawsink->out_height = rect.bottom - rect.top;
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_handle 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_window_handle = gst_directdraw_sink_set_window_handle;
iface->expose = gst_directdraw_sink_expose;
}
static void
gst_directdraw_sink_navigation_send_event (GstNavigation * navigation,
GstStructure * structure)
{
GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (navigation);
GstEvent *event;
GstVideoRectangle src = { 0, };
GstVideoRectangle dst = { 0, };
GstVideoRectangle result;
RECT rect;
gdouble x, y, old_x, old_y, xscale = 1.0, yscale = 1.0;
GstPad *pad = NULL;
src.w = GST_VIDEO_SINK_WIDTH (ddrawsink);
src.h = GST_VIDEO_SINK_HEIGHT (ddrawsink);
GetClientRect ((HWND) ddrawsink->video_window, &rect);
ddrawsink->out_width = rect.right - rect.left;
ddrawsink->out_height = rect.bottom - rect.top;
dst.w = ddrawsink->out_width;
dst.h = ddrawsink->out_height;
event = gst_event_new_navigation (structure);
if (ddrawsink->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;
}
/* We calculate scaling using the original video frames geometry to include
pixel aspect ratio scaling. */
xscale = (gdouble) ddrawsink->video_width / result.w;
yscale = (gdouble) ddrawsink->video_height / result.h;
/* Converting pointer coordinates to the non scaled geometry */
if (gst_structure_get_double (structure, "pointer_x", &old_x)) {
x = old_x;
x = MIN (x, result.x + result.w);
x = MAX (x - result.x, 0);
gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
(gdouble) x * xscale, NULL);
GST_DEBUG_OBJECT (ddrawsink,
"translated navigation event x coordinate from %f to %f", old_x, x);
}
if (gst_structure_get_double (structure, "pointer_y", &old_y)) {
y = old_y;
y = MIN (y, result.y + result.h);
y = MAX (y - result.y, 0);
gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
(gdouble) y * yscale, NULL);
GST_DEBUG_OBJECT (ddrawsink,
"translated navigation event x coordinate from %f to %f", old_y, y);
}
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ddrawsink));
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
gst_pad_send_event (pad, event);
gst_object_unref (pad);
}
}
static void
gst_directdraw_sink_navigation_interface_init (GstNavigationInterface * iface)
{
iface->send_event = gst_directdraw_sink_navigation_send_event;
}
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,
};
static const GInterfaceInfo navigation_info = {
(GInterfaceInitFunc) gst_directdraw_sink_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);
}
/* 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);
}
static 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_static_metadata (element_class,
"Direct Draw Video Sink", "Sink/Video",
"Output to a video card via Direct Draw",
"Sebastien Moutte <sebastien@moutte.net>");
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", TRUE,
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->previous_wndproc = NULL;
ddrawsink->previous_user_data = (LONG_PTR) NULL;
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;
gint par_n, par_d;
structure = gst_caps_get_structure (caps, 0);
if (!structure)
return FALSE;
if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) {
par_n = 1;
par_d = 1;
}
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;
}
GST_VIDEO_SINK_WIDTH (ddrawsink) = ddrawsink->video_width * par_n / par_d;
GST_VIDEO_SINK_HEIGHT (ddrawsink) = ddrawsink->video_height;
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,
GST_VIDEO_SINK_WIDTH (ddrawsink) +
(GetSystemMetrics (SM_CXSIZEFRAME) * 2),
GST_VIDEO_SINK_HEIGHT (ddrawsink) + 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 ((hdc = GetDC (ddrawsink->video_window))) {
HBRUSH brush = CreateSolidBrush (RGB (0, 0, 0));
/* arrange for logical coordinates that match screen coordinates */
SetWindowOrgEx (hdc, win_point.x, win_point.y, NULL);
/* 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);
ReleaseDC (ddrawsink->video_window, 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);
/* Check to see if we have an area to draw to.
* When the window is minimized, it will trigger the
* "IDirectDrawSurface7_Blt (object's offscreen surface)" warning,
* with a msg that the rectangle is invalid */
if (destsurf_rect.right <= destsurf_rect.left ||
destsurf_rect.bottom <= destsurf_rect.top) {
GST_OBJECT_UNLOCK (ddrawsink);
GST_DEBUG_OBJECT (ddrawsink, "invalid rendering window rectangle "
"(%ld, %ld), (%ld, %ld)", destsurf_rect.left, destsurf_rect.top,
destsurf_rect.right, destsurf_rect.bottom);
goto beach;
}
if (ddrawsink->keep_aspect_ratio) {
/* center image to dest image keeping aspect ratio */
src_rect.top = 0;
src_rect.left = 0;
src_rect.bottom = GST_VIDEO_SINK_HEIGHT (ddrawsink);
src_rect.right = GST_VIDEO_SINK_WIDTH (ddrawsink);
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;
if (ddrawsink->video_height) {
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));
}
beach:
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.
*/
static const 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;
}
static LRESULT FAR PASCAL
WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
GstDirectDrawSink *ddrawsink;
LRESULT ret;
ddrawsink = (GstDirectDrawSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
switch (message) {
case WM_CREATE:{
LPCREATESTRUCT crs = (LPCREATESTRUCT) lParam;
/* Nail pointer to the video sink down to this window */
SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) crs->lpCreateParams);
break;
}
case WM_SIZE:
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:{
GstDirectDrawSink *ddrawsink;
ddrawsink = (GstDirectDrawSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
if (G_UNLIKELY (!ddrawsink))
break;
switch (message) {
case WM_SIZE:{
GST_OBJECT_LOCK (ddrawsink);
ddrawsink->out_width = LOWORD (lParam);
ddrawsink->out_height = HIWORD (lParam);
GST_OBJECT_UNLOCK (ddrawsink);
GST_DEBUG_OBJECT (ddrawsink, "Window size is %dx%d", LOWORD (wParam),
HIWORD (wParam));
break;
}
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:{
gunichar2 wcrep[128];
if (GetKeyNameTextW (lParam, 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 (ddrawsink),
"key-press", utfrep);
if (message == WM_CHAR || message == WM_KEYUP)
gst_navigation_send_key_event (GST_NAVIGATION (ddrawsink),
"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:{
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;
}
x = LOWORD (lParam);
y = HIWORD (lParam);
if (button == 0) {
GST_DEBUG_OBJECT (ddrawsink, "Mouse moved to %dx%d", x, y);
} else
GST_DEBUG_OBJECT (ddrawsink, "Mouse button %d pressed at %dx%d",
button, x, y);
if (button < 4)
gst_navigation_send_mouse_event (GST_NAVIGATION (ddrawsink),
action, button, x, y);
break;
}
}
break;
}
case WM_ERASEBKGND:
return TRUE;
case WM_CLOSE:
DestroyWindow (hWnd);
case WM_DESTROY:
PostQuitMessage (0);
return 0;
}
if (ddrawsink && ddrawsink->previous_wndproc) {
/* If there was a previous custom WndProc, call it */
/* Temporarily restore the previous user_data */
if (ddrawsink->previous_user_data)
SetWindowLongPtr (hWnd, GWLP_USERDATA, ddrawsink->previous_user_data);
/* Call previous WndProc */
ret =
CallWindowProc (ddrawsink->previous_wndproc, hWnd, message, wParam,
lParam);
/* Point the user_data back to our ddraw_sink */
SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) ddrawsink);
} else {
/* if there was no previous custom WndProc, call Window's default one */
ret = DefWindowProc (hWnd, message, wParam, lParam);
}
return ret;
}
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, (LPVOID) ddrawsink);
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_window_handle (GST_X_OVERLAY (ddrawsink),
(guintptr) 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;
}
static 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 %" G_GSIZE_FORMAT " 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;
}