300bfe074892f8e9027fe8fdba728377079c4ccd
[platform/upstream/gst-plugins-good.git] / sys / directdraw / gstdirectdrawsink.c
1 /* GStreamer
2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
4 *       
5 * Based on directfb video sink
6 * gstdirectdrawsink.c:
7 *
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.
12 *
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.
17 *
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.
22 *
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
25 *
26 */
27
28 /**
29  * SECTION:element-directdrawsink
30  *
31  * <refsect2>
32  * <para>
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.
38  * </para>
39  * <title>Examples</title>
40  * <para>
41  * Here is a simple pipeline to test the sink :
42  * <programlisting>
43  * gst-launch-0.10 -v videotestsrc ! directdrawsink
44  * </programlisting>
45  * </para>
46  * </refsect2>
47  */
48
49 #ifdef HAVE_CONFIG_H
50 #include "config.h"
51 #endif
52
53 #include "gstdirectdrawsink.h"
54
55 GST_DEBUG_CATEGORY_STATIC (directdrawsink_debug);
56 #define GST_CAT_DEFAULT directdrawsink_debug
57
58 /* elementfactory information */
59 static const GstElementDetails gst_directdraw_sink_details =
60 GST_ELEMENT_DETAILS ("Direct Draw Video Sink",
61     "Sink/Video",
62     "Output to a video card via Direct Draw",
63     "Sebastien Moutte <sebastien@moutte.net>");
64
65 static void gst_directdraw_sink_init_interfaces (GType type);
66
67 GST_BOILERPLATE_FULL (GstDirectDrawSink, gst_directdraw_sink, GstVideoSink,
68     GST_TYPE_VIDEO_SINK, gst_directdraw_sink_init_interfaces);
69
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,
77     GstCaps * caps);
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,
85     GstBuffer * buf);
86
87 /* utils */
88 static gboolean gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink);
89 static gboolean gst_directdraw_sink_create_default_window (GstDirectDrawSink *
90     ddrawsink);
91 static gboolean gst_directdraw_sink_check_primary_surface (GstDirectDrawSink *
92     ddrawsink);
93 static gboolean gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink *
94     ddrawsink);
95 static GstCaps *gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink *
96     ddrawsink);
97 static GstCaps
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 *
101     ddrawsink);
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);
108
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);
116
117 static GstStaticPadTemplate directdrawsink_sink_factory =
118 GST_STATIC_PAD_TEMPLATE ("sink",
119     GST_PAD_SINK,
120     GST_PAD_ALWAYS,
121     GST_STATIC_CAPS ("video/x-raw-rgb, "
122         "framerate = (fraction) [ 0, MAX ], "
123         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
124     );
125
126 enum
127 {
128   PROP_0,
129   PROP_KEEP_ASPECT_RATIO
130 };
131
132 /* XOverlay interface implementation */
133 static gboolean
134 gst_directdraw_sink_interface_supported (GstImplementsInterface * iface,
135     GType type)
136 {
137   g_assert (type == GST_TYPE_X_OVERLAY);
138   return TRUE;
139 }
140
141 static void
142 gst_directdraw_sink_interface_init (GstImplementsInterfaceClass * klass)
143 {
144   klass->supported = gst_directdraw_sink_interface_supported;
145 }
146
147 static void
148 gst_directdraw_sink_set_window_id (GstXOverlay * overlay, ULONG window_id)
149 {
150   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
151
152   /* check if we are already using this window id */
153   if (ddrawsink->video_window == (HWND) window_id)
154     return;
155
156   if (window_id) {
157     HRESULT hres;
158
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);
165     }
166   }
167 }
168
169 static void
170 gst_directdraw_sink_expose (GstXOverlay * overlay)
171 {
172   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
173
174   gst_directdraw_sink_show_frame (GST_BASE_SINK (ddrawsink), NULL);
175 }
176
177 static void
178 gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
179 {
180   iface->set_xwindow_id = gst_directdraw_sink_set_window_id;
181   iface->expose = gst_directdraw_sink_expose;
182 }
183
184 static void
185 gst_directdraw_sink_init_interfaces (GType type)
186 {
187   static const GInterfaceInfo iface_info = {
188     (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
189     NULL,
190     NULL,
191   };
192
193   static const GInterfaceInfo xoverlay_info = {
194     (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_interface_init,
195     NULL,
196     NULL,
197   };
198
199   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
200       &iface_info);
201   g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
202 }
203
204 /* Subclass of GstBuffer which manages buffer_pool surfaces lifetime    */
205 static void gst_ddrawsurface_finalize (GstMiniObject * mini_object);
206
207 static void
208 gst_ddrawsurface_init (GstDDrawSurface * surface, gpointer g_class)
209 {
210   surface->surface = NULL;
211   surface->width = 0;
212   surface->height = 0;
213   surface->ddrawsink = NULL;
214   surface->locked = FALSE;
215   surface->system_memory = FALSE;
216   memset (&surface->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
217 }
218
219 static void
220 gst_ddrawsurface_class_init (gpointer g_class, gpointer class_data)
221 {
222   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
223
224   mini_object_class->finalize = GST_DEBUG_FUNCPTR (gst_ddrawsurface_finalize);
225 }
226
227 GType
228 gst_ddrawsurface_get_type (void)
229 {
230   static GType _gst_ddrawsurface_type;
231
232   if (G_UNLIKELY (_gst_ddrawsurface_type == 0)) {
233     static const GTypeInfo ddrawsurface_info = {
234       sizeof (GstBufferClass),
235       NULL,
236       NULL,
237       gst_ddrawsurface_class_init,
238       NULL,
239       NULL,
240       sizeof (GstDDrawSurface),
241       0,
242       (GInstanceInitFunc) gst_ddrawsurface_init,
243       NULL
244     };
245     _gst_ddrawsurface_type = g_type_register_static (GST_TYPE_BUFFER,
246         "GstDDrawSurface", &ddrawsurface_info, 0);
247   }
248   return _gst_ddrawsurface_type;
249 }
250
251 static void
252 gst_ddrawsurface_finalize (GstMiniObject * mini_object)
253 {
254   GstDirectDrawSink *ddrawsink = NULL;
255   GstDDrawSurface *surface;
256
257   surface = (GstDDrawSurface *) mini_object;
258
259   ddrawsink = surface->ddrawsink;
260   if (!ddrawsink)
261     goto no_sink;
262
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))
269       ) {
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);
275
276   } else {
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");
280
281     /* need to increment the refcount again to recycle */
282     gst_buffer_ref (GST_BUFFER (surface));
283
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);
287   }
288   return;
289
290 no_sink:
291   GST_CAT_WARNING (directdrawsink_debug, "no sink found");
292   return;
293 }
294
295 /************************************************************************/
296 /* Directdraw sink functions                                            */
297 /************************************************************************/
298 static void
299 gst_directdraw_sink_base_init (gpointer g_class)
300 {
301   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
302
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));
306 }
307
308 static void
309 gst_directdraw_sink_class_init (GstDirectDrawSinkClass * klass)
310 {
311   GObjectClass *gobject_class;
312   GstElementClass *gstelement_class;
313   GstBaseSinkClass *gstbasesink_class;
314
315   gobject_class = (GObjectClass *) klass;
316   gstbasesink_class = (GstBaseSinkClass *) klass;
317   gstelement_class = (GstElementClass *) klass;
318
319   GST_DEBUG_CATEGORY_INIT (directdrawsink_debug, "directdrawsink", 0,
320       "Directdraw sink");
321
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);
341
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,
347           G_PARAM_READWRITE));
348 }
349
350 static void
351 gst_directdraw_sink_set_property (GObject * object, guint prop_id,
352     const GValue * value, GParamSpec * pspec)
353 {
354   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
355
356   switch (prop_id) {
357     case PROP_KEEP_ASPECT_RATIO:
358       ddrawsink->keep_aspect_ratio = g_value_get_boolean (value);
359       break;
360     default:
361       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362       break;
363   }
364 }
365
366 static void
367 gst_directdraw_sink_get_property (GObject * object, guint prop_id,
368     GValue * value, GParamSpec * pspec)
369 {
370   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
371
372   switch (prop_id) {
373     case PROP_KEEP_ASPECT_RATIO:
374       g_value_set_boolean (value, ddrawsink->keep_aspect_ratio);
375       break;
376     default:
377       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378       break;
379   }
380 }
381
382 static void
383 gst_directdraw_sink_finalize (GObject * object)
384 {
385   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
386
387   if (ddrawsink->pool_lock) {
388     g_mutex_free (ddrawsink->pool_lock);
389     ddrawsink->pool_lock = NULL;
390   }
391   if (ddrawsink->caps) {
392     gst_caps_unref (ddrawsink->caps);
393     ddrawsink->caps = NULL;
394   }
395   if (ddrawsink->setup) {
396     gst_directdraw_sink_cleanup (ddrawsink);
397   }
398 }
399
400 static void
401 gst_directdraw_sink_init (GstDirectDrawSink * ddrawsink,
402     GstDirectDrawSinkClass * g_class)
403 {
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));
421
422   /*video default values */
423   ddrawsink->video_height = 0;
424   ddrawsink->video_width = 0;
425   ddrawsink->fps_n = 0;
426   ddrawsink->fps_d = 0;
427 }
428
429 static GstCaps *
430 gst_directdraw_sink_get_caps (GstBaseSink * bsink)
431 {
432   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
433   GstCaps *caps = NULL;
434
435   if (!ddrawsink->setup) {
436     caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
437             (ddrawsink)));
438     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
439         "getcaps called and we are not setup yet, " "returning template %"
440         GST_PTR_FORMAT, caps);
441   } else {
442     caps = gst_caps_ref (ddrawsink->caps);
443   }
444
445   return caps;
446 }
447
448 static gboolean
449 gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
450 {
451   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
452   GstStructure *structure = NULL;
453   gboolean ret;
454   const GValue *fps;
455
456   structure = gst_caps_get_structure (caps, 0);
457   if (!structure)
458     return FALSE;
459
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);
464   ret &=
465       gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
466       &ddrawsink->dd_pixel_format);
467   if (!ret) {
468     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
469         ("Failed to get caps properties from caps"), (NULL));
470     return FALSE;
471   }
472
473   ddrawsink->fps_n = gst_value_get_fraction_numerator (fps);
474   ddrawsink->fps_d = gst_value_get_fraction_denominator (fps);
475
476   /* Notify application to set window id now */
477   if (!ddrawsink->video_window) {
478     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ddrawsink));
479   }
480
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);
487   }
488
489   /* create an offscreen surface with the caps */
490   ret = gst_directdraw_sink_check_offscreen_surface (ddrawsink);
491   if (!ret) {
492     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
493         ("Can't create a directdraw offscreen surface with the input caps"),
494         (NULL));
495   }
496
497   return ret;
498 }
499
500 static GstStateChangeReturn
501 gst_directdraw_sink_change_state (GstElement * element,
502     GstStateChange transition)
503 {
504   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);;
505
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;
511
512       if (!gst_directdraw_sink_setup_ddraw (ddrawsink))
513         return GST_STATE_CHANGE_FAILURE;
514
515       if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink)))
516         return GST_STATE_CHANGE_FAILURE;
517       break;
518     case GST_STATE_CHANGE_READY_TO_PAUSED:
519       break;
520     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
521       break;
522     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
523       break;
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);
531       break;
532     case GST_STATE_CHANGE_READY_TO_NULL:
533       if (ddrawsink->setup)
534         gst_directdraw_sink_cleanup (ddrawsink);
535       break;
536   }
537
538   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
539 }
540
541 static GstFlowReturn
542 gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
543     guint size, GstCaps * caps, GstBuffer ** buf)
544 {
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;
550
551   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
552       "a buffer of %d bytes was requested", size);
553
554   g_mutex_lock (ddrawsink->pool_lock);
555
556   /* Inspect our buffer pool */
557   while (ddrawsink->buffer_pool) {
558     surface = (GstDDrawSurface *) ddrawsink->buffer_pool->data;
559     if (surface) {
560       /* Removing from the pool */
561       ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
562           ddrawsink->buffer_pool);
563
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))
570           ) {
571         gst_directdraw_sink_surface_destroy (ddrawsink, surface);
572         surface = NULL;
573       } else {
574         /* We found a suitable surface */
575         break;
576       }
577     }
578   }
579
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
584      */
585     guint depth;
586     HRESULT hres;
587     DDSURFACEDESC2 surface_desc;
588     DDSURFACEDESC2 *sd;
589     GstStructure *structure = NULL;
590
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;
596     }
597     surface_desc.dwSize = sizeof (surface_desc);
598     sd = &surface_desc;
599     hres =
600         IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
601         (DDSURFACEDESC *) sd);
602     if (hres != DD_OK) {
603       GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
604           "Can't get current display mode (error=%ld)", (glong) hres);
605       return GST_FLOW_ERROR;
606     }
607
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;
613
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);
617
618       display_caps =
619           gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
620       if (display_caps) {
621         display_structure = gst_caps_get_structure (display_caps, 0);
622         if (display_structure) {
623           gint bpp, endianness, red_mask, green_mask, blue_mask;
624
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);
632
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);
641
642           if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ddrawsink),
643                   copy_caps)) {
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);
648
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;
653
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));
657           } else {
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;
662           }
663         }
664         gst_caps_unref (display_caps);
665       }
666
667       if (!buffercaps_unref)
668         gst_caps_unref (copy_caps);
669     }
670   }
671
672   /* We haven't found anything, creating a new one */
673   if (!surface) {
674     surface = gst_directdraw_sink_surface_create (ddrawsink, buffer_caps, size);
675   }
676
677   /* Now we should have a surface, set appropriate caps on it */
678   if (surface) {
679     gst_buffer_set_caps (GST_BUFFER (surface), buffer_caps);
680   }
681
682   g_mutex_unlock (ddrawsink->pool_lock);
683
684   *buf = GST_BUFFER (surface);
685
686   if (buffercaps_unref)
687     gst_caps_unref (buffer_caps);
688
689   return ret;
690 }
691
692 static GstFlowReturn
693 gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
694 {
695   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
696   HRESULT hRes;
697   RECT destsurf_rect, src_rect;
698   POINT dest_surf_point;
699
700   if (buf) {
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);
705       }
706     }
707     ddrawsink->last_buffer = gst_buffer_ref (buf);
708   } else {
709     /* use last buffer */
710     buf = ddrawsink->last_buffer;
711   }
712   if (buf == NULL)
713     return GST_FLOW_ERROR;
714
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);
721
722   if (ddrawsink->keep_aspect_ratio) {
723     /* center image to dest image keeping aspect ratio */
724     src_rect.top = 0;
725     src_rect.left = 0;
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,
729         &destsurf_rect);
730   }
731
732   if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
733     IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
734     ddrawsink->offscreen_surface = NULL;
735   }
736
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;
741   }
742
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 */
748     LPBYTE data = NULL;
749     guint src_pitch, line;
750     DDSURFACEDESC2 surf_desc;
751     DDSURFACEDESC2 *sd;
752
753     ZeroMemory (&surf_desc, sizeof (surf_desc));
754     surf_desc.dwSize = sizeof (surf_desc);
755     sd = &surf_desc;
756
757     /* Lock the surface */
758     hRes =
759         IDirectDrawSurface7_Lock (ddrawsink->offscreen_surface, NULL,
760         (DDSURFACEDESC *) sd, DDLOCK_WAIT, NULL);
761     if (hRes != DD_OK) {
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;
766     }
767
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;
774     }
775
776     /* Unlock the surface */
777     hRes = IDirectDrawSurface7_Unlock (ddrawsink->offscreen_surface, NULL);
778     if (hRes != DD_OK) {
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;
783     }
784
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);
788     if (hRes != DD_OK)
789       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
790           "IDirectDrawSurface7_Blt (object's offscreen surface) " "returned %s",
791           DDErrorString (hRes));
792
793   } else {
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;
797
798     surface = GST_DDRAWSURFACE (buf);
799
800     /* Unlocking surface before blit */
801     IDirectDrawSurface7_Unlock (surface->surface, NULL);
802     surface->locked = FALSE;
803
804     /* blit to our primary surface */
805     hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
806         surface->surface, NULL, DDBLT_WAIT, NULL);
807     if (hRes != DD_OK)
808       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
809           "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
810           "returned %s", DDErrorString (hRes));
811   }
812
813   return GST_FLOW_OK;
814 }
815
816 static void
817 gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
818     GstClockTime * start, GstClockTime * end)
819 {
820   GstDirectDrawSink *ddrawsink;
821
822   ddrawsink = GST_DIRECTDRAW_SINK (bsink);
823
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);
828     } else {
829       if (ddrawsink->fps_n > 0) {
830         *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
831       }
832     }
833   }
834 }
835
836 /* Utility functions */
837
838 /* this function fill a DDPIXELFORMAT using Gstreamer caps */
839 static gboolean
840 gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
841     GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
842 {
843   GstStructure *structure = NULL;
844   gboolean ret = TRUE;
845
846   /* check params */
847   g_return_val_if_fail (pPixelFormat, FALSE);
848   g_return_val_if_fail (caps, FALSE);
849
850   /* init structure */
851   memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
852   pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
853
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");
857     return FALSE;
858   }
859
860   if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
861     gint depth, bitcount, bitmask, endianness;
862
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;
873
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);
880     }
881   } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
882     gint fourcc;
883
884     pPixelFormat->dwFlags = DDPF_FOURCC;
885     ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
886     pPixelFormat->dwFourCC = fourcc;
887   } else {
888     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
889         "unknown caps name received %" GST_PTR_FORMAT, caps);
890     ret = FALSE;
891   }
892
893   return ret;
894 }
895
896 /* This function centers the RECT of source surface to
897 a dest surface and set the result RECT into result */
898 static void
899 gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
900     RECT dst, RECT * result)
901 {
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;
908
909   g_return_if_fail (result != NULL);
910
911   src_ratio = (gdouble) src_width / src_height;
912   dst_ratio = (gdouble) dst_width / dst_heigth;
913
914   if (src_ratio > dst_ratio) {
915     /* new height */
916     result_height = (long) (dst_width / src_ratio);
917
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;
922
923   } else if (src_ratio < dst_ratio) {
924     /* new width */
925     result_width = (long) (dst_heigth * src_ratio);
926
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;
931
932   } else {
933     /* same ratio */
934     memcpy (result, &dst, sizeof (RECT));
935   }
936
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,
941       result->right);
942 }
943
944 /**
945  * Get DirectDraw error message.
946  * @hr: HRESULT code
947  * Returns: Text representation of the error.
948  */
949 char *
950 DDErrorString (HRESULT hr)
951 {
952   switch (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";
963     case DDERR_GENERIC:
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";
985     case DDERR_NO3D:
986       return "DDERR_NO3D";
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";
1005     case DDERR_NOGDI:
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";
1077     case DDERR_XALIGN:
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";
1095     case DDERR_NOHWND:
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";
1107     case DDERR_NOBLTHW:
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";
1129     case DDERR_NODC:
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";
1153   }
1154   return "Unknown Error";
1155 }
1156
1157 static gboolean
1158 gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
1159 {
1160   gboolean bRet = TRUE;
1161   HRESULT hRes;
1162
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));
1171     return FALSE;
1172   }
1173
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));
1181     return FALSE;
1182   }
1183
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);
1190   }
1191
1192   /* create our primary surface */
1193   if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
1194     return FALSE;
1195
1196   /* directdraw objects are setup */
1197   ddrawsink->setup = TRUE;
1198
1199   return bRet;
1200 }
1201
1202 long FAR PASCAL
1203 WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1204 {
1205   switch (message) {
1206     case WM_ERASEBKGND:
1207       return TRUE;
1208     case WM_DESTROY:
1209       PostQuitMessage (0);
1210       break;
1211     case WM_CLOSE:
1212       DestroyWindow (hWnd);
1213       return 0;
1214   }
1215   return DefWindowProc (hWnd, message, wParam, lParam);
1216 }
1217
1218 static gpointer
1219 gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
1220 {
1221   WNDCLASS WndClass;
1222
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);
1233
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)
1239     return FALSE;
1240
1241   /* signal application we create a window */
1242   gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ddrawsink),
1243       (gulong) ddrawsink->video_window);
1244
1245   ReleaseSemaphore (ddrawsink->window_created_signal, 1, NULL);
1246
1247   /* start message loop processing our default window messages */
1248   while (1) {
1249     MSG msg;
1250
1251     if (!GetMessage (&msg, ddrawsink->video_window, 0, 0))
1252       break;
1253     DispatchMessage (&msg);
1254   }
1255
1256   return NULL;
1257 }
1258
1259 static gboolean
1260 gst_directdraw_sink_create_default_window (GstDirectDrawSink * ddrawsink)
1261 {
1262   ddrawsink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);
1263   if (ddrawsink->window_created_signal == NULL)
1264     return FALSE;
1265
1266   ddrawsink->window_thread = g_thread_create (
1267       (GThreadFunc) gst_directdraw_sink_window_thread, ddrawsink, TRUE, NULL);
1268
1269   if (ddrawsink->window_thread == NULL)
1270     goto failed;
1271
1272   /* wait maximum 10 seconds for windows creating */
1273   if (WaitForSingleObject (ddrawsink->window_created_signal,
1274           10000) != WAIT_OBJECT_0)
1275     goto failed;
1276
1277   CloseHandle (ddrawsink->window_created_signal);
1278   return TRUE;
1279
1280 failed:
1281   CloseHandle (ddrawsink->window_created_signal);
1282   GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1283       ("Error creating our default window"), (NULL));
1284
1285   return FALSE;
1286 }
1287
1288 static gboolean
1289 gst_directdraw_sink_check_primary_surface (GstDirectDrawSink * ddrawsink)
1290 {
1291   HRESULT hres;
1292   DDSURFACEDESC2 dd_surface_desc;
1293   DDSURFACEDESC2 *sd;
1294
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 */
1299       return TRUE;
1300     } else {
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");
1306         return TRUE;
1307       } else {
1308         /* failed to restore our primary surface, 
1309          * probably because the display mode was changed. 
1310          * Release this surface and recreate a new one.
1311          */
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;
1317
1318         /* also release offscreen surface */
1319         IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1320         ddrawsink->offscreen_surface = NULL;
1321       }
1322     }
1323   }
1324
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;
1331   hres =
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)),
1337         (NULL));
1338     return FALSE;
1339   }
1340
1341   /* attach our clipper object to the new primary surface */
1342   if (ddrawsink->clipper) {
1343     hres = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
1344         ddrawsink->clipper);
1345   }
1346
1347   return TRUE;
1348 }
1349
1350 static gboolean
1351 gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink * ddrawsink)
1352 {
1353   DDSURFACEDESC2 dd_surface_desc;
1354   DDSURFACEDESC2 *sd;
1355   HRESULT hres;
1356
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 */
1361       return TRUE;
1362     } else {
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");
1368         return TRUE;
1369       } else {
1370         /* failed to restore our offscreen surface, 
1371          * probably because the display mode was changed. 
1372          * Release this surface and recreate a new one.
1373          */
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;
1379       }
1380     }
1381   }
1382
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));
1391
1392   dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1393   sd = &dd_surface_desc;
1394   hres =
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));
1401     return FALSE;
1402   }
1403
1404   ddrawsink->must_recreate_offscreen = FALSE;
1405
1406   return TRUE;
1407 }
1408
1409 static int
1410 gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat)
1411 {
1412   gint order = 0, binary;
1413
1414   binary =
1415       lpddpfPixelFormat->dwRBitMask | lpddpfPixelFormat->
1416       dwGBitMask | lpddpfPixelFormat->dwBBitMask | lpddpfPixelFormat->
1417       dwRGBAlphaBitMask;
1418   while (binary != 0) {
1419     if ((binary % 2) == 1)
1420       order++;
1421     binary = binary >> 1;
1422   }
1423   return order;
1424 }
1425
1426 HRESULT WINAPI
1427 EnumModesCallback2 (LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
1428 {
1429   GstDirectDrawSink *ddrawsink = (GstDirectDrawSink *) lpContext;
1430   GstCaps *format_caps = NULL;
1431   LPDDSURFACEDESC2 sd;
1432
1433   if (!ddrawsink || !lpDDSurfaceDesc)
1434     return DDENUMRET_CANCEL;
1435
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;
1441   }
1442
1443   if ((sd->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1444     return DDENUMRET_OK;
1445
1446   format_caps = gst_directdraw_sink_create_caps_from_surfacedesc (sd);
1447
1448   if (format_caps) {
1449     gst_caps_append (ddrawsink->caps, format_caps);
1450   }
1451
1452   return DDENUMRET_OK;
1453 }
1454
1455 static GstCaps *
1456 gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc)
1457 {
1458   GstCaps *caps = NULL;
1459   gint endianness = G_LITTLE_ENDIAN;
1460   gint depth;
1461
1462   if ((desc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1463     return NULL;
1464
1465   depth = gst_directdraw_sink_get_depth (&desc->ddpfPixelFormat);
1466
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;
1481     }
1482   }
1483
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);
1494
1495   return caps;
1496 }
1497
1498 static GstCaps *
1499 gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
1500 {
1501   HRESULT hRes = S_OK;
1502   DDCAPS ddcaps_hardware;
1503   DDCAPS ddcaps_emulation;
1504   GstCaps *format_caps = NULL;
1505
1506   ddrawsink->caps = gst_caps_new_empty ();
1507   if (!ddrawsink->caps)
1508     return FALSE;
1509
1510   /* get hardware caps */
1511   ddcaps_hardware.dwSize = sizeof (DDCAPS);
1512   ddcaps_emulation.dwSize = sizeof (DDCAPS);
1513   IDirectDraw7_GetCaps (ddrawsink->ddraw_object, &ddcaps_hardware,
1514       &ddcaps_emulation);
1515
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;
1519     DDSURFACEDESC2 *sd;
1520
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");
1524
1525     /* save blit caps */
1526     ddrawsink->can_blit_between_colorspace = FALSE;
1527
1528     surface_desc.dwSize = sizeof (surface_desc);
1529     sd = &surface_desc;
1530     hRes =
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));
1537       return NULL;
1538     }
1539
1540     format_caps =
1541         gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
1542     if (format_caps) {
1543       gst_caps_append (ddrawsink->caps, format_caps);
1544     }
1545
1546     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
1547         gst_caps_to_string (ddrawsink->caps));
1548     return ddrawsink->caps;
1549   }
1550
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");
1554
1555   /* save blit caps */
1556   ddrawsink->can_blit_between_colorspace = TRUE;
1557
1558   /* enumerate display modes exposed by directdraw object 
1559      to know supported RGB modes */
1560   hRes =
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)),
1566         (NULL));
1567
1568     return NULL;
1569   }
1570
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));
1576     return NULL;
1577   }
1578
1579   /*GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s", gst_caps_to_string (ddrawsink->caps)); */
1580
1581   return ddrawsink->caps;
1582 }
1583
1584 /* Creates miniobject and our internal surface */
1585 static GstDDrawSurface *
1586 gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
1587     GstCaps * caps, size_t size)
1588 {
1589   GstDDrawSurface *surface = NULL;
1590   GstStructure *structure = NULL;
1591   gint pitch;
1592
1593 #if 0
1594   HRESULT hRes;
1595 #endif
1596   DDSURFACEDESC2 surf_desc, surf_lock_desc;
1597
1598   g_return_val_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink), NULL);
1599
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);
1605
1606   /*create miniobject and initialize it */
1607   surface = (GstDDrawSurface *) gst_mini_object_new (GST_TYPE_DDRAWSURFACE);
1608   surface->locked = FALSE;
1609
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);
1615   }
1616
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);
1622   }
1623
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.
1630    */
1631 #if 0
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 */
1635
1636   surf_desc.ddsCaps.dwCaps =
1637       DDSCAPS_OFFSCREENPLAIN /* | DDSCAPS_SYSTEMMEMORY */ ;
1638   surf_desc.dwFlags =
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));
1644
1645   hRes = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &surf_desc,
1646       &surface->surface, NULL);
1647   if (hRes != DD_OK) {
1648     goto surface_pitch_bad;
1649   }
1650
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 */
1654 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);
1659     goto lock;
1660   }
1661   surface->locked = TRUE;
1662
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);
1667
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;
1671   }
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);
1677
1678 surface_pitch_bad:
1679 #else
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);
1686
1687 #endif
1688
1689   /* Keep a ref to our sink */
1690   surface->ddrawsink = gst_object_ref (ddrawsink);
1691
1692   return surface;
1693 }
1694
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 */
1697 static void
1698 gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
1699     GstDDrawSurface * surface)
1700 {
1701   g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
1702
1703   /* Release our internal surface */
1704   if (surface->surface) {
1705     if (surface->locked) {
1706       IDirectDrawSurface7_Unlock (surface->surface, NULL);
1707       surface->locked = FALSE;
1708     }
1709     IDirectDrawSurface7_Release (surface->surface);
1710     surface->surface = NULL;
1711   }
1712
1713   if (GST_BUFFER (surface)->malloc_data) {
1714     g_free (GST_BUFFER (surface)->malloc_data);
1715     GST_BUFFER (surface)->malloc_data = NULL;
1716   }
1717
1718   if (!surface->ddrawsink) {
1719     goto no_sink;
1720   }
1721
1722   /* Release the ref to our sink */
1723   surface->ddrawsink = NULL;
1724   gst_object_unref (ddrawsink);
1725
1726   return;
1727
1728 no_sink:
1729   GST_WARNING ("no sink found in surface");
1730   return;
1731 }
1732
1733 static gboolean
1734 gst_directdraw_sink_surface_check (GstDirectDrawSink * ddrawsink,
1735     GstDDrawSurface * surface)
1736 {
1737   if (!surface->surface)
1738     return TRUE;                /* system memory buffer */
1739
1740   if (IDirectDrawSurface7_IsLost (surface->surface) == DD_OK) {
1741     /* no problem with this surface */
1742     return TRUE;
1743   } else {
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");
1749       return TRUE;
1750     }
1751   }
1752
1753   return FALSE;
1754 }
1755
1756 static void
1757 gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink * ddrawsink)
1758 {
1759   g_mutex_lock (ddrawsink->pool_lock);
1760   while (ddrawsink->buffer_pool) {
1761     GstDDrawSurface *surface = ddrawsink->buffer_pool->data;
1762
1763     ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
1764         ddrawsink->buffer_pool);
1765     gst_directdraw_sink_surface_destroy (ddrawsink, surface);
1766   }
1767   g_mutex_unlock (ddrawsink->pool_lock);
1768 }
1769
1770 static void
1771 gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink)
1772 {
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);
1776
1777   if (ddrawsink->window_thread) {
1778     g_thread_join (ddrawsink->window_thread);
1779     ddrawsink->window_thread = NULL;
1780   }
1781
1782   if (ddrawsink->buffer_pool) {
1783     gst_directdraw_sink_bufferpool_clear (ddrawsink);
1784     ddrawsink->buffer_pool = NULL;
1785   }
1786
1787   if (ddrawsink->offscreen_surface) {
1788     IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1789     ddrawsink->offscreen_surface = NULL;
1790   }
1791
1792   if (ddrawsink->clipper) {
1793     IDirectDrawClipper_Release (ddrawsink->clipper);
1794     ddrawsink->clipper = NULL;
1795   }
1796
1797   if (ddrawsink->primary_surface) {
1798     IDirectDrawSurface7_Release (ddrawsink->primary_surface);
1799     ddrawsink->primary_surface = NULL;
1800   }
1801
1802   if (ddrawsink->ddraw_object) {
1803     IDirectDraw7_Release (ddrawsink->ddraw_object);
1804     ddrawsink->ddraw_object = NULL;
1805   }
1806
1807   if (ddrawsink->last_buffer) {
1808     gst_buffer_unref (ddrawsink->last_buffer);
1809     ddrawsink->last_buffer = NULL;
1810   }
1811
1812   ddrawsink->setup = FALSE;
1813 }