2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
20 * The development of this code was made possible due to the involvement
21 * of Pioneers of the Inevitable, the creators of the Songbird Music player
26 * SECTION:element-directdrawsink
28 * DirectdrawSink renders video RGB frames to any win32 window. This element
29 * can receive a window ID from the application through the #XOverlay interface
30 * and will then render video frames in this window.
31 * If no Window ID was provided by the application, the element will create its
32 * own internal window and render into it.
35 * <title>Example pipelines</title>
37 * gst-launch -v videotestsrc ! directdrawsink
38 * ]| a simple pipeline to test the sink
46 #include "gstdirectdrawsink.h"
47 #include <gst/video/video.h>
49 GST_DEBUG_CATEGORY_STATIC (directdrawsink_debug);
50 #define GST_CAT_DEFAULT directdrawsink_debug
52 static void gst_directdraw_sink_init_interfaces (GType type);
54 GST_BOILERPLATE_FULL (GstDirectDrawSink, gst_directdraw_sink, GstVideoSink,
55 GST_TYPE_VIDEO_SINK, gst_directdraw_sink_init_interfaces);
57 static void gst_directdraw_sink_finalize (GObject * object);
58 static void gst_directdraw_sink_set_property (GObject * object,
59 guint prop_id, const GValue * value, GParamSpec * pspec);
60 static void gst_directdraw_sink_get_property (GObject * object,
61 guint prop_id, GValue * value, GParamSpec * pspec);
62 static GstCaps *gst_directdraw_sink_get_caps (GstBaseSink * bsink);
63 static gboolean gst_directdraw_sink_set_caps (GstBaseSink * bsink,
65 static GstStateChangeReturn gst_directdraw_sink_change_state (GstElement *
66 element, GstStateChange transition);
67 static GstFlowReturn gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink,
68 guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
69 static void gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
70 GstClockTime * start, GstClockTime * end);
71 static GstFlowReturn gst_directdraw_sink_show_frame (GstBaseSink * bsink,
75 static gboolean gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink);
76 static gboolean gst_directdraw_sink_create_default_window (GstDirectDrawSink *
78 static gboolean gst_directdraw_sink_check_primary_surface (GstDirectDrawSink *
80 static gboolean gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink *
82 static GstCaps *gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink *
85 * gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc);
86 static void gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink);
87 static void gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink *
89 static int gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat);
90 static gboolean gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink *
91 ddrawsink, GstCaps * caps, DDPIXELFORMAT * pPixelFormat);
92 static void gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink,
93 RECT src, RECT dst, RECT * result);
94 static const char *DDErrorString (HRESULT hr);
95 static long FAR PASCAL WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
97 /* surfaces management functions */
98 static void gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
99 GstDDrawSurface * surface);
100 static GstDDrawSurface *gst_directdraw_sink_surface_create (GstDirectDrawSink *
101 ddrawsink, GstCaps * caps, size_t size);
102 static gboolean gst_directdraw_sink_surface_check (GstDirectDrawSink *
103 ddrawsink, GstDDrawSurface * surface);
105 static GstStaticPadTemplate directdrawsink_sink_factory =
106 GST_STATIC_PAD_TEMPLATE ("sink",
109 GST_STATIC_CAPS ("video/x-raw-rgb, "
110 "framerate = (fraction) [ 0, MAX ], "
111 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
117 PROP_KEEP_ASPECT_RATIO
120 /* XOverlay interface implementation */
122 gst_directdraw_sink_interface_supported (GstImplementsInterface * iface,
125 if (type == GST_TYPE_X_OVERLAY)
127 else if (type == GST_TYPE_NAVIGATION)
133 gst_directdraw_sink_interface_init (GstImplementsInterfaceClass * klass)
135 klass->supported = gst_directdraw_sink_interface_supported;
139 gst_directdraw_sink_set_window_handle (GstXOverlay * overlay,
140 guintptr window_handle)
142 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
144 GST_OBJECT_LOCK (ddrawsink);
145 /* check if we are already using this window id */
146 if (ddrawsink->video_window == (HWND) window_handle) {
147 GST_OBJECT_UNLOCK (ddrawsink);
155 /* If we had an internal window, close it first */
156 if (ddrawsink->video_window && ddrawsink->our_video_window) {
157 /* Trick to let the event thread know that it has to die silently */
158 ddrawsink->our_video_window = FALSE;
159 /* Post quit message and wait for our event window thread */
160 PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
163 ddrawsink->video_window = (HWND) window_handle;
164 ddrawsink->our_video_window = FALSE;
166 /* Hook WndProc and user_data */
167 ddrawsink->previous_user_data = (LONG_PTR) SetWindowLongPtr (
168 (HWND) window_handle, GWLP_USERDATA, (LONG_PTR) ddrawsink);
169 ddrawsink->previous_wndproc = (WNDPROC) SetWindowLongPtr (
170 (HWND) window_handle, GWLP_WNDPROC, (LONG_PTR) WndProc);
171 if (!ddrawsink->previous_wndproc)
172 GST_DEBUG_OBJECT (ddrawsink, "Failed to hook previous WndProc");
174 /* Get initial window size. If it changes, we will track it from the
176 GetClientRect ((HWND) window_handle, &rect);
177 ddrawsink->out_width = rect.right - rect.left;
178 ddrawsink->out_height = rect.bottom - rect.top;
180 if (ddrawsink->setup) {
181 /* update the clipper object with the new window */
182 hres = IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0,
183 ddrawsink->video_window);
186 /* FIXME: Handle the case where window_handle is 0 and we want the sink to
187 * create a new window when playback was already started (after set_caps) */
188 GST_OBJECT_UNLOCK (ddrawsink);
192 gst_directdraw_sink_expose (GstXOverlay * overlay)
194 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
196 gst_directdraw_sink_show_frame (GST_BASE_SINK (ddrawsink), NULL);
200 gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
202 iface->set_window_handle = gst_directdraw_sink_set_window_handle;
203 iface->expose = gst_directdraw_sink_expose;
207 gst_directdraw_sink_navigation_send_event (GstNavigation * navigation,
208 GstStructure * structure)
210 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (navigation);
212 GstVideoRectangle src, dst, result;
213 gdouble x, y, old_x, old_y, xscale = 1.0, yscale=1.0;
216 src.w = GST_VIDEO_SINK_WIDTH (ddrawsink);
217 src.h = GST_VIDEO_SINK_HEIGHT (ddrawsink);
218 dst.w = ddrawsink->out_width;
219 dst.h = ddrawsink->out_height;
221 event = gst_event_new_navigation (structure);
223 if (ddrawsink->keep_aspect_ratio) {
224 gst_video_sink_center_rect (src, dst, &result, TRUE);
232 /* We calculate scaling using the original video frames geometry to include
233 pixel aspect ratio scaling. */
234 xscale = (gdouble) ddrawsink->video_width / result.w;
235 yscale = (gdouble) ddrawsink->video_height / result.h;
237 /* Converting pointer coordinates to the non scaled geometry */
238 if (gst_structure_get_double (structure, "pointer_x", &old_x)) {
240 x = MIN (x, result.x + result.w);
241 x = MAX (x - result.x, 0);
242 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
243 (gdouble) x * xscale, NULL);
244 GST_DEBUG_OBJECT (ddrawsink,
245 "translated navigation event x coordinate from %f to %f", old_x, x);
247 if (gst_structure_get_double (structure, "pointer_y", &old_y)) {
249 y = MIN (y, result.y + result.h);
250 y = MAX (y - result.y, 0);
251 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
252 (gdouble) y * yscale, NULL);
253 GST_DEBUG_OBJECT (ddrawsink,
254 "translated navigation event x coordinate from %f to %f", old_y, y);
257 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ddrawsink));
259 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
260 gst_pad_send_event (pad, event);
262 gst_object_unref (pad);
267 gst_directdraw_sink_navigation_interface_init (GstNavigationInterface * iface)
269 iface->send_event = gst_directdraw_sink_navigation_send_event;
273 gst_directdraw_sink_init_interfaces (GType type)
275 static const GInterfaceInfo iface_info = {
276 (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
281 static const GInterfaceInfo xoverlay_info = {
282 (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_interface_init,
287 static const GInterfaceInfo navigation_info = {
288 (GInterfaceInitFunc) gst_directdraw_sink_navigation_interface_init,
293 g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
295 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
296 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
299 /* Subclass of GstBuffer which manages buffer_pool surfaces lifetime */
300 static void gst_ddrawsurface_finalize (GstMiniObject * mini_object);
301 static GstBufferClass *ddrawsurface_parent_class = NULL;
304 gst_ddrawsurface_init (GstDDrawSurface * surface, gpointer g_class)
306 surface->surface = NULL;
309 surface->ddrawsink = NULL;
310 surface->locked = FALSE;
311 surface->system_memory = FALSE;
312 memset (&surface->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
316 gst_ddrawsurface_class_init (gpointer g_class, gpointer class_data)
318 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
320 ddrawsurface_parent_class = g_type_class_peek_parent (g_class);
322 mini_object_class->finalize = GST_DEBUG_FUNCPTR (gst_ddrawsurface_finalize);
326 gst_ddrawsurface_get_type (void)
328 static GType _gst_ddrawsurface_type;
330 if (G_UNLIKELY (_gst_ddrawsurface_type == 0)) {
331 static const GTypeInfo ddrawsurface_info = {
332 sizeof (GstBufferClass),
335 gst_ddrawsurface_class_init,
338 sizeof (GstDDrawSurface),
340 (GInstanceInitFunc) gst_ddrawsurface_init,
343 _gst_ddrawsurface_type = g_type_register_static (GST_TYPE_BUFFER,
344 "GstDDrawSurface", &ddrawsurface_info, 0);
346 return _gst_ddrawsurface_type;
350 gst_ddrawsurface_finalize (GstMiniObject * mini_object)
352 GstDirectDrawSink *ddrawsink = NULL;
353 GstDDrawSurface *surface;
355 surface = (GstDDrawSurface *) mini_object;
357 ddrawsink = surface->ddrawsink;
361 /* If our geometry changed we can't reuse that image. */
362 if ((surface->width != ddrawsink->video_width) ||
363 (surface->height != ddrawsink->video_height) ||
364 (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
365 sizeof (DDPIXELFORMAT)) != 0 ||
366 !gst_directdraw_sink_surface_check (ddrawsink, surface))
368 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
369 "destroy image as its size changed %dx%d vs current %dx%d",
370 surface->width, surface->height, ddrawsink->video_width,
371 ddrawsink->video_height);
372 gst_directdraw_sink_surface_destroy (ddrawsink, surface);
373 GST_MINI_OBJECT_CLASS (ddrawsurface_parent_class)->finalize (mini_object);
375 /* In that case we can reuse the image and add it to our image pool. */
376 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
377 "recycling image in pool");
379 /* need to increment the refcount again to recycle */
380 gst_buffer_ref (GST_BUFFER (surface));
382 g_mutex_lock (ddrawsink->pool_lock);
383 ddrawsink->buffer_pool = g_slist_prepend (ddrawsink->buffer_pool, surface);
384 g_mutex_unlock (ddrawsink->pool_lock);
390 GST_CAT_WARNING (directdrawsink_debug, "no sink found");
391 GST_MINI_OBJECT_CLASS (ddrawsurface_parent_class)->finalize (mini_object);
395 /************************************************************************/
396 /* Directdraw sink functions */
397 /************************************************************************/
399 gst_directdraw_sink_base_init (gpointer g_class)
401 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
403 gst_element_class_set_static_metadata (element_class,
404 "Direct Draw Video Sink", "Sink/Video",
405 "Output to a video card via Direct Draw",
406 "Sebastien Moutte <sebastien@moutte.net>");
407 gst_element_class_add_pad_template (element_class,
408 gst_static_pad_template_get (&directdrawsink_sink_factory));
412 gst_directdraw_sink_class_init (GstDirectDrawSinkClass * klass)
414 GObjectClass *gobject_class;
415 GstElementClass *gstelement_class;
416 GstBaseSinkClass *gstbasesink_class;
418 gobject_class = (GObjectClass *) klass;
419 gstbasesink_class = (GstBaseSinkClass *) klass;
420 gstelement_class = (GstElementClass *) klass;
422 GST_DEBUG_CATEGORY_INIT (directdrawsink_debug, "directdrawsink", 0,
425 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directdraw_sink_finalize);
426 gobject_class->get_property =
427 GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_property);
428 gobject_class->set_property =
429 GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_property);
430 gstelement_class->change_state =
431 GST_DEBUG_FUNCPTR (gst_directdraw_sink_change_state);
432 gstbasesink_class->get_caps =
433 GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_caps);
434 gstbasesink_class->set_caps =
435 GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_caps);
436 gstbasesink_class->preroll =
437 GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
438 gstbasesink_class->render =
439 GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
440 gstbasesink_class->get_times =
441 GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_times);
442 gstbasesink_class->buffer_alloc =
443 GST_DEBUG_FUNCPTR (gst_directdraw_sink_buffer_alloc);
445 /* install properties */
446 /* setup aspect ratio mode */
447 g_object_class_install_property (G_OBJECT_CLASS (klass),
448 PROP_KEEP_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio",
449 "Force aspect ratio",
450 "When enabled, scaling will respect original aspect ratio", TRUE,
455 gst_directdraw_sink_set_property (GObject * object, guint prop_id,
456 const GValue * value, GParamSpec * pspec)
458 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
461 case PROP_KEEP_ASPECT_RATIO:
462 ddrawsink->keep_aspect_ratio = g_value_get_boolean (value);
465 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
471 gst_directdraw_sink_get_property (GObject * object, guint prop_id,
472 GValue * value, GParamSpec * pspec)
474 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
477 case PROP_KEEP_ASPECT_RATIO:
478 g_value_set_boolean (value, ddrawsink->keep_aspect_ratio);
481 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
487 gst_directdraw_sink_finalize (GObject * object)
489 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
491 if (ddrawsink->pool_lock) {
492 g_mutex_free (ddrawsink->pool_lock);
493 ddrawsink->pool_lock = NULL;
495 if (ddrawsink->caps) {
496 gst_caps_unref (ddrawsink->caps);
497 ddrawsink->caps = NULL;
499 if (ddrawsink->setup) {
500 gst_directdraw_sink_cleanup (ddrawsink);
503 G_OBJECT_CLASS (parent_class)->finalize (object);
507 gst_directdraw_sink_init (GstDirectDrawSink * ddrawsink,
508 GstDirectDrawSinkClass * g_class)
510 /*init members variables */
511 ddrawsink->ddraw_object = NULL;
512 ddrawsink->primary_surface = NULL;
513 ddrawsink->offscreen_surface = NULL;
514 ddrawsink->clipper = NULL;
515 ddrawsink->video_window = NULL;
516 ddrawsink->our_video_window = TRUE;
517 ddrawsink->previous_wndproc = NULL;
518 ddrawsink->previous_user_data = (LONG_PTR)NULL;
519 ddrawsink->last_buffer = NULL;
520 ddrawsink->caps = NULL;
521 ddrawsink->window_thread = NULL;
522 ddrawsink->setup = FALSE;
523 ddrawsink->buffer_pool = NULL;
524 ddrawsink->keep_aspect_ratio = FALSE;
525 ddrawsink->pool_lock = g_mutex_new ();
526 ddrawsink->can_blit_between_colorspace = TRUE;
527 ddrawsink->must_recreate_offscreen = FALSE;
528 memset (&ddrawsink->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
530 /*video default values */
531 ddrawsink->video_height = 0;
532 ddrawsink->video_width = 0;
533 ddrawsink->fps_n = 0;
534 ddrawsink->fps_d = 0;
538 gst_directdraw_sink_get_caps (GstBaseSink * bsink)
540 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
541 GstCaps *caps = NULL;
543 if (!ddrawsink->setup) {
544 caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
546 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
547 "getcaps called and we are not setup yet, " "returning template %"
548 GST_PTR_FORMAT, caps);
550 caps = gst_caps_ref (ddrawsink->caps);
557 gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
559 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
560 GstStructure *structure = NULL;
565 structure = gst_caps_get_structure (caps, 0);
569 if (!gst_video_parse_caps_pixel_aspect_ratio (caps, &par_n, &par_d)) {
574 ret = gst_structure_get_int (structure, "width", &ddrawsink->video_width);
575 ret &= gst_structure_get_int (structure, "height", &ddrawsink->video_height);
576 fps = gst_structure_get_value (structure, "framerate");
577 ret &= (fps != NULL);
579 gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
580 &ddrawsink->dd_pixel_format);
582 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
583 ("Failed to get caps properties from caps"), (NULL));
586 GST_VIDEO_SINK_WIDTH (ddrawsink) = ddrawsink->video_width * par_n / par_d;
587 GST_VIDEO_SINK_HEIGHT (ddrawsink) = ddrawsink->video_height;
589 ddrawsink->fps_n = gst_value_get_fraction_numerator (fps);
590 ddrawsink->fps_d = gst_value_get_fraction_denominator (fps);
592 /* Notify application to set window id now */
593 if (!ddrawsink->video_window) {
594 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ddrawsink));
597 /* If we still don't have a window at that stage we create our own */
598 if (!ddrawsink->video_window) {
599 gst_directdraw_sink_create_default_window (ddrawsink);
602 /* if we are rendering to our own window, resize it to video size */
603 if (ddrawsink->video_window && ddrawsink->our_video_window) {
604 SetWindowPos (ddrawsink->video_window, NULL,
606 GST_VIDEO_SINK_WIDTH (ddrawsink) +
607 (GetSystemMetrics (SM_CXSIZEFRAME) * 2),
608 GST_VIDEO_SINK_HEIGHT (ddrawsink) + GetSystemMetrics (SM_CYCAPTION) +
609 (GetSystemMetrics (SM_CYSIZEFRAME) * 2), SWP_SHOWWINDOW | SWP_NOMOVE);
612 /* release the surface, we have to recreate it! */
613 if (ddrawsink->offscreen_surface) {
614 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
615 ddrawsink->offscreen_surface = NULL;
618 /* create an offscreen surface with the caps */
619 ret = gst_directdraw_sink_check_offscreen_surface (ddrawsink);
621 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
622 ("Can't create a directdraw offscreen surface with the input caps"),
629 static GstStateChangeReturn
630 gst_directdraw_sink_change_state (GstElement * element,
631 GstStateChange transition)
633 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);
634 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
636 switch (transition) {
637 case GST_STATE_CHANGE_NULL_TO_READY:
638 if (!gst_directdraw_sink_setup_ddraw (ddrawsink)) {
639 ret = GST_STATE_CHANGE_FAILURE;
643 if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink))) {
644 ret = GST_STATE_CHANGE_FAILURE;
652 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
654 switch (transition) {
655 case GST_STATE_CHANGE_PAUSED_TO_READY:
656 ddrawsink->fps_n = 0;
657 ddrawsink->fps_d = 1;
658 ddrawsink->video_width = 0;
659 ddrawsink->video_height = 0;
660 if (ddrawsink->buffer_pool)
661 gst_directdraw_sink_bufferpool_clear (ddrawsink);
663 case GST_STATE_CHANGE_READY_TO_NULL:
664 if (ddrawsink->setup)
665 gst_directdraw_sink_cleanup (ddrawsink);
676 gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
677 guint size, GstCaps * caps, GstBuffer ** buf)
679 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
680 GstStructure *structure;
682 GstDDrawSurface *surface = NULL;
683 GstFlowReturn ret = GST_FLOW_OK;
684 GstCaps *buffer_caps = caps;
685 gboolean buffercaps_unref = FALSE;
687 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
688 "a buffer of %u bytes was requested", size);
690 structure = gst_caps_get_structure (caps, 0);
691 if (!gst_structure_get_int (structure, "width", &width) ||
692 !gst_structure_get_int (structure, "height", &height)) {
693 GST_WARNING_OBJECT (ddrawsink, "invalid caps for buffer allocation %"
694 GST_PTR_FORMAT, caps);
695 return GST_FLOW_UNEXPECTED;
698 g_mutex_lock (ddrawsink->pool_lock);
700 /* Inspect our buffer pool */
701 while (ddrawsink->buffer_pool) {
702 surface = (GstDDrawSurface *) ddrawsink->buffer_pool->data;
704 /* Removing from the pool */
705 ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
706 ddrawsink->buffer_pool);
708 /* If the surface is invalid for our need, destroy */
709 if ((surface->width != width) ||
710 (surface->height != height) ||
711 (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
712 sizeof (DDPIXELFORMAT)) ||
713 !gst_directdraw_sink_surface_check (ddrawsink, surface))
715 gst_directdraw_sink_surface_destroy (ddrawsink, surface);
716 gst_buffer_unref (GST_BUFFER_CAST (surface));
719 /* We found a suitable surface */
725 if (!ddrawsink->can_blit_between_colorspace) {
726 /* Hardware doesn't support blit from one colorspace to another.
727 * Check if the colorspace of the current display mode has changed since
728 * the last negociation. If it's the case, we will have to renegociate
732 DDSURFACEDESC2 surface_desc;
735 if (!gst_structure_get_int (structure, "depth", (gint *) & depth)) {
736 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
737 "Can't get depth from buffer_alloc caps");
738 return GST_FLOW_ERROR;
740 surface_desc.dwSize = sizeof (surface_desc);
743 IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
744 (DDSURFACEDESC *) sd);
746 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
747 "Can't get current display mode (error=%ld)", (glong) hres);
748 return GST_FLOW_ERROR;
751 if (depth != gst_directdraw_sink_get_depth (&surface_desc.ddpfPixelFormat)) {
752 GstCaps *copy_caps = NULL;
753 GstStructure *copy_structure = NULL;
754 GstCaps *display_caps = NULL;
755 GstStructure *display_structure = NULL;
757 /* make a copy of the original caps */
758 copy_caps = gst_caps_copy (caps);
759 copy_structure = gst_caps_get_structure (copy_caps, 0);
762 gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
764 display_structure = gst_caps_get_structure (display_caps, 0);
765 if (display_structure) {
766 gint bpp, endianness, red_mask, green_mask, blue_mask;
768 /* get new display mode properties */
769 gst_structure_get_int (display_structure, "depth", (gint *) & depth);
770 gst_structure_get_int (display_structure, "bpp", &bpp);
771 gst_structure_get_int (display_structure, "endianness", &endianness);
772 gst_structure_get_int (display_structure, "red_mask", &red_mask);
773 gst_structure_get_int (display_structure, "green_mask", &green_mask);
774 gst_structure_get_int (display_structure, "blue_mask", &blue_mask);
776 /* apply the new display mode changes to the previous caps */
777 gst_structure_set (copy_structure,
778 "bpp", G_TYPE_INT, bpp,
779 "depth", G_TYPE_INT, depth,
780 "endianness", G_TYPE_INT, endianness,
781 "red_mask", G_TYPE_INT, red_mask,
782 "green_mask", G_TYPE_INT, green_mask,
783 "blue_mask", G_TYPE_INT, blue_mask, NULL);
785 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ddrawsink),
787 buffer_caps = copy_caps;
788 buffercaps_unref = TRUE;
789 /* update buffer size needed to store video frames according to new caps */
790 size = width * height * (bpp / 8);
792 /* update our member pixel format */
793 gst_ddrawvideosink_get_format_from_caps (ddrawsink, buffer_caps,
794 &ddrawsink->dd_pixel_format);
795 ddrawsink->must_recreate_offscreen = TRUE;
797 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
798 " desired caps %s \n\n new caps %s", gst_caps_to_string (caps),
799 gst_caps_to_string (buffer_caps));
801 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
802 "peer refused caps re-negociation "
803 "and we can't render with the current caps.");
804 ret = GST_FLOW_ERROR;
807 gst_caps_unref (display_caps);
810 if (!buffercaps_unref)
811 gst_caps_unref (copy_caps);
815 /* We haven't found anything, creating a new one */
817 surface = gst_directdraw_sink_surface_create (ddrawsink, buffer_caps, size);
820 /* Now we should have a surface, set appropriate caps on it */
822 GST_BUFFER_FLAGS (GST_BUFFER (surface)) = 0;
823 gst_buffer_set_caps (GST_BUFFER (surface), buffer_caps);
826 g_mutex_unlock (ddrawsink->pool_lock);
828 *buf = GST_BUFFER (surface);
830 if (buffercaps_unref)
831 gst_caps_unref (buffer_caps);
837 gst_directdraw_sink_draw_borders (GstDirectDrawSink * ddrawsink, RECT dst_rect)
839 RECT win_rect, fill_rect;
843 g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
845 /* Get the target window rect */
848 ClientToScreen (ddrawsink->video_window, &win_point);
849 GetClientRect (ddrawsink->video_window, &win_rect);
850 OffsetRect (&win_rect, win_point.x, win_point.y);
852 /* We acquire a drawing context */
853 if ((hdc = GetDC (ddrawsink->video_window))) {
854 HBRUSH brush = CreateSolidBrush (RGB (0, 0, 0));
856 /* arrange for logical coordinates that match screen coordinates */
857 SetWindowOrgEx (hdc, win_point.x, win_point.y, NULL);
859 if (dst_rect.left > win_rect.left) {
860 fill_rect.left = win_rect.left;
861 fill_rect.top = win_rect.top;
862 fill_rect.bottom = win_rect.bottom;
863 fill_rect.right = dst_rect.left;
864 FillRect (hdc, &fill_rect, brush);
867 if (dst_rect.right < win_rect.right) {
868 fill_rect.top = win_rect.top;
869 fill_rect.left = dst_rect.right;
870 fill_rect.bottom = win_rect.bottom;
871 fill_rect.right = win_rect.right;
872 FillRect (hdc, &fill_rect, brush);
875 if (dst_rect.top > win_rect.top) {
876 fill_rect.top = win_rect.top;
877 fill_rect.left = win_rect.left;
878 fill_rect.right = win_rect.right;
879 fill_rect.bottom = dst_rect.top;
880 FillRect (hdc, &fill_rect, brush);
883 if (dst_rect.bottom < win_rect.bottom) {
884 fill_rect.top = dst_rect.bottom;
885 fill_rect.left = win_rect.left;
886 fill_rect.right = win_rect.right;
887 fill_rect.bottom = win_rect.bottom;
888 FillRect (hdc, &fill_rect, brush);
890 DeleteObject (brush);
891 ReleaseDC (ddrawsink->video_window, hdc);
896 gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
898 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
900 RECT destsurf_rect, src_rect;
901 POINT dest_surf_point;
904 /* save a reference to the input buffer */
905 gst_buffer_ref (buf);
906 if (ddrawsink->last_buffer != NULL)
907 gst_buffer_unref (ddrawsink->last_buffer);
908 ddrawsink->last_buffer = buf;
910 /* use last buffer */
911 buf = ddrawsink->last_buffer;
915 GST_ERROR_OBJECT (ddrawsink, "No buffer to render.");
916 return GST_FLOW_ERROR;
917 } else if (!ddrawsink->video_window) {
918 GST_WARNING_OBJECT (ddrawsink, "No video window to render to.");
919 return GST_FLOW_ERROR;
922 /* get the video window position */
923 GST_OBJECT_LOCK (ddrawsink);
924 if (G_UNLIKELY (!ddrawsink->video_window)) {
925 GST_OBJECT_UNLOCK (ddrawsink);
926 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
927 "gst_directdraw_sink_show_frame our video window disappeared");
928 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, NOT_FOUND,
929 ("Output window was closed"), (NULL));
930 return GST_FLOW_ERROR;
932 dest_surf_point.x = 0;
933 dest_surf_point.y = 0;
934 ClientToScreen (ddrawsink->video_window, &dest_surf_point);
935 GetClientRect (ddrawsink->video_window, &destsurf_rect);
936 OffsetRect (&destsurf_rect, dest_surf_point.x, dest_surf_point.y);
938 /* Check to see if we have an area to draw to.
939 * When the window is minimized, it will trigger the
940 * "IDirectDrawSurface7_Blt (object's offscreen surface)" warning,
941 * with a msg that the rectangle is invalid */
942 if (destsurf_rect.right <= destsurf_rect.left ||
943 destsurf_rect.bottom <= destsurf_rect.top) {
944 GST_OBJECT_UNLOCK (ddrawsink);
945 GST_DEBUG_OBJECT (ddrawsink, "invalid rendering window rectangle "
946 "(%ld, %ld), (%ld, %ld)", destsurf_rect.left, destsurf_rect.top,
947 destsurf_rect.right, destsurf_rect.bottom);
951 if (ddrawsink->keep_aspect_ratio) {
952 /* center image to dest image keeping aspect ratio */
955 src_rect.bottom = GST_VIDEO_SINK_HEIGHT (ddrawsink);
956 src_rect.right = GST_VIDEO_SINK_WIDTH (ddrawsink);
957 gst_directdraw_sink_center_rect (ddrawsink, src_rect, destsurf_rect,
959 gst_directdraw_sink_draw_borders (ddrawsink, destsurf_rect);
961 GST_OBJECT_UNLOCK (ddrawsink);
963 if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
964 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
965 ddrawsink->offscreen_surface = NULL;
968 /* check for surfaces lost */
969 if (!gst_directdraw_sink_check_primary_surface (ddrawsink) ||
970 !gst_directdraw_sink_check_offscreen_surface (ddrawsink)) {
971 return GST_FLOW_ERROR;
974 if (!GST_IS_DDRAWSURFACE (buf) ||
975 ((GST_IS_DDRAWSURFACE (buf)) && (GST_BUFFER (buf)->malloc_data))) {
976 /* We are receiving a system memory buffer so we will copy
977 to the memory of our offscreen surface and next blit this surface
978 on the primary surface */
980 guint src_pitch, line;
981 DDSURFACEDESC2 surf_desc;
984 ZeroMemory (&surf_desc, sizeof (surf_desc));
985 surf_desc.dwSize = sizeof (surf_desc);
988 /* Lock the surface */
990 IDirectDrawSurface7_Lock (ddrawsink->offscreen_surface, NULL,
991 (DDSURFACEDESC *) sd, DDLOCK_WAIT, NULL);
993 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
994 "gst_directdraw_sink_show_frame failed locking surface %s",
995 DDErrorString (hRes));
997 if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK)
1000 return GST_FLOW_ERROR;
1003 /* Write each line respecting the destination surface pitch */
1004 data = surf_desc.lpSurface;
1005 if (ddrawsink->video_height) {
1006 src_pitch = GST_BUFFER_SIZE (buf) / ddrawsink->video_height;
1007 for (line = 0; line < surf_desc.dwHeight; line++) {
1008 memcpy (data, GST_BUFFER_DATA (buf) + (line * src_pitch), src_pitch);
1009 data += surf_desc.lPitch;
1013 /* Unlock the surface */
1014 hRes = IDirectDrawSurface7_Unlock (ddrawsink->offscreen_surface, NULL);
1015 if (hRes != DD_OK) {
1016 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1017 "gst_directdraw_sink_show_frame failed unlocking surface %s",
1018 DDErrorString (hRes));
1019 return GST_FLOW_ERROR;
1022 /* blit to primary surface ( Blt will scale the video the dest rect surface
1024 hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
1025 ddrawsink->offscreen_surface, NULL, DDBLT_WAIT, NULL);
1026 if (hRes != DD_OK) /* FIXME: Is it really safe to continue past here ? */
1027 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1028 "IDirectDrawSurface7_Blt (object's offscreen surface) " "returned %s",
1029 DDErrorString (hRes));
1032 /* We are receiving a directdraw surface (previously returned by our buffer
1033 * pool so we will simply blit it on the primary surface */
1034 GstDDrawSurface *surface = NULL;
1036 surface = GST_DDRAWSURFACE (buf);
1038 /* Unlocking surface before blit */
1039 IDirectDrawSurface7_Unlock (surface->surface, NULL);
1040 surface->locked = FALSE;
1042 /* blit to our primary surface */
1043 hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
1044 surface->surface, NULL, DDBLT_WAIT, NULL);
1045 if (hRes != DD_OK) /* FIXME: Is it really safe to continue past here ? */
1046 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1047 "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
1048 "returned %s", DDErrorString (hRes));
1056 gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1057 GstClockTime * start, GstClockTime * end)
1059 GstDirectDrawSink *ddrawsink;
1061 ddrawsink = GST_DIRECTDRAW_SINK (bsink);
1063 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1064 *start = GST_BUFFER_TIMESTAMP (buf);
1065 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1066 *end = *start + GST_BUFFER_DURATION (buf);
1068 if (ddrawsink->fps_n > 0) {
1069 *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
1075 /* Utility functions */
1077 /* this function fill a DDPIXELFORMAT using Gstreamer caps */
1079 gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
1080 GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
1082 GstStructure *structure = NULL;
1083 gboolean ret = TRUE;
1086 g_return_val_if_fail (pPixelFormat, FALSE);
1087 g_return_val_if_fail (caps, FALSE);
1089 /* init structure */
1090 memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
1091 pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
1093 if (!(structure = gst_caps_get_structure (caps, 0))) {
1094 GST_CAT_ERROR_OBJECT (directdrawsink_debug, ddrawsink,
1095 "can't get structure pointer from caps");
1099 if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
1100 gint depth, bitcount, bitmask, endianness;
1102 pPixelFormat->dwFlags = DDPF_RGB;
1103 ret &= gst_structure_get_int (structure, "bpp", &bitcount);
1104 pPixelFormat->dwRGBBitCount = bitcount;
1105 ret &= gst_structure_get_int (structure, "depth", &depth);
1106 ret &= gst_structure_get_int (structure, "red_mask", &bitmask);
1107 pPixelFormat->dwRBitMask = bitmask;
1108 ret &= gst_structure_get_int (structure, "green_mask", &bitmask);
1109 pPixelFormat->dwGBitMask = bitmask;
1110 ret &= gst_structure_get_int (structure, "blue_mask", &bitmask);
1111 pPixelFormat->dwBBitMask = bitmask;
1113 gst_structure_get_int (structure, "endianness", &endianness);
1114 if (endianness == G_BIG_ENDIAN) {
1115 endianness = G_LITTLE_ENDIAN;
1116 pPixelFormat->dwRBitMask = GUINT32_TO_BE (pPixelFormat->dwRBitMask);
1117 pPixelFormat->dwGBitMask = GUINT32_TO_BE (pPixelFormat->dwGBitMask);
1118 pPixelFormat->dwBBitMask = GUINT32_TO_BE (pPixelFormat->dwBBitMask);
1120 } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
1123 pPixelFormat->dwFlags = DDPF_FOURCC;
1124 ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
1125 pPixelFormat->dwFourCC = fourcc;
1127 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1128 "unknown caps name received %" GST_PTR_FORMAT, caps);
1135 /* This function centers the RECT of source surface to
1136 a dest surface and set the result RECT into result */
1138 gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
1139 RECT dst, RECT * result)
1141 gdouble src_ratio, dst_ratio;
1142 long src_width = src.right;
1143 long src_height = src.bottom;
1144 long dst_width = dst.right - dst.left;
1145 long dst_heigth = dst.bottom - dst.top;
1146 long result_width = 0, result_height = 0;
1148 g_return_if_fail (result != NULL);
1150 src_ratio = (gdouble) src_width / src_height;
1151 dst_ratio = (gdouble) dst_width / dst_heigth;
1153 if (src_ratio > dst_ratio) {
1155 result_height = (long) (dst_width / src_ratio);
1157 result->left = dst.left;
1158 result->right = dst.right;
1159 result->top = dst.top + (dst_heigth - result_height) / 2;
1160 result->bottom = result->top + result_height;
1162 } else if (src_ratio < dst_ratio) {
1164 result_width = (long) (dst_heigth * src_ratio);
1166 result->top = dst.top;
1167 result->bottom = dst.bottom;
1168 result->left = dst.left + (dst_width - result_width) / 2;
1169 result->right = result->left + result_width;
1173 memcpy (result, &dst, sizeof (RECT));
1176 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1177 "source is %ldx%ld dest is %ldx%ld, result is %ldx%ld with x,y %ldx%ld",
1178 src_width, src_height, dst_width, dst_heigth,
1179 result->right - result->left, result->bottom - result->top, result->left,
1184 * Get DirectDraw error message.
1186 * Returns: Text representation of the error.
1189 DDErrorString (HRESULT hr)
1192 case DDERR_ALREADYINITIALIZED:
1193 return "DDERR_ALREADYINITIALIZED";
1194 case DDERR_CANNOTATTACHSURFACE:
1195 return "DDERR_CANNOTATTACHSURFACE";
1196 case DDERR_CANNOTDETACHSURFACE:
1197 return "DDERR_CANNOTDETACHSURFACE";
1198 case DDERR_CURRENTLYNOTAVAIL:
1199 return "DDERR_CURRENTLYNOTAVAIL";
1200 case DDERR_EXCEPTION:
1201 return "DDERR_EXCEPTION";
1203 return "DDERR_GENERIC";
1204 case DDERR_HEIGHTALIGN:
1205 return "DDERR_HEIGHTALIGN";
1206 case DDERR_INCOMPATIBLEPRIMARY:
1207 return "DDERR_INCOMPATIBLEPRIMARY";
1208 case DDERR_INVALIDCAPS:
1209 return "DDERR_INVALIDCAPS";
1210 case DDERR_INVALIDCLIPLIST:
1211 return "DDERR_INVALIDCLIPLIST";
1212 case DDERR_INVALIDMODE:
1213 return "DDERR_INVALIDMODE";
1214 case DDERR_INVALIDOBJECT:
1215 return "DDERR_INVALIDOBJECT";
1216 case DDERR_INVALIDPARAMS:
1217 return "DDERR_INVALIDPARAMS";
1218 case DDERR_INVALIDPIXELFORMAT:
1219 return "DDERR_INVALIDPIXELFORMAT";
1220 case DDERR_INVALIDRECT:
1221 return "DDERR_INVALIDRECT";
1222 case DDERR_LOCKEDSURFACES:
1223 return "DDERR_LOCKEDSURFACES";
1225 return "DDERR_NO3D";
1226 case DDERR_NOALPHAHW:
1227 return "DDERR_NOALPHAHW";
1228 case DDERR_NOCLIPLIST:
1229 return "DDERR_NOCLIPLIST";
1230 case DDERR_NOCOLORCONVHW:
1231 return "DDERR_NOCOLORCONVHW";
1232 case DDERR_NOCOOPERATIVELEVELSET:
1233 return "DDERR_NOCOOPERATIVELEVELSET";
1234 case DDERR_NOCOLORKEY:
1235 return "DDERR_NOCOLORKEY";
1236 case DDERR_NOCOLORKEYHW:
1237 return "DDERR_NOCOLORKEYHW";
1238 case DDERR_NODIRECTDRAWSUPPORT:
1239 return "DDERR_NODIRECTDRAWSUPPORT";
1240 case DDERR_NOEXCLUSIVEMODE:
1241 return "DDERR_NOEXCLUSIVEMODE";
1242 case DDERR_NOFLIPHW:
1243 return "DDERR_NOFLIPHW";
1245 return "DDERR_NOGDI";
1246 case DDERR_NOMIRRORHW:
1247 return "DDERR_NOMIRRORHW";
1248 case DDERR_NOTFOUND:
1249 return "DDERR_NOTFOUND";
1250 case DDERR_NOOVERLAYHW:
1251 return "DDERR_NOOVERLAYHW";
1252 case DDERR_NORASTEROPHW:
1253 return "DDERR_NORASTEROPHW";
1254 case DDERR_NOROTATIONHW:
1255 return "DDERR_NOROTATIONHW";
1256 case DDERR_NOSTRETCHHW:
1257 return "DDERR_NOSTRETCHHW";
1258 case DDERR_NOT4BITCOLOR:
1259 return "DDERR_NOT4BITCOLOR";
1260 case DDERR_NOT4BITCOLORINDEX:
1261 return "DDERR_NOT4BITCOLORINDEX";
1262 case DDERR_NOT8BITCOLOR:
1263 return "DDERR_NOT8BITCOLOR";
1264 case DDERR_NOTEXTUREHW:
1265 return "DDERR_NOTEXTUREHW";
1266 case DDERR_NOVSYNCHW:
1267 return "DDERR_NOVSYNCHW";
1268 case DDERR_NOZBUFFERHW:
1269 return "DDERR_NOZBUFFERHW";
1270 case DDERR_NOZOVERLAYHW:
1271 return "DDERR_NOZOVERLAYHW";
1272 case DDERR_OUTOFCAPS:
1273 return "DDERR_OUTOFCAPS";
1274 case DDERR_OUTOFMEMORY:
1275 return "DDERR_OUTOFMEMORY";
1276 case DDERR_OUTOFVIDEOMEMORY:
1277 return "DDERR_OUTOFVIDEOMEMORY";
1278 case DDERR_OVERLAYCANTCLIP:
1279 return "DDERR_OVERLAYCANTCLIP";
1280 case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
1281 return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE";
1282 case DDERR_PALETTEBUSY:
1283 return "DDERR_PALETTEBUSY";
1284 case DDERR_COLORKEYNOTSET:
1285 return "DDERR_COLORKEYNOTSET";
1286 case DDERR_SURFACEALREADYATTACHED:
1287 return "DDERR_SURFACEALREADYATTACHED";
1288 case DDERR_SURFACEALREADYDEPENDENT:
1289 return "DDERR_SURFACEALREADYDEPENDENT";
1290 case DDERR_SURFACEBUSY:
1291 return "DDERR_SURFACEBUSY";
1292 case DDERR_CANTLOCKSURFACE:
1293 return "DDERR_CANTLOCKSURFACE";
1294 case DDERR_SURFACEISOBSCURED:
1295 return "DDERR_SURFACEISOBSCURED";
1296 case DDERR_SURFACELOST:
1297 return "DDERR_SURFACELOST";
1298 case DDERR_SURFACENOTATTACHED:
1299 return "DDERR_SURFACENOTATTACHED";
1300 case DDERR_TOOBIGHEIGHT:
1301 return "DDERR_TOOBIGHEIGHT";
1302 case DDERR_TOOBIGSIZE:
1303 return "DDERR_TOOBIGSIZE";
1304 case DDERR_TOOBIGWIDTH:
1305 return "DDERR_TOOBIGWIDTH";
1306 case DDERR_UNSUPPORTED:
1307 return "DDERR_UNSUPPORTED";
1308 case DDERR_UNSUPPORTEDFORMAT:
1309 return "DDERR_UNSUPPORTEDFORMAT";
1310 case DDERR_UNSUPPORTEDMASK:
1311 return "DDERR_UNSUPPORTEDMASK";
1312 case DDERR_VERTICALBLANKINPROGRESS:
1313 return "DDERR_VERTICALBLANKINPROGRESS";
1314 case DDERR_WASSTILLDRAWING:
1315 return "DDERR_WASSTILLDRAWING";
1317 return "DDERR_XALIGN";
1318 case DDERR_INVALIDDIRECTDRAWGUID:
1319 return "DDERR_INVALIDDIRECTDRAWGUID";
1320 case DDERR_DIRECTDRAWALREADYCREATED:
1321 return "DDERR_DIRECTDRAWALREADYCREATED";
1322 case DDERR_NODIRECTDRAWHW:
1323 return "DDERR_NODIRECTDRAWHW";
1324 case DDERR_PRIMARYSURFACEALREADYEXISTS:
1325 return "DDERR_PRIMARYSURFACEALREADYEXISTS";
1326 case DDERR_NOEMULATION:
1327 return "DDERR_NOEMULATION";
1328 case DDERR_REGIONTOOSMALL:
1329 return "DDERR_REGIONTOOSMALL";
1330 case DDERR_CLIPPERISUSINGHWND:
1331 return "DDERR_CLIPPERISUSINGHWND";
1332 case DDERR_NOCLIPPERATTACHED:
1333 return "DDERR_NOCLIPPERATTACHED";
1335 return "DDERR_NOHWND";
1336 case DDERR_HWNDSUBCLASSED:
1337 return "DDERR_HWNDSUBCLASSED";
1338 case DDERR_HWNDALREADYSET:
1339 return "DDERR_HWNDALREADYSET";
1340 case DDERR_NOPALETTEATTACHED:
1341 return "DDERR_NOPALETTEATTACHED";
1342 case DDERR_NOPALETTEHW:
1343 return "DDERR_NOPALETTEHW";
1344 case DDERR_BLTFASTCANTCLIP:
1345 return "DDERR_BLTFASTCANTCLIP";
1347 return "DDERR_NOBLTHW";
1348 case DDERR_NODDROPSHW:
1349 return "DDERR_NODDROPSHW";
1350 case DDERR_OVERLAYNOTVISIBLE:
1351 return "DDERR_OVERLAYNOTVISIBLE";
1352 case DDERR_NOOVERLAYDEST:
1353 return "DDERR_NOOVERLAYDEST";
1354 case DDERR_INVALIDPOSITION:
1355 return "DDERR_INVALIDPOSITION";
1356 case DDERR_NOTAOVERLAYSURFACE:
1357 return "DDERR_NOTAOVERLAYSURFACE";
1358 case DDERR_EXCLUSIVEMODEALREADYSET:
1359 return "DDERR_EXCLUSIVEMODEALREADYSET";
1360 case DDERR_NOTFLIPPABLE:
1361 return "DDERR_NOTFLIPPABLE";
1362 case DDERR_CANTDUPLICATE:
1363 return "DDERR_CANTDUPLICATE";
1364 case DDERR_NOTLOCKED:
1365 return "DDERR_NOTLOCKED";
1366 case DDERR_CANTCREATEDC:
1367 return "DDERR_CANTCREATEDC";
1369 return "DDERR_NODC";
1370 case DDERR_WRONGMODE:
1371 return "DDERR_WRONGMODE";
1372 case DDERR_IMPLICITLYCREATED:
1373 return "DDERR_IMPLICITLYCREATED";
1374 case DDERR_NOTPALETTIZED:
1375 return "DDERR_NOTPALETTIZED";
1376 case DDERR_UNSUPPORTEDMODE:
1377 return "DDERR_UNSUPPORTEDMODE";
1378 case DDERR_NOMIPMAPHW:
1379 return "DDERR_NOMIPMAPHW";
1380 case DDERR_INVALIDSURFACETYPE:
1381 return "DDERR_INVALIDSURFACETYPE";
1382 case DDERR_DCALREADYCREATED:
1383 return "DDERR_DCALREADYCREATED";
1384 case DDERR_CANTPAGELOCK:
1385 return "DDERR_CANTPAGELOCK";
1386 case DDERR_CANTPAGEUNLOCK:
1387 return "DDERR_CANTPAGEUNLOCK";
1388 case DDERR_NOTPAGELOCKED:
1389 return "DDERR_NOTPAGELOCKED";
1390 case DDERR_NOTINITIALIZED:
1391 return "DDERR_NOTINITIALIZED";
1393 return "Unknown Error";
1397 gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
1399 gboolean bRet = TRUE;
1401 /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first
1402 * parameter to force Directdraw to use the hardware emulation layer */
1403 hRes = DirectDrawCreateEx ( /*DDCREATE_EMULATIONONLY */ 0,
1404 (void **) &ddrawsink->ddraw_object, &IID_IDirectDraw7, NULL);
1405 if (hRes != DD_OK || ddrawsink->ddraw_object == NULL) {
1406 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1407 ("Failed to create the DirectDraw object error=%s",
1408 DDErrorString (hRes)), (NULL));
1412 /* set cooperative level */
1413 hRes = IDirectDraw7_SetCooperativeLevel (ddrawsink->ddraw_object,
1414 NULL, DDSCL_NORMAL);
1415 if (hRes != DD_OK) {
1416 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1417 ("Failed to set the set the cooperative level error=%s",
1418 DDErrorString (hRes)), (NULL));
1422 /* setup the clipper object */
1423 hRes = IDirectDraw7_CreateClipper (ddrawsink->ddraw_object, 0,
1424 &ddrawsink->clipper, NULL);
1426 if (hRes == DD_OK && ddrawsink->video_window)
1427 IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
1429 /* create our primary surface */
1430 if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
1433 /* directdraw objects are setup */
1434 ddrawsink->setup = TRUE;
1439 static LRESULT FAR PASCAL
1440 WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1442 GstDirectDrawSink *ddrawsink;
1445 ddrawsink = (GstDirectDrawSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
1449 LPCREATESTRUCT crs = (LPCREATESTRUCT) lParam;
1450 /* Nail pointer to the video sink down to this window */
1451 SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) crs->lpCreateParams);
1458 case WM_LBUTTONDOWN:
1459 case WM_RBUTTONDOWN:
1460 case WM_MBUTTONDOWN:
1465 GstDirectDrawSink *ddrawsink;
1466 ddrawsink = (GstDirectDrawSink *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
1468 if (G_UNLIKELY (!ddrawsink))
1473 GST_OBJECT_LOCK (ddrawsink);
1474 ddrawsink->out_width = LOWORD (lParam);
1475 ddrawsink->out_height = HIWORD (lParam);
1476 GST_OBJECT_UNLOCK (ddrawsink);
1477 GST_DEBUG_OBJECT (ddrawsink, "Window size is %dx%d", LOWORD (wParam),
1484 gunichar2 wcrep[128];
1485 if (GetKeyNameTextW (lParam, wcrep, 128)) {
1486 gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
1488 if (message == WM_CHAR || message == WM_KEYDOWN)
1489 gst_navigation_send_key_event (GST_NAVIGATION (ddrawsink),
1490 "key-press", utfrep);
1491 if (message == WM_CHAR || message == WM_KEYUP)
1492 gst_navigation_send_key_event (GST_NAVIGATION (ddrawsink),
1493 "key-release", utfrep);
1499 case WM_LBUTTONDOWN:
1501 case WM_RBUTTONDOWN:
1503 case WM_MBUTTONDOWN:
1507 const gchar *action;
1512 action = "mouse-move";
1514 case WM_LBUTTONDOWN:
1516 action = "mouse-button-press";
1520 action = "mouse-button-release";
1522 case WM_RBUTTONDOWN:
1524 action = "mouse-button-press";
1528 action = "mouse-button-release";
1530 case WM_MBUTTONDOWN:
1532 action = "mouse-button-press";
1536 action = "mouse-button-release";
1543 x = LOWORD (lParam);
1544 y = HIWORD (lParam);
1547 GST_DEBUG_OBJECT (ddrawsink, "Mouse moved to %dx%d", x, y);
1549 GST_DEBUG_OBJECT (ddrawsink, "Mouse button %d pressed at %dx%d",
1553 gst_navigation_send_mouse_event (GST_NAVIGATION (ddrawsink),
1554 action, button, x, y);
1564 DestroyWindow (hWnd);
1566 PostQuitMessage (0);
1569 if (ddrawsink && ddrawsink->previous_wndproc) {
1570 /* If there was a previous custom WndProc, call it */
1572 /* Temporarily restore the previous user_data */
1573 if (ddrawsink->previous_user_data)
1574 SetWindowLongPtr ( hWnd, GWLP_USERDATA, ddrawsink->previous_user_data );
1576 /* Call previous WndProc */
1577 ret = CallWindowProc (
1578 ddrawsink->previous_wndproc, hWnd, message, wParam, lParam);
1580 /* Point the user_data back to our ddraw_sink */
1581 SetWindowLongPtr ( hWnd, GWLP_USERDATA, (LONG_PTR)ddrawsink );
1583 /* if there was no previous custom WndProc, call Window's default one */
1584 ret = DefWindowProc (hWnd, message, wParam, lParam);
1591 gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
1596 memset (&WndClass, 0, sizeof (WNDCLASS));
1597 WndClass.style = CS_HREDRAW | CS_VREDRAW;
1598 WndClass.hInstance = GetModuleHandle (NULL);
1599 WndClass.lpszClassName = "GStreamer-DirectDraw";
1600 WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
1601 WndClass.cbClsExtra = 0;
1602 WndClass.cbWndExtra = 0;
1603 WndClass.lpfnWndProc = WndProc;
1604 WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
1605 RegisterClass (&WndClass);
1607 ddrawsink->video_window = CreateWindowEx (0, "GStreamer-DirectDraw",
1608 "GStreamer-DirectDraw sink default window",
1609 WS_OVERLAPPEDWINDOW | WS_SIZEBOX, 0, 0, 640, 480, NULL, NULL,
1610 WndClass.hInstance, (LPVOID) ddrawsink);
1611 if (ddrawsink->video_window == NULL)
1614 /* Set the clipper on that window */
1615 IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
1617 /* signal application we created a window */
1618 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ddrawsink),
1619 (guintptr) ddrawsink->video_window);
1621 ReleaseSemaphore (ddrawsink->window_created_signal, 1, NULL);
1623 /* start message loop processing our default window messages */
1624 while (GetMessage (&msg, NULL, 0, 0) != FALSE) {
1625 TranslateMessage (&msg);
1626 DispatchMessage (&msg);
1629 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1630 "our window received WM_QUIT or error.");
1631 /* The window could have changed, if it is not ours anymore we don't
1632 * overwrite the current video window with NULL */
1633 if (ddrawsink->our_video_window) {
1634 GST_OBJECT_LOCK (ddrawsink);
1635 ddrawsink->video_window = NULL;
1636 GST_OBJECT_UNLOCK (ddrawsink);
1643 gst_directdraw_sink_create_default_window (GstDirectDrawSink * ddrawsink)
1645 ddrawsink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);
1646 if (ddrawsink->window_created_signal == NULL)
1649 ddrawsink->window_thread = g_thread_create (
1650 (GThreadFunc) gst_directdraw_sink_window_thread, ddrawsink, TRUE, NULL);
1652 if (ddrawsink->window_thread == NULL)
1655 /* wait maximum 10 seconds for windows creating */
1656 if (WaitForSingleObject (ddrawsink->window_created_signal,
1657 10000) != WAIT_OBJECT_0)
1660 CloseHandle (ddrawsink->window_created_signal);
1664 CloseHandle (ddrawsink->window_created_signal);
1665 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1666 ("Error creating our default window"), (NULL));
1672 gst_directdraw_sink_check_primary_surface (GstDirectDrawSink * ddrawsink)
1675 DDSURFACEDESC2 dd_surface_desc;
1678 /* if our primary surface already exist, check if it's not lost */
1679 if (ddrawsink->primary_surface) {
1680 if (IDirectDrawSurface7_IsLost (ddrawsink->primary_surface) == DD_OK) {
1681 /* no problem with our primary surface */
1684 /* our primary surface was lost, try to restore it */
1685 if (IDirectDrawSurface7_Restore (ddrawsink->primary_surface) == DD_OK) {
1686 /* restore is done */
1687 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1688 "Our primary surface" " was restored after lost");
1691 /* failed to restore our primary surface,
1692 * probably because the display mode was changed.
1693 * Release this surface and recreate a new one.
1695 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1696 "Our primary surface"
1697 " was lost and display mode has changed. Destroy and recreate our surface.");
1698 IDirectDrawSurface7_Release (ddrawsink->primary_surface);
1699 ddrawsink->primary_surface = NULL;
1701 /* also release offscreen surface */
1702 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1703 ddrawsink->offscreen_surface = NULL;
1708 /* create our primary surface */
1709 memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
1710 dd_surface_desc.dwSize = sizeof (dd_surface_desc);
1711 dd_surface_desc.dwFlags = DDSD_CAPS;
1712 dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
1713 sd = &dd_surface_desc;
1715 IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
1716 &ddrawsink->primary_surface, NULL);
1717 if (hres != DD_OK) {
1718 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1719 ("Failed to create our primary surface error=%s", DDErrorString (hres)),
1724 /* attach our clipper object to the new primary surface */
1725 if (ddrawsink->clipper) {
1726 hres = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
1727 ddrawsink->clipper);
1734 gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink * ddrawsink)
1736 DDSURFACEDESC2 dd_surface_desc;
1740 /* if our offscreen surface already exist, check if it's not lost */
1741 if (ddrawsink->offscreen_surface) {
1742 if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK) {
1743 /* no problem with our offscreen surface */
1746 /* our offscreen surface was lost, try to restore it */
1747 if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
1748 /* restore is done */
1749 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1750 "Our offscreen surface" " was restored after lost");
1753 /* failed to restore our offscreen surface,
1754 * probably because the display mode was changed.
1755 * Release this surface and recreate a new one.
1757 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1758 "Our offscreen surface"
1759 " was lost and display mode has changed. Destroy and recreate our surface.");
1760 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1761 ddrawsink->offscreen_surface = NULL;
1766 memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
1767 dd_surface_desc.dwSize = sizeof (dd_surface_desc);
1768 dd_surface_desc.dwFlags =
1769 DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
1770 dd_surface_desc.dwHeight = ddrawsink->video_height;
1771 dd_surface_desc.dwWidth = ddrawsink->video_width;
1772 memcpy (&(dd_surface_desc.ddpfPixelFormat), &ddrawsink->dd_pixel_format,
1773 sizeof (DDPIXELFORMAT));
1775 dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1776 sd = &dd_surface_desc;
1778 IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
1779 &ddrawsink->offscreen_surface, NULL);
1780 if (hres != DD_OK) {
1781 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1782 "create_ddraw_surface:CreateSurface (offscreen surface for buffer_pool) failed %s",
1783 DDErrorString (hres));
1787 ddrawsink->must_recreate_offscreen = FALSE;
1793 gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat)
1795 gint order = 0, binary;
1799 dwRBitMask | lpddpfPixelFormat->dwGBitMask | lpddpfPixelFormat->
1800 dwBBitMask | lpddpfPixelFormat->dwRGBAlphaBitMask;
1801 while (binary != 0) {
1802 if ((binary % 2) == 1)
1804 binary = binary >> 1;
1809 static HRESULT WINAPI
1810 EnumModesCallback2 (LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
1812 GstDirectDrawSink *ddrawsink = (GstDirectDrawSink *) lpContext;
1813 GstCaps *format_caps = NULL;
1814 LPDDSURFACEDESC2 sd;
1816 if (!ddrawsink || !lpDDSurfaceDesc)
1817 return DDENUMRET_CANCEL;
1819 sd = (LPDDSURFACEDESC2) lpDDSurfaceDesc;
1820 if ((sd->dwFlags & DDSD_PIXELFORMAT) != DDSD_PIXELFORMAT) {
1821 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1822 "Display mode found with DDSD_PIXELFORMAT not set");
1823 return DDENUMRET_OK;
1826 if ((sd->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1827 return DDENUMRET_OK;
1829 format_caps = gst_directdraw_sink_create_caps_from_surfacedesc (sd);
1832 gst_caps_append (ddrawsink->caps, format_caps);
1835 return DDENUMRET_OK;
1839 gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc)
1841 GstCaps *caps = NULL;
1842 gint endianness = G_LITTLE_ENDIAN;
1845 if ((desc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1848 depth = gst_directdraw_sink_get_depth (&desc->ddpfPixelFormat);
1850 if (desc->ddpfPixelFormat.dwRGBBitCount == 24 ||
1851 desc->ddpfPixelFormat.dwRGBBitCount == 32) {
1852 /* ffmpegcolorspace handles 24/32 bpp RGB as big-endian. */
1853 endianness = G_BIG_ENDIAN;
1854 desc->ddpfPixelFormat.dwRBitMask =
1855 GUINT32_TO_BE (desc->ddpfPixelFormat.dwRBitMask);
1856 desc->ddpfPixelFormat.dwGBitMask =
1857 GUINT32_TO_BE (desc->ddpfPixelFormat.dwGBitMask);
1858 desc->ddpfPixelFormat.dwBBitMask =
1859 GUINT32_TO_BE (desc->ddpfPixelFormat.dwBBitMask);
1860 if (desc->ddpfPixelFormat.dwRGBBitCount == 24) {
1861 desc->ddpfPixelFormat.dwRBitMask >>= 8;
1862 desc->ddpfPixelFormat.dwGBitMask >>= 8;
1863 desc->ddpfPixelFormat.dwBBitMask >>= 8;
1867 caps = gst_caps_new_simple ("video/x-raw-rgb",
1868 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1869 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1870 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
1871 "bpp", G_TYPE_INT, desc->ddpfPixelFormat.dwRGBBitCount,
1872 "depth", G_TYPE_INT, depth,
1873 "endianness", G_TYPE_INT, endianness,
1874 "red_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwRBitMask,
1875 "green_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwGBitMask,
1876 "blue_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwBBitMask, NULL);
1882 gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
1884 HRESULT hRes = S_OK;
1885 DDCAPS ddcaps_hardware;
1886 DDCAPS ddcaps_emulation;
1887 GstCaps *format_caps = NULL;
1889 ddrawsink->caps = gst_caps_new_empty ();
1890 if (!ddrawsink->caps)
1893 /* get hardware caps */
1894 ddcaps_hardware.dwSize = sizeof (DDCAPS);
1895 ddcaps_emulation.dwSize = sizeof (DDCAPS);
1896 IDirectDraw7_GetCaps (ddrawsink->ddraw_object, &ddcaps_hardware,
1899 /* we don't test for DDCAPS_BLTSTRETCH on the hardware as the directdraw
1900 * emulation layer can do it */
1901 if (!(ddcaps_hardware.dwCaps & DDCAPS_BLTFOURCC)) {
1902 DDSURFACEDESC2 surface_desc;
1905 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1906 "hardware doesn't support blit from one colorspace to another one. "
1907 "so we will create a caps with only the current display mode");
1909 /* save blit caps */
1910 ddrawsink->can_blit_between_colorspace = FALSE;
1912 surface_desc.dwSize = sizeof (surface_desc);
1915 IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
1916 (DDSURFACEDESC *) sd);
1917 if (hRes != DD_OK) {
1918 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1919 ("Error getting the current display mode error=%s",
1920 DDErrorString (hRes)), (NULL));
1925 gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
1927 gst_caps_append (ddrawsink->caps, format_caps);
1930 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
1931 gst_caps_to_string (ddrawsink->caps));
1932 return ddrawsink->caps;
1935 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1936 "the hardware can blit from one colorspace to another, "
1937 "then enumerate the colorspace supported by the hardware");
1939 /* save blit caps */
1940 ddrawsink->can_blit_between_colorspace = TRUE;
1942 /* enumerate display modes exposed by directdraw object
1943 to know supported RGB modes */
1945 IDirectDraw7_EnumDisplayModes (ddrawsink->ddraw_object,
1946 DDEDM_REFRESHRATES, NULL, ddrawsink, EnumModesCallback2);
1947 if (hRes != DD_OK) {
1948 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1949 ("Error enumerating display modes error=%s", DDErrorString (hRes)),
1955 if (gst_caps_is_empty (ddrawsink->caps)) {
1956 gst_caps_unref (ddrawsink->caps);
1957 ddrawsink->caps = NULL;
1958 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1959 ("No supported caps found."), (NULL));
1963 /*GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
1964 * gst_caps_to_string (ddrawsink->caps)); */
1966 return ddrawsink->caps;
1969 /* Creates miniobject and our internal surface */
1970 static GstDDrawSurface *
1971 gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
1972 GstCaps * caps, size_t size)
1974 GstDDrawSurface *surface = NULL;
1975 GstStructure *structure = NULL;
1981 DDSURFACEDESC2 surf_desc, surf_lock_desc;
1983 g_return_val_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink), NULL);
1985 /*init structures */
1986 memset (&surf_desc, 0, sizeof (surf_desc));
1987 memset (&surf_lock_desc, 0, sizeof (surf_desc));
1988 surf_desc.dwSize = sizeof (surf_desc);
1989 surf_lock_desc.dwSize = sizeof (surf_lock_desc);
1991 /*create miniobject and initialize it */
1992 surface = (GstDDrawSurface *) gst_mini_object_new (GST_TYPE_DDRAWSURFACE);
1993 surface->locked = FALSE;
1995 structure = gst_caps_get_structure (caps, 0);
1996 if (!gst_structure_get_int (structure, "width", &surface->width) ||
1997 !gst_structure_get_int (structure, "height", &surface->height)) {
1998 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1999 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
2002 pitch = GST_ROUND_UP_8 (size / surface->height);
2003 if (!gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
2004 &surface->dd_pixel_format)) {
2005 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
2006 "failed getting pixel format from caps %" GST_PTR_FORMAT, caps);
2009 /* disable return of directdraw surface to buffer alloc because actually I
2010 * have no solution to handle display mode changes. The problem is that when
2011 * the display mode is changed surface's memory is freed then the upstream
2012 * filter would crash trying to write to this memory. Directdraw has a system
2013 * lock (DDLOCK_NOSYSLOCK to disable it) to prevent display mode changes
2014 * when a surface memory is locked but we need to disable this lock to return
2015 * multiple buffers (surfaces) and do not lock directdraw API calls.
2018 /* if (ddrawsink->ddraw_object) {*/
2019 /* Creating an internal surface which will be used as GstBuffer, we used
2020 the detected pixel format and video dimensions */
2022 surf_desc.ddsCaps.dwCaps =
2023 DDSCAPS_OFFSCREENPLAIN /* | DDSCAPS_SYSTEMMEMORY */ ;
2025 DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH;
2026 surf_desc.dwHeight = surface->height;
2027 surf_desc.dwWidth = surface->width;
2028 memcpy (&(surf_desc.ddpfPixelFormat), &surface->dd_pixel_format,
2029 sizeof (DDPIXELFORMAT));
2031 hRes = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &surf_desc,
2032 &surface->surface, NULL);
2033 if (hRes != DD_OK) {
2034 goto surface_pitch_bad;
2037 /* Locking the surface to acquire the memory pointer.
2038 Use DDLOCK_NOSYSLOCK to disable syslock which can cause a deadlock
2039 if directdraw api is used while a buffer is lock */
2041 hRes = IDirectDrawSurface7_Lock (surface->surface, NULL, &surf_lock_desc,
2042 DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
2043 if (hRes == DDERR_SURFACELOST) {
2044 IDirectDrawSurface7_Restore (surface->surface);
2047 surface->locked = TRUE;
2049 if (surf_lock_desc.lPitch != pitch) {
2050 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
2051 "DDraw stride/pitch %ld isn't as expected value %d, let's continue allocating a system memory buffer.",
2052 surf_lock_desc.lPitch, pitch);
2054 /*Unlock the surface as we will change it to use system memory with a GStreamer compatible pitch */
2055 hRes = IDirectDrawSurface_Unlock (surface->surface, NULL);
2056 goto surface_pitch_bad;
2058 GST_BUFFER_DATA (surface) = surf_lock_desc.lpSurface;
2059 GST_BUFFER_SIZE (surface) = surf_lock_desc.lPitch * surface->height;
2060 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
2061 "allocating a surface of %d bytes (stride=%ld)\n", size,
2062 surf_lock_desc.lPitch);
2066 GST_BUFFER (surface)->malloc_data = g_malloc (size);
2067 GST_BUFFER_DATA (surface) = GST_BUFFER (surface)->malloc_data;
2068 GST_BUFFER_SIZE (surface) = size;
2069 surface->surface = NULL;
2070 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
2071 "allocating a system memory buffer of %" G_GSIZE_FORMAT " bytes", size);
2075 /* Keep a ref to our sink */
2076 surface->ddrawsink = gst_object_ref (ddrawsink);
2081 /* We are called from the finalize method of miniobject, the object will be
2082 * destroyed so we just have to clean our internal stuff */
2084 gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
2085 GstDDrawSurface * surface)
2087 g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
2089 /* Release our internal surface */
2090 if (surface->surface) {
2091 if (surface->locked) {
2092 IDirectDrawSurface7_Unlock (surface->surface, NULL);
2093 surface->locked = FALSE;
2095 IDirectDrawSurface7_Release (surface->surface);
2096 surface->surface = NULL;
2099 if (GST_BUFFER (surface)->malloc_data) {
2100 g_free (GST_BUFFER (surface)->malloc_data);
2101 GST_BUFFER (surface)->malloc_data = NULL;
2104 if (!surface->ddrawsink) {
2108 /* Release the ref to our sink */
2109 surface->ddrawsink = NULL;
2110 gst_object_unref (ddrawsink);
2115 GST_WARNING ("no sink found in surface");
2120 gst_directdraw_sink_surface_check (GstDirectDrawSink * ddrawsink,
2121 GstDDrawSurface * surface)
2123 if (!surface->surface)
2124 return TRUE; /* system memory buffer */
2126 if (IDirectDrawSurface7_IsLost (surface->surface) == DD_OK) {
2127 /* no problem with this surface */
2130 /* this surface was lost, try to restore it */
2131 if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
2132 /* restore is done */
2133 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink, "A surface from our"
2134 " bufferpool was restored after lost");
2143 gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink * ddrawsink)
2145 g_mutex_lock (ddrawsink->pool_lock);
2146 while (ddrawsink->buffer_pool) {
2147 GstDDrawSurface *surface = ddrawsink->buffer_pool->data;
2149 ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
2150 ddrawsink->buffer_pool);
2151 gst_directdraw_sink_surface_destroy (ddrawsink, surface);
2152 gst_buffer_unref (GST_BUFFER_CAST (surface));
2154 g_mutex_unlock (ddrawsink->pool_lock);
2158 gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink)
2160 /* Post quit message and wait for our event window thread */
2161 if (ddrawsink->video_window && ddrawsink->our_video_window)
2162 PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
2164 if (ddrawsink->window_thread) {
2165 g_thread_join (ddrawsink->window_thread);
2166 ddrawsink->window_thread = NULL;
2169 if (ddrawsink->buffer_pool) {
2170 gst_directdraw_sink_bufferpool_clear (ddrawsink);
2171 ddrawsink->buffer_pool = NULL;
2174 if (ddrawsink->offscreen_surface) {
2175 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
2176 ddrawsink->offscreen_surface = NULL;
2179 if (ddrawsink->clipper) {
2180 IDirectDrawClipper_Release (ddrawsink->clipper);
2181 ddrawsink->clipper = NULL;
2184 if (ddrawsink->primary_surface) {
2185 IDirectDrawSurface7_Release (ddrawsink->primary_surface);
2186 ddrawsink->primary_surface = NULL;
2189 if (ddrawsink->ddraw_object) {
2190 IDirectDraw7_Release (ddrawsink->ddraw_object);
2191 ddrawsink->ddraw_object = NULL;
2194 if (ddrawsink->last_buffer) {
2195 gst_buffer_unref (ddrawsink->last_buffer);
2196 ddrawsink->last_buffer = NULL;
2199 ddrawsink->setup = FALSE;