2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
5 * Based on directfb video sink
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
23 * The development of this code was made possible due to the involvement
24 * of Pioneers of the Inevitable, the creators of the Songbird Music player
29 * SECTION:element-directdrawsink
33 * DirectdrawSink renders video RGB frames to any win32 window. This element
34 * can receive a window ID from the application through the XOverlay interface
35 * and will then render video frames in this window.
36 * If no Window ID was provided by the application, the element will create its
37 * own internal window and render into it.
39 * <title>Examples</title>
41 * Here is a simple pipeline to test the sink :
43 * gst-launch-0.10 -v videotestsrc ! directdrawsink
53 #include "gstdirectdrawsink.h"
55 GST_DEBUG_CATEGORY_STATIC (directdrawsink_debug);
56 #define GST_CAT_DEFAULT directdrawsink_debug
58 /* elementfactory information */
59 static const GstElementDetails gst_directdraw_sink_details =
60 GST_ELEMENT_DETAILS ("Direct Draw Video Sink",
62 "Output to a video card via Direct Draw",
63 "Sebastien Moutte <sebastien@moutte.net>");
65 static void gst_directdraw_sink_init_interfaces (GType type);
67 GST_BOILERPLATE_FULL (GstDirectDrawSink, gst_directdraw_sink, GstVideoSink,
68 GST_TYPE_VIDEO_SINK, gst_directdraw_sink_init_interfaces);
70 static void gst_directdraw_sink_finalize (GObject * object);
71 static void gst_directdraw_sink_set_property (GObject * object,
72 guint prop_id, const GValue * value, GParamSpec * pspec);
73 static void gst_directdraw_sink_get_property (GObject * object,
74 guint prop_id, GValue * value, GParamSpec * pspec);
75 static GstCaps *gst_directdraw_sink_get_caps (GstBaseSink * bsink);
76 static gboolean gst_directdraw_sink_set_caps (GstBaseSink * bsink,
78 static GstStateChangeReturn gst_directdraw_sink_change_state (GstElement *
79 element, GstStateChange transition);
80 static GstFlowReturn gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink,
81 guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
82 static void gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
83 GstClockTime * start, GstClockTime * end);
84 static GstFlowReturn gst_directdraw_sink_show_frame (GstBaseSink * bsink,
88 static gboolean gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink);
89 static gboolean gst_directdraw_sink_create_default_window (GstDirectDrawSink *
91 static gboolean gst_directdraw_sink_check_primary_surface (GstDirectDrawSink *
93 static gboolean gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink *
95 static GstCaps *gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink *
98 * gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc);
99 static void gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink);
100 static void gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink *
102 static int gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat);
103 static gboolean gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink *
104 ddrawsink, GstCaps * caps, DDPIXELFORMAT * pPixelFormat);
105 static void gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink,
106 RECT src, RECT dst, RECT * result);
107 char *DDErrorString (HRESULT hr);
109 /* surfaces management functions */
110 static void gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
111 GstDDrawSurface * surface);
112 static GstDDrawSurface *gst_directdraw_sink_surface_create (GstDirectDrawSink *
113 ddrawsink, GstCaps * caps, size_t size);
114 static gboolean gst_directdraw_sink_surface_check (GstDirectDrawSink *
115 ddrawsink, GstDDrawSurface * surface);
117 static GstStaticPadTemplate directdrawsink_sink_factory =
118 GST_STATIC_PAD_TEMPLATE ("sink",
121 GST_STATIC_CAPS ("video/x-raw-rgb, "
122 "framerate = (fraction) [ 0, MAX ], "
123 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
129 PROP_KEEP_ASPECT_RATIO
132 /* XOverlay interface implementation */
134 gst_directdraw_sink_interface_supported (GstImplementsInterface * iface,
137 g_assert (type == GST_TYPE_X_OVERLAY);
142 gst_directdraw_sink_interface_init (GstImplementsInterfaceClass * klass)
144 klass->supported = gst_directdraw_sink_interface_supported;
148 gst_directdraw_sink_set_window_id (GstXOverlay * overlay, ULONG window_id)
150 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
152 /* check if we are already using this window id */
153 if (ddrawsink->video_window == (HWND) window_id)
159 ddrawsink->video_window = (HWND) window_id;
160 ddrawsink->our_video_window = FALSE;
161 if (ddrawsink->setup) {
162 /* update the clipper object with the new window */
163 hres = IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0,
164 ddrawsink->video_window);
170 gst_directdraw_sink_expose (GstXOverlay * overlay)
172 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
174 gst_directdraw_sink_show_frame (GST_BASE_SINK (ddrawsink), NULL);
178 gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
180 iface->set_xwindow_id = gst_directdraw_sink_set_window_id;
181 iface->expose = gst_directdraw_sink_expose;
185 gst_directdraw_sink_init_interfaces (GType type)
187 static const GInterfaceInfo iface_info = {
188 (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
193 static const GInterfaceInfo xoverlay_info = {
194 (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_interface_init,
199 g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
201 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
204 /* Subclass of GstBuffer which manages buffer_pool surfaces lifetime */
205 static void gst_ddrawsurface_finalize (GstMiniObject * mini_object);
208 gst_ddrawsurface_init (GstDDrawSurface * surface, gpointer g_class)
210 surface->surface = NULL;
213 surface->ddrawsink = NULL;
214 surface->locked = FALSE;
215 surface->system_memory = FALSE;
216 memset (&surface->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
220 gst_ddrawsurface_class_init (gpointer g_class, gpointer class_data)
222 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
224 mini_object_class->finalize = GST_DEBUG_FUNCPTR (gst_ddrawsurface_finalize);
228 gst_ddrawsurface_get_type (void)
230 static GType _gst_ddrawsurface_type;
232 if (G_UNLIKELY (_gst_ddrawsurface_type == 0)) {
233 static const GTypeInfo ddrawsurface_info = {
234 sizeof (GstBufferClass),
237 gst_ddrawsurface_class_init,
240 sizeof (GstDDrawSurface),
242 (GInstanceInitFunc) gst_ddrawsurface_init,
245 _gst_ddrawsurface_type = g_type_register_static (GST_TYPE_BUFFER,
246 "GstDDrawSurface", &ddrawsurface_info, 0);
248 return _gst_ddrawsurface_type;
252 gst_ddrawsurface_finalize (GstMiniObject * mini_object)
254 GstDirectDrawSink *ddrawsink = NULL;
255 GstDDrawSurface *surface;
257 surface = (GstDDrawSurface *) mini_object;
259 ddrawsink = surface->ddrawsink;
263 /* If our geometry changed we can't reuse that image. */
264 if ((surface->width != ddrawsink->video_width) ||
265 (surface->height != ddrawsink->video_height) ||
266 (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
267 sizeof (DDPIXELFORMAT)) != 0 ||
268 !gst_directdraw_sink_surface_check (ddrawsink, surface))
270 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
271 "destroy image as its size changed %dx%d vs current %dx%d",
272 surface->width, surface->height, ddrawsink->video_width,
273 ddrawsink->video_height);
274 gst_directdraw_sink_surface_destroy (ddrawsink, surface);
277 /* In that case we can reuse the image and add it to our image pool. */
278 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
279 "recycling image in pool");
281 /* need to increment the refcount again to recycle */
282 gst_buffer_ref (GST_BUFFER (surface));
284 g_mutex_lock (ddrawsink->pool_lock);
285 ddrawsink->buffer_pool = g_slist_prepend (ddrawsink->buffer_pool, surface);
286 g_mutex_unlock (ddrawsink->pool_lock);
291 GST_CAT_WARNING (directdrawsink_debug, "no sink found");
295 /************************************************************************/
296 /* Directdraw sink functions */
297 /************************************************************************/
299 gst_directdraw_sink_base_init (gpointer g_class)
301 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
303 gst_element_class_set_details (element_class, &gst_directdraw_sink_details);
304 gst_element_class_add_pad_template (element_class,
305 gst_static_pad_template_get (&directdrawsink_sink_factory));
309 gst_directdraw_sink_class_init (GstDirectDrawSinkClass * klass)
311 GObjectClass *gobject_class;
312 GstElementClass *gstelement_class;
313 GstBaseSinkClass *gstbasesink_class;
315 gobject_class = (GObjectClass *) klass;
316 gstbasesink_class = (GstBaseSinkClass *) klass;
317 gstelement_class = (GstElementClass *) klass;
319 GST_DEBUG_CATEGORY_INIT (directdrawsink_debug, "directdrawsink", 0,
322 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directdraw_sink_finalize);
323 gobject_class->get_property =
324 GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_property);
325 gobject_class->set_property =
326 GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_property);
327 gstelement_class->change_state =
328 GST_DEBUG_FUNCPTR (gst_directdraw_sink_change_state);
329 gstbasesink_class->get_caps =
330 GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_caps);
331 gstbasesink_class->set_caps =
332 GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_caps);
333 gstbasesink_class->preroll =
334 GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
335 gstbasesink_class->render =
336 GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
337 gstbasesink_class->get_times =
338 GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_times);
339 gstbasesink_class->buffer_alloc =
340 GST_DEBUG_FUNCPTR (gst_directdraw_sink_buffer_alloc);
342 /* install properties */
343 /* setup aspect ratio mode */
344 g_object_class_install_property (G_OBJECT_CLASS (klass),
345 PROP_KEEP_ASPECT_RATIO, g_param_spec_boolean ("keep-aspect-ratio",
346 "keep-aspect-ratio", "keep the aspect ratio or not", FALSE,
351 gst_directdraw_sink_set_property (GObject * object, guint prop_id,
352 const GValue * value, GParamSpec * pspec)
354 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
357 case PROP_KEEP_ASPECT_RATIO:
358 ddrawsink->keep_aspect_ratio = g_value_get_boolean (value);
361 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367 gst_directdraw_sink_get_property (GObject * object, guint prop_id,
368 GValue * value, GParamSpec * pspec)
370 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
373 case PROP_KEEP_ASPECT_RATIO:
374 g_value_set_boolean (value, ddrawsink->keep_aspect_ratio);
377 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
383 gst_directdraw_sink_finalize (GObject * object)
385 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
387 if (ddrawsink->pool_lock) {
388 g_mutex_free (ddrawsink->pool_lock);
389 ddrawsink->pool_lock = NULL;
391 if (ddrawsink->caps) {
392 gst_caps_unref (ddrawsink->caps);
393 ddrawsink->caps = NULL;
395 if (ddrawsink->setup) {
396 gst_directdraw_sink_cleanup (ddrawsink);
401 gst_directdraw_sink_init (GstDirectDrawSink * ddrawsink,
402 GstDirectDrawSinkClass * g_class)
404 /*init members variables */
405 ddrawsink->ddraw_object = NULL;
406 ddrawsink->primary_surface = NULL;
407 ddrawsink->offscreen_surface = NULL;
408 ddrawsink->clipper = NULL;
409 ddrawsink->video_window = NULL;
410 ddrawsink->our_video_window = TRUE;
411 ddrawsink->last_buffer = NULL;
412 ddrawsink->caps = NULL;
413 ddrawsink->window_thread = NULL;
414 ddrawsink->setup = FALSE;
415 ddrawsink->buffer_pool = NULL;
416 ddrawsink->keep_aspect_ratio = FALSE;
417 ddrawsink->pool_lock = g_mutex_new ();
418 ddrawsink->can_blit_between_colorspace = TRUE;
419 ddrawsink->must_recreate_offscreen = FALSE;
420 memset (&ddrawsink->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
422 /*video default values */
423 ddrawsink->video_height = 0;
424 ddrawsink->video_width = 0;
425 ddrawsink->fps_n = 0;
426 ddrawsink->fps_d = 0;
430 gst_directdraw_sink_get_caps (GstBaseSink * bsink)
432 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
433 GstCaps *caps = NULL;
435 if (!ddrawsink->setup) {
436 caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
438 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
439 "getcaps called and we are not setup yet, " "returning template %"
440 GST_PTR_FORMAT, caps);
442 caps = gst_caps_ref (ddrawsink->caps);
449 gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
451 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
452 GstStructure *structure = NULL;
456 structure = gst_caps_get_structure (caps, 0);
460 ret = gst_structure_get_int (structure, "width", &ddrawsink->video_width);
461 ret &= gst_structure_get_int (structure, "height", &ddrawsink->video_height);
462 fps = gst_structure_get_value (structure, "framerate");
463 ret &= (fps != NULL);
465 gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
466 &ddrawsink->dd_pixel_format);
468 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
469 ("Failed to get caps properties from caps"), (NULL));
473 ddrawsink->fps_n = gst_value_get_fraction_numerator (fps);
474 ddrawsink->fps_d = gst_value_get_fraction_denominator (fps);
476 /* Notify application to set window id now */
477 if (!ddrawsink->video_window) {
478 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ddrawsink));
481 /* if we are rendering to our own window, resize it to video size */
482 if (ddrawsink->video_window && ddrawsink->our_video_window) {
483 SetWindowPos (ddrawsink->video_window, NULL,
484 0, 0, ddrawsink->video_width + (GetSystemMetrics (SM_CXSIZEFRAME) * 2),
485 ddrawsink->video_height + GetSystemMetrics (SM_CYCAPTION) +
486 (GetSystemMetrics (SM_CYSIZEFRAME) * 2), SWP_SHOWWINDOW | SWP_NOMOVE);
489 /* create an offscreen surface with the caps */
490 ret = gst_directdraw_sink_check_offscreen_surface (ddrawsink);
492 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
493 ("Can't create a directdraw offscreen surface with the input caps"),
500 static GstStateChangeReturn
501 gst_directdraw_sink_change_state (GstElement * element,
502 GstStateChange transition)
504 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);;
506 switch (transition) {
507 case GST_STATE_CHANGE_NULL_TO_READY:
508 if (ddrawsink->video_window == NULL)
509 if (!gst_directdraw_sink_create_default_window (ddrawsink))
510 return GST_STATE_CHANGE_FAILURE;
512 if (!gst_directdraw_sink_setup_ddraw (ddrawsink))
513 return GST_STATE_CHANGE_FAILURE;
515 if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink)))
516 return GST_STATE_CHANGE_FAILURE;
518 case GST_STATE_CHANGE_READY_TO_PAUSED:
520 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
522 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
524 case GST_STATE_CHANGE_PAUSED_TO_READY:
525 ddrawsink->fps_n = 0;
526 ddrawsink->fps_d = 1;
527 ddrawsink->video_width = 0;
528 ddrawsink->video_height = 0;
529 if (ddrawsink->buffer_pool)
530 gst_directdraw_sink_bufferpool_clear (ddrawsink);
532 case GST_STATE_CHANGE_READY_TO_NULL:
533 if (ddrawsink->setup)
534 gst_directdraw_sink_cleanup (ddrawsink);
538 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
542 gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
543 guint size, GstCaps * caps, GstBuffer ** buf)
545 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
546 GstDDrawSurface *surface = NULL;
547 GstFlowReturn ret = GST_FLOW_OK;
548 GstCaps *buffer_caps = caps;
549 gboolean buffercaps_unref = FALSE;
551 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
552 "a buffer of %d bytes was requested", size);
554 g_mutex_lock (ddrawsink->pool_lock);
556 /* Inspect our buffer pool */
557 while (ddrawsink->buffer_pool) {
558 surface = (GstDDrawSurface *) ddrawsink->buffer_pool->data;
560 /* Removing from the pool */
561 ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
562 ddrawsink->buffer_pool);
564 /* If the surface is invalid for our need, destroy */
565 if ((surface->width != ddrawsink->video_width) ||
566 (surface->height != ddrawsink->video_height) ||
567 (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
568 sizeof (DDPIXELFORMAT)) ||
569 !gst_directdraw_sink_surface_check (ddrawsink, surface))
571 gst_directdraw_sink_surface_destroy (ddrawsink, surface);
574 /* We found a suitable surface */
580 if (!ddrawsink->can_blit_between_colorspace) {
581 /* Hardware doesn't support blit from one colorspace to another.
582 * Check if the colorspace of the current display mode has changed since
583 * the last negociation. If it's the case, we will have to renegociate
587 DDSURFACEDESC2 surface_desc;
589 GstStructure *structure = NULL;
591 structure = gst_caps_get_structure (caps, 0);
592 if (!gst_structure_get_int (structure, "depth", &depth)) {
593 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
594 "Can't get depth from buffer_alloc caps");
595 return GST_FLOW_ERROR;
597 surface_desc.dwSize = sizeof (surface_desc);
600 IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
601 (DDSURFACEDESC *) sd);
603 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
604 "Can't get current display mode (error=%ld)", (glong) hres);
605 return GST_FLOW_ERROR;
608 if (depth != gst_directdraw_sink_get_depth (&surface_desc.ddpfPixelFormat)) {
609 GstCaps *copy_caps = NULL;
610 GstStructure *copy_structure = NULL;
611 GstCaps *display_caps = NULL;
612 GstStructure *display_structure = NULL;
614 /* make a copy of the original caps */
615 copy_caps = gst_caps_copy (caps);
616 copy_structure = gst_caps_get_structure (copy_caps, 0);
619 gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
621 display_structure = gst_caps_get_structure (display_caps, 0);
622 if (display_structure) {
623 gint bpp, endianness, red_mask, green_mask, blue_mask;
625 /* get new display mode properties */
626 gst_structure_get_int (display_structure, "depth", &depth);
627 gst_structure_get_int (display_structure, "bpp", &bpp);
628 gst_structure_get_int (display_structure, "endianness", &endianness);
629 gst_structure_get_int (display_structure, "red_mask", &red_mask);
630 gst_structure_get_int (display_structure, "green_mask", &green_mask);
631 gst_structure_get_int (display_structure, "blue_mask", &blue_mask);
633 /* apply the new display mode changes to the previous caps */
634 gst_structure_set (copy_structure,
635 "bpp", G_TYPE_INT, bpp,
636 "depth", G_TYPE_INT, depth,
637 "endianness", G_TYPE_INT, endianness,
638 "red_mask", G_TYPE_INT, red_mask,
639 "green_mask", G_TYPE_INT, green_mask,
640 "blue_mask", G_TYPE_INT, blue_mask, NULL);
642 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ddrawsink),
644 buffer_caps = copy_caps;
645 buffercaps_unref = TRUE;
646 /* update buffer size needed to store video frames according to new caps */
647 size = ddrawsink->video_width * ddrawsink->video_height * (bpp / 8);
649 /* update our member pixel format */
650 gst_ddrawvideosink_get_format_from_caps (ddrawsink, buffer_caps,
651 &ddrawsink->dd_pixel_format);
652 ddrawsink->must_recreate_offscreen = TRUE;
654 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
655 " desired caps %s \n\n new caps %s", gst_caps_to_string (caps),
656 gst_caps_to_string (buffer_caps));
658 GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
659 "peer refused caps re-negociation "
660 "and we can't render with the current caps.");
661 ret = GST_FLOW_ERROR;
664 gst_caps_unref (display_caps);
667 if (!buffercaps_unref)
668 gst_caps_unref (copy_caps);
672 /* We haven't found anything, creating a new one */
674 surface = gst_directdraw_sink_surface_create (ddrawsink, buffer_caps, size);
677 /* Now we should have a surface, set appropriate caps on it */
679 gst_buffer_set_caps (GST_BUFFER (surface), buffer_caps);
682 g_mutex_unlock (ddrawsink->pool_lock);
684 *buf = GST_BUFFER (surface);
686 if (buffercaps_unref)
687 gst_caps_unref (buffer_caps);
693 gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
695 GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
697 RECT destsurf_rect, src_rect;
698 POINT dest_surf_point;
701 /* save a reference to the input buffer */
702 if (ddrawsink->last_buffer != buf) {
703 if (ddrawsink->last_buffer) {
704 gst_buffer_unref (ddrawsink->last_buffer);
707 ddrawsink->last_buffer = gst_buffer_ref (buf);
709 /* use last buffer */
710 buf = ddrawsink->last_buffer;
713 return GST_FLOW_ERROR;
715 /* get the video window position */
716 dest_surf_point.x = 0;
717 dest_surf_point.y = 0;
718 ClientToScreen (ddrawsink->video_window, &dest_surf_point);
719 GetClientRect (ddrawsink->video_window, &destsurf_rect);
720 OffsetRect (&destsurf_rect, dest_surf_point.x, dest_surf_point.y);
722 if (ddrawsink->keep_aspect_ratio) {
723 /* center image to dest image keeping aspect ratio */
726 src_rect.bottom = ddrawsink->video_height;
727 src_rect.right = ddrawsink->video_width;
728 gst_directdraw_sink_center_rect (ddrawsink, src_rect, destsurf_rect,
732 if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
733 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
734 ddrawsink->offscreen_surface = NULL;
737 /* check for surfaces lost */
738 if (!gst_directdraw_sink_check_primary_surface (ddrawsink) ||
739 !gst_directdraw_sink_check_offscreen_surface (ddrawsink)) {
740 return GST_FLOW_ERROR;
743 if (!GST_IS_DDRAWSURFACE (buf) ||
744 ((GST_IS_DDRAWSURFACE (buf)) && (GST_BUFFER (buf)->malloc_data))) {
745 /* We are receiving a system memory buffer so we will copy
746 to the memory of our offscreen surface and next blit this surface
747 on the primary surface */
749 guint src_pitch, line;
750 DDSURFACEDESC2 surf_desc;
753 ZeroMemory (&surf_desc, sizeof (surf_desc));
754 surf_desc.dwSize = sizeof (surf_desc);
757 /* Lock the surface */
759 IDirectDrawSurface7_Lock (ddrawsink->offscreen_surface, NULL,
760 (DDSURFACEDESC *) sd, DDLOCK_WAIT, NULL);
762 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
763 "gst_directdraw_sink_show_frame failed locking surface %s",
764 DDErrorString (hRes));
765 return GST_FLOW_ERROR;
768 /* Write each line respecting the destination surface pitch */
769 data = surf_desc.lpSurface;
770 src_pitch = GST_BUFFER_SIZE (buf) / ddrawsink->video_height;
771 for (line = 0; line < surf_desc.dwHeight; line++) {
772 memcpy (data, GST_BUFFER_DATA (buf) + (line * src_pitch), src_pitch);
773 data += surf_desc.lPitch;
776 /* Unlock the surface */
777 hRes = IDirectDrawSurface7_Unlock (ddrawsink->offscreen_surface, NULL);
779 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
780 "gst_directdraw_sink_show_frame failed unlocking surface %s",
781 DDErrorString (hRes));
782 return GST_FLOW_ERROR;
785 /* blit to primary surface ( Blt will scale the video the dest rect surface if needed */
786 hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
787 ddrawsink->offscreen_surface, NULL, DDBLT_WAIT, NULL);
789 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
790 "IDirectDrawSurface7_Blt (object's offscreen surface) " "returned %s",
791 DDErrorString (hRes));
794 /* We are receiving a directdraw surface (previously returned by our buffer pool
795 So we will simply blit it on the primary surface */
796 GstDDrawSurface *surface = NULL;
798 surface = GST_DDRAWSURFACE (buf);
800 /* Unlocking surface before blit */
801 IDirectDrawSurface7_Unlock (surface->surface, NULL);
802 surface->locked = FALSE;
804 /* blit to our primary surface */
805 hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
806 surface->surface, NULL, DDBLT_WAIT, NULL);
808 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
809 "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
810 "returned %s", DDErrorString (hRes));
817 gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
818 GstClockTime * start, GstClockTime * end)
820 GstDirectDrawSink *ddrawsink;
822 ddrawsink = GST_DIRECTDRAW_SINK (bsink);
824 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
825 *start = GST_BUFFER_TIMESTAMP (buf);
826 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
827 *end = *start + GST_BUFFER_DURATION (buf);
829 if (ddrawsink->fps_n > 0) {
830 *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
836 /* Utility functions */
838 /* this function fill a DDPIXELFORMAT using Gstreamer caps */
840 gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
841 GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
843 GstStructure *structure = NULL;
847 g_return_val_if_fail (pPixelFormat, FALSE);
848 g_return_val_if_fail (caps, FALSE);
851 memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
852 pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
854 if (!(structure = gst_caps_get_structure (caps, 0))) {
855 GST_CAT_ERROR_OBJECT (directdrawsink_debug, ddrawsink,
856 "can't get structure pointer from caps");
860 if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
861 gint depth, bitcount, bitmask, endianness;
863 pPixelFormat->dwFlags = DDPF_RGB;
864 ret &= gst_structure_get_int (structure, "bpp", &bitcount);
865 pPixelFormat->dwRGBBitCount = bitcount;
866 ret &= gst_structure_get_int (structure, "depth", &depth);
867 ret &= gst_structure_get_int (structure, "red_mask", &bitmask);
868 pPixelFormat->dwRBitMask = bitmask;
869 ret &= gst_structure_get_int (structure, "green_mask", &bitmask);
870 pPixelFormat->dwGBitMask = bitmask;
871 ret &= gst_structure_get_int (structure, "blue_mask", &bitmask);
872 pPixelFormat->dwBBitMask = bitmask;
874 gst_structure_get_int (structure, "endianness", &endianness);
875 if (endianness == G_BIG_ENDIAN) {
876 endianness = G_LITTLE_ENDIAN;
877 pPixelFormat->dwRBitMask = GUINT32_TO_BE (pPixelFormat->dwRBitMask);
878 pPixelFormat->dwGBitMask = GUINT32_TO_BE (pPixelFormat->dwGBitMask);
879 pPixelFormat->dwBBitMask = GUINT32_TO_BE (pPixelFormat->dwBBitMask);
881 } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
884 pPixelFormat->dwFlags = DDPF_FOURCC;
885 ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
886 pPixelFormat->dwFourCC = fourcc;
888 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
889 "unknown caps name received %" GST_PTR_FORMAT, caps);
896 /* This function centers the RECT of source surface to
897 a dest surface and set the result RECT into result */
899 gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
900 RECT dst, RECT * result)
902 gdouble src_ratio, dst_ratio;
903 long src_width = src.right;
904 long src_height = src.bottom;
905 long dst_width = dst.right - dst.left;
906 long dst_heigth = dst.bottom - dst.top;
907 long result_width = 0, result_height = 0;
909 g_return_if_fail (result != NULL);
911 src_ratio = (gdouble) src_width / src_height;
912 dst_ratio = (gdouble) dst_width / dst_heigth;
914 if (src_ratio > dst_ratio) {
916 result_height = (long) (dst_width / src_ratio);
918 result->left = dst.left;
919 result->right = dst.right;
920 result->top = dst.top + (dst_heigth - result_height) / 2;
921 result->bottom = result->top + result_height;
923 } else if (src_ratio < dst_ratio) {
925 result_width = (long) (dst_heigth * src_ratio);
927 result->top = dst.top;
928 result->bottom = dst.bottom;
929 result->left = dst.left + (dst_width - result_width) / 2;
930 result->right = result->left + result_width;
934 memcpy (result, &dst, sizeof (RECT));
937 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
938 "source is %ldx%ld dest is %ldx%ld, result is %ldx%ld with x,y %ldx%ld",
939 src_width, src_height, dst_width, dst_heigth,
940 result->right - result->left, result->bottom - result->top, result->left,
945 * Get DirectDraw error message.
947 * Returns: Text representation of the error.
950 DDErrorString (HRESULT hr)
953 case DDERR_ALREADYINITIALIZED:
954 return "DDERR_ALREADYINITIALIZED";
955 case DDERR_CANNOTATTACHSURFACE:
956 return "DDERR_CANNOTATTACHSURFACE";
957 case DDERR_CANNOTDETACHSURFACE:
958 return "DDERR_CANNOTDETACHSURFACE";
959 case DDERR_CURRENTLYNOTAVAIL:
960 return "DDERR_CURRENTLYNOTAVAIL";
961 case DDERR_EXCEPTION:
962 return "DDERR_EXCEPTION";
964 return "DDERR_GENERIC";
965 case DDERR_HEIGHTALIGN:
966 return "DDERR_HEIGHTALIGN";
967 case DDERR_INCOMPATIBLEPRIMARY:
968 return "DDERR_INCOMPATIBLEPRIMARY";
969 case DDERR_INVALIDCAPS:
970 return "DDERR_INVALIDCAPS";
971 case DDERR_INVALIDCLIPLIST:
972 return "DDERR_INVALIDCLIPLIST";
973 case DDERR_INVALIDMODE:
974 return "DDERR_INVALIDMODE";
975 case DDERR_INVALIDOBJECT:
976 return "DDERR_INVALIDOBJECT";
977 case DDERR_INVALIDPARAMS:
978 return "DDERR_INVALIDPARAMS";
979 case DDERR_INVALIDPIXELFORMAT:
980 return "DDERR_INVALIDPIXELFORMAT";
981 case DDERR_INVALIDRECT:
982 return "DDERR_INVALIDRECT";
983 case DDERR_LOCKEDSURFACES:
984 return "DDERR_LOCKEDSURFACES";
987 case DDERR_NOALPHAHW:
988 return "DDERR_NOALPHAHW";
989 case DDERR_NOCLIPLIST:
990 return "DDERR_NOCLIPLIST";
991 case DDERR_NOCOLORCONVHW:
992 return "DDERR_NOCOLORCONVHW";
993 case DDERR_NOCOOPERATIVELEVELSET:
994 return "DDERR_NOCOOPERATIVELEVELSET";
995 case DDERR_NOCOLORKEY:
996 return "DDERR_NOCOLORKEY";
997 case DDERR_NOCOLORKEYHW:
998 return "DDERR_NOCOLORKEYHW";
999 case DDERR_NODIRECTDRAWSUPPORT:
1000 return "DDERR_NODIRECTDRAWSUPPORT";
1001 case DDERR_NOEXCLUSIVEMODE:
1002 return "DDERR_NOEXCLUSIVEMODE";
1003 case DDERR_NOFLIPHW:
1004 return "DDERR_NOFLIPHW";
1006 return "DDERR_NOGDI";
1007 case DDERR_NOMIRRORHW:
1008 return "DDERR_NOMIRRORHW";
1009 case DDERR_NOTFOUND:
1010 return "DDERR_NOTFOUND";
1011 case DDERR_NOOVERLAYHW:
1012 return "DDERR_NOOVERLAYHW";
1013 case DDERR_NORASTEROPHW:
1014 return "DDERR_NORASTEROPHW";
1015 case DDERR_NOROTATIONHW:
1016 return "DDERR_NOROTATIONHW";
1017 case DDERR_NOSTRETCHHW:
1018 return "DDERR_NOSTRETCHHW";
1019 case DDERR_NOT4BITCOLOR:
1020 return "DDERR_NOT4BITCOLOR";
1021 case DDERR_NOT4BITCOLORINDEX:
1022 return "DDERR_NOT4BITCOLORINDEX";
1023 case DDERR_NOT8BITCOLOR:
1024 return "DDERR_NOT8BITCOLOR";
1025 case DDERR_NOTEXTUREHW:
1026 return "DDERR_NOTEXTUREHW";
1027 case DDERR_NOVSYNCHW:
1028 return "DDERR_NOVSYNCHW";
1029 case DDERR_NOZBUFFERHW:
1030 return "DDERR_NOZBUFFERHW";
1031 case DDERR_NOZOVERLAYHW:
1032 return "DDERR_NOZOVERLAYHW";
1033 case DDERR_OUTOFCAPS:
1034 return "DDERR_OUTOFCAPS";
1035 case DDERR_OUTOFMEMORY:
1036 return "DDERR_OUTOFMEMORY";
1037 case DDERR_OUTOFVIDEOMEMORY:
1038 return "DDERR_OUTOFVIDEOMEMORY";
1039 case DDERR_OVERLAYCANTCLIP:
1040 return "DDERR_OVERLAYCANTCLIP";
1041 case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
1042 return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE";
1043 case DDERR_PALETTEBUSY:
1044 return "DDERR_PALETTEBUSY";
1045 case DDERR_COLORKEYNOTSET:
1046 return "DDERR_COLORKEYNOTSET";
1047 case DDERR_SURFACEALREADYATTACHED:
1048 return "DDERR_SURFACEALREADYATTACHED";
1049 case DDERR_SURFACEALREADYDEPENDENT:
1050 return "DDERR_SURFACEALREADYDEPENDENT";
1051 case DDERR_SURFACEBUSY:
1052 return "DDERR_SURFACEBUSY";
1053 case DDERR_CANTLOCKSURFACE:
1054 return "DDERR_CANTLOCKSURFACE";
1055 case DDERR_SURFACEISOBSCURED:
1056 return "DDERR_SURFACEISOBSCURED";
1057 case DDERR_SURFACELOST:
1058 return "DDERR_SURFACELOST";
1059 case DDERR_SURFACENOTATTACHED:
1060 return "DDERR_SURFACENOTATTACHED";
1061 case DDERR_TOOBIGHEIGHT:
1062 return "DDERR_TOOBIGHEIGHT";
1063 case DDERR_TOOBIGSIZE:
1064 return "DDERR_TOOBIGSIZE";
1065 case DDERR_TOOBIGWIDTH:
1066 return "DDERR_TOOBIGWIDTH";
1067 case DDERR_UNSUPPORTED:
1068 return "DDERR_UNSUPPORTED";
1069 case DDERR_UNSUPPORTEDFORMAT:
1070 return "DDERR_UNSUPPORTEDFORMAT";
1071 case DDERR_UNSUPPORTEDMASK:
1072 return "DDERR_UNSUPPORTEDMASK";
1073 case DDERR_VERTICALBLANKINPROGRESS:
1074 return "DDERR_VERTICALBLANKINPROGRESS";
1075 case DDERR_WASSTILLDRAWING:
1076 return "DDERR_WASSTILLDRAWING";
1078 return "DDERR_XALIGN";
1079 case DDERR_INVALIDDIRECTDRAWGUID:
1080 return "DDERR_INVALIDDIRECTDRAWGUID";
1081 case DDERR_DIRECTDRAWALREADYCREATED:
1082 return "DDERR_DIRECTDRAWALREADYCREATED";
1083 case DDERR_NODIRECTDRAWHW:
1084 return "DDERR_NODIRECTDRAWHW";
1085 case DDERR_PRIMARYSURFACEALREADYEXISTS:
1086 return "DDERR_PRIMARYSURFACEALREADYEXISTS";
1087 case DDERR_NOEMULATION:
1088 return "DDERR_NOEMULATION";
1089 case DDERR_REGIONTOOSMALL:
1090 return "DDERR_REGIONTOOSMALL";
1091 case DDERR_CLIPPERISUSINGHWND:
1092 return "DDERR_CLIPPERISUSINGHWND";
1093 case DDERR_NOCLIPPERATTACHED:
1094 return "DDERR_NOCLIPPERATTACHED";
1096 return "DDERR_NOHWND";
1097 case DDERR_HWNDSUBCLASSED:
1098 return "DDERR_HWNDSUBCLASSED";
1099 case DDERR_HWNDALREADYSET:
1100 return "DDERR_HWNDALREADYSET";
1101 case DDERR_NOPALETTEATTACHED:
1102 return "DDERR_NOPALETTEATTACHED";
1103 case DDERR_NOPALETTEHW:
1104 return "DDERR_NOPALETTEHW";
1105 case DDERR_BLTFASTCANTCLIP:
1106 return "DDERR_BLTFASTCANTCLIP";
1108 return "DDERR_NOBLTHW";
1109 case DDERR_NODDROPSHW:
1110 return "DDERR_NODDROPSHW";
1111 case DDERR_OVERLAYNOTVISIBLE:
1112 return "DDERR_OVERLAYNOTVISIBLE";
1113 case DDERR_NOOVERLAYDEST:
1114 return "DDERR_NOOVERLAYDEST";
1115 case DDERR_INVALIDPOSITION:
1116 return "DDERR_INVALIDPOSITION";
1117 case DDERR_NOTAOVERLAYSURFACE:
1118 return "DDERR_NOTAOVERLAYSURFACE";
1119 case DDERR_EXCLUSIVEMODEALREADYSET:
1120 return "DDERR_EXCLUSIVEMODEALREADYSET";
1121 case DDERR_NOTFLIPPABLE:
1122 return "DDERR_NOTFLIPPABLE";
1123 case DDERR_CANTDUPLICATE:
1124 return "DDERR_CANTDUPLICATE";
1125 case DDERR_NOTLOCKED:
1126 return "DDERR_NOTLOCKED";
1127 case DDERR_CANTCREATEDC:
1128 return "DDERR_CANTCREATEDC";
1130 return "DDERR_NODC";
1131 case DDERR_WRONGMODE:
1132 return "DDERR_WRONGMODE";
1133 case DDERR_IMPLICITLYCREATED:
1134 return "DDERR_IMPLICITLYCREATED";
1135 case DDERR_NOTPALETTIZED:
1136 return "DDERR_NOTPALETTIZED";
1137 case DDERR_UNSUPPORTEDMODE:
1138 return "DDERR_UNSUPPORTEDMODE";
1139 case DDERR_NOMIPMAPHW:
1140 return "DDERR_NOMIPMAPHW";
1141 case DDERR_INVALIDSURFACETYPE:
1142 return "DDERR_INVALIDSURFACETYPE";
1143 case DDERR_DCALREADYCREATED:
1144 return "DDERR_DCALREADYCREATED";
1145 case DDERR_CANTPAGELOCK:
1146 return "DDERR_CANTPAGELOCK";
1147 case DDERR_CANTPAGEUNLOCK:
1148 return "DDERR_CANTPAGEUNLOCK";
1149 case DDERR_NOTPAGELOCKED:
1150 return "DDERR_NOTPAGELOCKED";
1151 case DDERR_NOTINITIALIZED:
1152 return "DDERR_NOTINITIALIZED";
1154 return "Unknown Error";
1158 gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
1160 gboolean bRet = TRUE;
1163 /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first parameter to
1164 force Directdraw to use the hardware emulation layer */
1165 hRes = DirectDrawCreateEx ( /*DDCREATE_EMULATIONONLY */ 0,
1166 (void **) &ddrawsink->ddraw_object, &IID_IDirectDraw7, NULL);
1167 if (hRes != DD_OK || ddrawsink->ddraw_object == NULL) {
1168 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1169 ("Failed to create the DirectDraw object error=%s",
1170 DDErrorString (hRes)), (NULL));
1174 /* set cooperative level */
1175 hRes = IDirectDraw7_SetCooperativeLevel (ddrawsink->ddraw_object,
1176 ddrawsink->video_window, DDSCL_NORMAL);
1177 if (hRes != DD_OK) {
1178 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1179 ("Failed to set the set the cooperative level error=%s",
1180 DDErrorString (hRes)), (NULL));
1184 /* setup the clipper object */
1185 hRes = IDirectDraw7_CreateClipper (ddrawsink->ddraw_object, 0,
1186 &ddrawsink->clipper, NULL);
1187 if (hRes == DD_OK) {
1188 hRes = IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0,
1189 ddrawsink->video_window);
1192 /* create our primary surface */
1193 if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
1196 /* directdraw objects are setup */
1197 ddrawsink->setup = TRUE;
1203 WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1209 PostQuitMessage (0);
1212 DestroyWindow (hWnd);
1215 return DefWindowProc (hWnd, message, wParam, lParam);
1219 gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
1223 memset (&WndClass, 0, sizeof (WNDCLASS));
1224 WndClass.style = CS_HREDRAW | CS_VREDRAW;
1225 WndClass.hInstance = GetModuleHandle (NULL);
1226 WndClass.lpszClassName = "GStreamer-DirectDraw";
1227 WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
1228 WndClass.cbClsExtra = 0;
1229 WndClass.cbWndExtra = 0;
1230 WndClass.lpfnWndProc = WndProc;
1231 WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
1232 RegisterClass (&WndClass);
1234 ddrawsink->video_window = CreateWindowEx (0, "GStreamer-DirectDraw",
1235 "GStreamer-DirectDraw sink default window",
1236 WS_OVERLAPPEDWINDOW | WS_SIZEBOX, 0, 0, 640, 480, NULL, NULL,
1237 WndClass.hInstance, NULL);
1238 if (ddrawsink->video_window == NULL)
1241 /* signal application we create a window */
1242 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ddrawsink),
1243 (gulong) ddrawsink->video_window);
1245 ReleaseSemaphore (ddrawsink->window_created_signal, 1, NULL);
1247 /* start message loop processing our default window messages */
1251 if (!GetMessage (&msg, ddrawsink->video_window, 0, 0))
1253 DispatchMessage (&msg);
1260 gst_directdraw_sink_create_default_window (GstDirectDrawSink * ddrawsink)
1262 ddrawsink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);
1263 if (ddrawsink->window_created_signal == NULL)
1266 ddrawsink->window_thread = g_thread_create (
1267 (GThreadFunc) gst_directdraw_sink_window_thread, ddrawsink, TRUE, NULL);
1269 if (ddrawsink->window_thread == NULL)
1272 /* wait maximum 10 seconds for windows creating */
1273 if (WaitForSingleObject (ddrawsink->window_created_signal,
1274 10000) != WAIT_OBJECT_0)
1277 CloseHandle (ddrawsink->window_created_signal);
1281 CloseHandle (ddrawsink->window_created_signal);
1282 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1283 ("Error creating our default window"), (NULL));
1289 gst_directdraw_sink_check_primary_surface (GstDirectDrawSink * ddrawsink)
1292 DDSURFACEDESC2 dd_surface_desc;
1295 /* if our primary surface already exist, check if it's not lost */
1296 if (ddrawsink->primary_surface) {
1297 if (IDirectDrawSurface7_IsLost (ddrawsink->primary_surface) == DD_OK) {
1298 /* no problem with our primary surface */
1301 /* our primary surface was lost, try to restore it */
1302 if (IDirectDrawSurface7_Restore (ddrawsink->primary_surface) == DD_OK) {
1303 /* restore is done */
1304 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1305 "Our primary surface" " was restored after lost");
1308 /* failed to restore our primary surface,
1309 * probably because the display mode was changed.
1310 * Release this surface and recreate a new one.
1312 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1313 "Our primary surface"
1314 " was lost and display mode has changed. Destroy and recreate our surface.");
1315 IDirectDrawSurface7_Release (ddrawsink->primary_surface);
1316 ddrawsink->primary_surface = NULL;
1318 /* also release offscreen surface */
1319 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1320 ddrawsink->offscreen_surface = NULL;
1325 /* create our primary surface */
1326 memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
1327 dd_surface_desc.dwSize = sizeof (dd_surface_desc);
1328 dd_surface_desc.dwFlags = DDSD_CAPS;
1329 dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
1330 sd = &dd_surface_desc;
1332 IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
1333 &ddrawsink->primary_surface, NULL);
1334 if (hres != DD_OK) {
1335 GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1336 ("Failed to create our primary surface error=%s", DDErrorString (hres)),
1341 /* attach our clipper object to the new primary surface */
1342 if (ddrawsink->clipper) {
1343 hres = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
1344 ddrawsink->clipper);
1351 gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink * ddrawsink)
1353 DDSURFACEDESC2 dd_surface_desc;
1357 /* if our offscreen surface already exist, check if it's not lost */
1358 if (ddrawsink->offscreen_surface) {
1359 if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK) {
1360 /* no problem with our offscreen surface */
1363 /* our offscreen surface was lost, try to restore it */
1364 if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
1365 /* restore is done */
1366 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1367 "Our offscreen surface" " was restored after lost");
1370 /* failed to restore our offscreen surface,
1371 * probably because the display mode was changed.
1372 * Release this surface and recreate a new one.
1374 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1375 "Our offscreen surface"
1376 " was lost and display mode has changed. Destroy and recreate our surface.");
1377 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1378 ddrawsink->offscreen_surface = NULL;
1383 memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
1384 dd_surface_desc.dwSize = sizeof (dd_surface_desc);
1385 dd_surface_desc.dwFlags =
1386 DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
1387 dd_surface_desc.dwHeight = ddrawsink->video_height;
1388 dd_surface_desc.dwWidth = ddrawsink->video_width;
1389 memcpy (&(dd_surface_desc.ddpfPixelFormat), &ddrawsink->dd_pixel_format,
1390 sizeof (DDPIXELFORMAT));
1392 dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1393 sd = &dd_surface_desc;
1395 IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
1396 &ddrawsink->offscreen_surface, NULL);
1397 if (hres != DD_OK) {
1398 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1399 "create_ddraw_surface:CreateSurface (offscreen surface for buffer_pool) failed %s",
1400 DDErrorString (hres));
1404 ddrawsink->must_recreate_offscreen = FALSE;
1410 gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat)
1412 gint order = 0, binary;
1415 lpddpfPixelFormat->dwRBitMask | lpddpfPixelFormat->
1416 dwGBitMask | lpddpfPixelFormat->dwBBitMask | lpddpfPixelFormat->
1418 while (binary != 0) {
1419 if ((binary % 2) == 1)
1421 binary = binary >> 1;
1427 EnumModesCallback2 (LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
1429 GstDirectDrawSink *ddrawsink = (GstDirectDrawSink *) lpContext;
1430 GstCaps *format_caps = NULL;
1431 LPDDSURFACEDESC2 sd;
1433 if (!ddrawsink || !lpDDSurfaceDesc)
1434 return DDENUMRET_CANCEL;
1436 sd = (LPDDSURFACEDESC2) lpDDSurfaceDesc;
1437 if ((sd->dwFlags & DDSD_PIXELFORMAT) != DDSD_PIXELFORMAT) {
1438 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1439 "Display mode found with DDSD_PIXELFORMAT not set");
1440 return DDENUMRET_OK;
1443 if ((sd->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1444 return DDENUMRET_OK;
1446 format_caps = gst_directdraw_sink_create_caps_from_surfacedesc (sd);
1449 gst_caps_append (ddrawsink->caps, format_caps);
1452 return DDENUMRET_OK;
1456 gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc)
1458 GstCaps *caps = NULL;
1459 gint endianness = G_LITTLE_ENDIAN;
1462 if ((desc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1465 depth = gst_directdraw_sink_get_depth (&desc->ddpfPixelFormat);
1467 if (desc->ddpfPixelFormat.dwRGBBitCount == 24 ||
1468 desc->ddpfPixelFormat.dwRGBBitCount == 32) {
1469 /* ffmpegcolorspace handles 24/32 bpp RGB as big-endian. */
1470 endianness = G_BIG_ENDIAN;
1471 desc->ddpfPixelFormat.dwRBitMask =
1472 GUINT32_TO_BE (desc->ddpfPixelFormat.dwRBitMask);
1473 desc->ddpfPixelFormat.dwGBitMask =
1474 GUINT32_TO_BE (desc->ddpfPixelFormat.dwGBitMask);
1475 desc->ddpfPixelFormat.dwBBitMask =
1476 GUINT32_TO_BE (desc->ddpfPixelFormat.dwBBitMask);
1477 if (desc->ddpfPixelFormat.dwRGBBitCount == 24) {
1478 desc->ddpfPixelFormat.dwRBitMask >>= 8;
1479 desc->ddpfPixelFormat.dwGBitMask >>= 8;
1480 desc->ddpfPixelFormat.dwBBitMask >>= 8;
1484 caps = gst_caps_new_simple ("video/x-raw-rgb",
1485 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1486 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1487 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
1488 "bpp", G_TYPE_INT, desc->ddpfPixelFormat.dwRGBBitCount,
1489 "depth", G_TYPE_INT, depth,
1490 "endianness", G_TYPE_INT, endianness,
1491 "red_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwRBitMask,
1492 "green_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwGBitMask,
1493 "blue_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwBBitMask, NULL);
1499 gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
1501 HRESULT hRes = S_OK;
1502 DDCAPS ddcaps_hardware;
1503 DDCAPS ddcaps_emulation;
1504 GstCaps *format_caps = NULL;
1506 ddrawsink->caps = gst_caps_new_empty ();
1507 if (!ddrawsink->caps)
1510 /* get hardware caps */
1511 ddcaps_hardware.dwSize = sizeof (DDCAPS);
1512 ddcaps_emulation.dwSize = sizeof (DDCAPS);
1513 IDirectDraw7_GetCaps (ddrawsink->ddraw_object, &ddcaps_hardware,
1516 /* we don't test for DDCAPS_BLTSTRETCH on the hardware as the directdraw emulation layer can do it */
1517 if (!(ddcaps_hardware.dwCaps & DDCAPS_BLTFOURCC)) {
1518 DDSURFACEDESC2 surface_desc;
1521 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1522 "hardware doesn't support blit from one colorspace to another one. "
1523 "so we will create a caps with only the current display mode");
1525 /* save blit caps */
1526 ddrawsink->can_blit_between_colorspace = FALSE;
1528 surface_desc.dwSize = sizeof (surface_desc);
1531 IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
1532 (DDSURFACEDESC *) sd);
1533 if (hRes != DD_OK) {
1534 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1535 ("Error getting the current display mode error=%s",
1536 DDErrorString (hRes)), (NULL));
1541 gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
1543 gst_caps_append (ddrawsink->caps, format_caps);
1546 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
1547 gst_caps_to_string (ddrawsink->caps));
1548 return ddrawsink->caps;
1551 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1552 "the hardware can blit from one colorspace to another, "
1553 "then enumerate the colorspace supported by the hardware");
1555 /* save blit caps */
1556 ddrawsink->can_blit_between_colorspace = TRUE;
1558 /* enumerate display modes exposed by directdraw object
1559 to know supported RGB modes */
1561 IDirectDraw7_EnumDisplayModes (ddrawsink->ddraw_object,
1562 DDEDM_REFRESHRATES, NULL, ddrawsink, EnumModesCallback2);
1563 if (hRes != DD_OK) {
1564 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1565 ("Error enumerating display modes error=%s", DDErrorString (hRes)),
1571 if (gst_caps_is_empty (ddrawsink->caps)) {
1572 gst_caps_unref (ddrawsink->caps);
1573 ddrawsink->caps = NULL;
1574 GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1575 ("No supported caps found."), (NULL));
1579 /*GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s", gst_caps_to_string (ddrawsink->caps)); */
1581 return ddrawsink->caps;
1584 /* Creates miniobject and our internal surface */
1585 static GstDDrawSurface *
1586 gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
1587 GstCaps * caps, size_t size)
1589 GstDDrawSurface *surface = NULL;
1590 GstStructure *structure = NULL;
1596 DDSURFACEDESC2 surf_desc, surf_lock_desc;
1598 g_return_val_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink), NULL);
1600 /*init structures */
1601 memset (&surf_desc, 0, sizeof (surf_desc));
1602 memset (&surf_lock_desc, 0, sizeof (surf_desc));
1603 surf_desc.dwSize = sizeof (surf_desc);
1604 surf_lock_desc.dwSize = sizeof (surf_lock_desc);
1606 /*create miniobject and initialize it */
1607 surface = (GstDDrawSurface *) gst_mini_object_new (GST_TYPE_DDRAWSURFACE);
1608 surface->locked = FALSE;
1610 structure = gst_caps_get_structure (caps, 0);
1611 if (!gst_structure_get_int (structure, "width", &surface->width) ||
1612 !gst_structure_get_int (structure, "height", &surface->height)) {
1613 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1614 "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
1617 pitch = GST_ROUND_UP_8 (size / surface->height);
1618 if (!gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
1619 &surface->dd_pixel_format)) {
1620 GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1621 "failed getting pixel format from caps %" GST_PTR_FORMAT, caps);
1624 /* disable return of directdraw surface to buffer alloc because actually I have no solution
1625 * to handle display mode changes. The problem is that when the display mode is changed
1626 * surface's memory is freed then the upstream filter would crash trying to write to this memory.
1627 * Directdraw has a system lock (DDLOCK_NOSYSLOCK to disable it) to prevent display mode changes
1628 * when a surface memory is locked but we need to disable this lock to return multiple buffers (surfaces)
1629 * and do not lock directdraw API calls.
1632 /* if (ddrawsink->ddraw_object) {*/
1633 /* Creating an internal surface which will be used as GstBuffer, we used
1634 the detected pixel format and video dimensions */
1636 surf_desc.ddsCaps.dwCaps =
1637 DDSCAPS_OFFSCREENPLAIN /* | DDSCAPS_SYSTEMMEMORY */ ;
1639 DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH;
1640 surf_desc.dwHeight = surface->height;
1641 surf_desc.dwWidth = surface->width;
1642 memcpy (&(surf_desc.ddpfPixelFormat), &surface->dd_pixel_format,
1643 sizeof (DDPIXELFORMAT));
1645 hRes = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &surf_desc,
1646 &surface->surface, NULL);
1647 if (hRes != DD_OK) {
1648 goto surface_pitch_bad;
1651 /* Locking the surface to acquire the memory pointer.
1652 Use DDLOCK_NOSYSLOCK to disable syslock which can cause a deadlock
1653 if directdraw api is used while a buffer is lock */
1655 hRes = IDirectDrawSurface7_Lock (surface->surface, NULL, &surf_lock_desc,
1656 DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
1657 if (hRes == DDERR_SURFACELOST) {
1658 IDirectDrawSurface7_Restore (surface->surface);
1661 surface->locked = TRUE;
1663 if (surf_lock_desc.lPitch != pitch) {
1664 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1665 "DDraw stride/pitch %ld isn't as expected value %d, let's continue allocating a system memory buffer.",
1666 surf_lock_desc.lPitch, pitch);
1668 /*Unlock the surface as we will change it to use system memory with a GStreamer compatible pitch */
1669 hRes = IDirectDrawSurface_Unlock (surface->surface, NULL);
1670 goto surface_pitch_bad;
1672 GST_BUFFER_DATA (surface) = surf_lock_desc.lpSurface;
1673 GST_BUFFER_SIZE (surface) = surf_lock_desc.lPitch * surface->height;
1674 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1675 "allocating a surface of %d bytes (stride=%ld)\n", size,
1676 surf_lock_desc.lPitch);
1680 GST_BUFFER (surface)->malloc_data = g_malloc (size);
1681 GST_BUFFER_DATA (surface) = GST_BUFFER (surface)->malloc_data;
1682 GST_BUFFER_SIZE (surface) = size;
1683 surface->surface = NULL;
1684 GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1685 "allocating a system memory buffer of %d bytes", size);
1689 /* Keep a ref to our sink */
1690 surface->ddrawsink = gst_object_ref (ddrawsink);
1695 /* We are called from the finalize method of miniobject, the object will be
1696 * destroyed so we just have to clean our internal stuff */
1698 gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
1699 GstDDrawSurface * surface)
1701 g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
1703 /* Release our internal surface */
1704 if (surface->surface) {
1705 if (surface->locked) {
1706 IDirectDrawSurface7_Unlock (surface->surface, NULL);
1707 surface->locked = FALSE;
1709 IDirectDrawSurface7_Release (surface->surface);
1710 surface->surface = NULL;
1713 if (GST_BUFFER (surface)->malloc_data) {
1714 g_free (GST_BUFFER (surface)->malloc_data);
1715 GST_BUFFER (surface)->malloc_data = NULL;
1718 if (!surface->ddrawsink) {
1722 /* Release the ref to our sink */
1723 surface->ddrawsink = NULL;
1724 gst_object_unref (ddrawsink);
1729 GST_WARNING ("no sink found in surface");
1734 gst_directdraw_sink_surface_check (GstDirectDrawSink * ddrawsink,
1735 GstDDrawSurface * surface)
1737 if (!surface->surface)
1738 return TRUE; /* system memory buffer */
1740 if (IDirectDrawSurface7_IsLost (surface->surface) == DD_OK) {
1741 /* no problem with this surface */
1744 /* this surface was lost, try to restore it */
1745 if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
1746 /* restore is done */
1747 GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink, "A surface from our"
1748 " bufferpool was restored after lost");
1757 gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink * ddrawsink)
1759 g_mutex_lock (ddrawsink->pool_lock);
1760 while (ddrawsink->buffer_pool) {
1761 GstDDrawSurface *surface = ddrawsink->buffer_pool->data;
1763 ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
1764 ddrawsink->buffer_pool);
1765 gst_directdraw_sink_surface_destroy (ddrawsink, surface);
1767 g_mutex_unlock (ddrawsink->pool_lock);
1771 gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink)
1773 /* Post quit message and wait for our event window thread */
1774 if (ddrawsink->video_window && ddrawsink->our_video_window)
1775 PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
1777 if (ddrawsink->window_thread) {
1778 g_thread_join (ddrawsink->window_thread);
1779 ddrawsink->window_thread = NULL;
1782 if (ddrawsink->buffer_pool) {
1783 gst_directdraw_sink_bufferpool_clear (ddrawsink);
1784 ddrawsink->buffer_pool = NULL;
1787 if (ddrawsink->offscreen_surface) {
1788 IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1789 ddrawsink->offscreen_surface = NULL;
1792 if (ddrawsink->clipper) {
1793 IDirectDrawClipper_Release (ddrawsink->clipper);
1794 ddrawsink->clipper = NULL;
1797 if (ddrawsink->primary_surface) {
1798 IDirectDrawSurface7_Release (ddrawsink->primary_surface);
1799 ddrawsink->primary_surface = NULL;
1802 if (ddrawsink->ddraw_object) {
1803 IDirectDraw7_Release (ddrawsink->ddraw_object);
1804 ddrawsink->ddraw_object = NULL;
1807 if (ddrawsink->last_buffer) {
1808 gst_buffer_unref (ddrawsink->last_buffer);
1809 ddrawsink->last_buffer = NULL;
1812 ddrawsink->setup = FALSE;