caa2800591e01e9d8188af65032ae169eeeb2ed4
[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  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * The development of this code was made possible due to the involvement
21  * of Pioneers of the Inevitable, the creators of the Songbird Music player
22  *
23  */
24
25 /**
26  * SECTION:element-directdrawsink
27  *
28  * <refsect2>
29  * <para>
30  * DirectdrawSink renders video RGB frames to any win32 window. This element
31  * can receive a window ID from the application through the XOverlay interface
32  * and will then render video frames in this window.
33  * If no Window ID was provided by the application, the element will create its
34  * own internal window and render into it.
35  * </para>
36  * <title>Examples</title>
37  * <para>
38  * Here is a simple pipeline to test the sink :
39  * <programlisting>
40  * gst-launch-0.10 -v videotestsrc ! directdrawsink
41  * </programlisting>
42  * </para>
43  * </refsect2>
44  */
45
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
49
50 #include "gstdirectdrawsink.h"
51
52 GST_DEBUG_CATEGORY_STATIC (directdrawsink_debug);
53 #define GST_CAT_DEFAULT directdrawsink_debug
54
55 /* elementfactory information */
56 static const GstElementDetails gst_directdraw_sink_details =
57 GST_ELEMENT_DETAILS ("Direct Draw Video Sink",
58     "Sink/Video",
59     "Output to a video card via Direct Draw",
60     "Sebastien Moutte <sebastien@moutte.net>");
61
62 static void gst_directdraw_sink_init_interfaces (GType type);
63
64 GST_BOILERPLATE_FULL (GstDirectDrawSink, gst_directdraw_sink, GstVideoSink,
65     GST_TYPE_VIDEO_SINK, gst_directdraw_sink_init_interfaces);
66
67 static void gst_directdraw_sink_finalize (GObject * object);
68 static void gst_directdraw_sink_set_property (GObject * object,
69     guint prop_id, const GValue * value, GParamSpec * pspec);
70 static void gst_directdraw_sink_get_property (GObject * object,
71     guint prop_id, GValue * value, GParamSpec * pspec);
72 static GstCaps *gst_directdraw_sink_get_caps (GstBaseSink * bsink);
73 static gboolean gst_directdraw_sink_set_caps (GstBaseSink * bsink,
74     GstCaps * caps);
75 static GstStateChangeReturn gst_directdraw_sink_change_state (GstElement *
76     element, GstStateChange transition);
77 static GstFlowReturn gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink,
78     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
79 static void gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
80     GstClockTime * start, GstClockTime * end);
81 static GstFlowReturn gst_directdraw_sink_show_frame (GstBaseSink * bsink,
82     GstBuffer * buf);
83
84 /* utils */
85 static gboolean gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink);
86 static gboolean gst_directdraw_sink_create_default_window (GstDirectDrawSink *
87     ddrawsink);
88 static gboolean gst_directdraw_sink_check_primary_surface (GstDirectDrawSink *
89     ddrawsink);
90 static gboolean gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink *
91     ddrawsink);
92 static GstCaps *gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink *
93     ddrawsink);
94 static GstCaps
95     * gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc);
96 static void gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink);
97 static void gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink *
98     ddrawsink);
99 static int gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat);
100 static gboolean gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink *
101     ddrawsink, GstCaps * caps, DDPIXELFORMAT * pPixelFormat);
102 static void gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink,
103     RECT src, RECT dst, RECT * result);
104 char *DDErrorString (HRESULT hr);
105
106 /* surfaces management functions */
107 static void gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
108     GstDDrawSurface * surface);
109 static GstDDrawSurface *gst_directdraw_sink_surface_create (GstDirectDrawSink *
110     ddrawsink, GstCaps * caps, size_t size);
111 static gboolean gst_directdraw_sink_surface_check (GstDirectDrawSink *
112     ddrawsink, GstDDrawSurface * surface);
113
114 static GstStaticPadTemplate directdrawsink_sink_factory =
115 GST_STATIC_PAD_TEMPLATE ("sink",
116     GST_PAD_SINK,
117     GST_PAD_ALWAYS,
118     GST_STATIC_CAPS ("video/x-raw-rgb, "
119         "framerate = (fraction) [ 0, MAX ], "
120         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
121     );
122
123 enum
124 {
125   PROP_0,
126   PROP_KEEP_ASPECT_RATIO
127 };
128
129 /* XOverlay interface implementation */
130 static gboolean
131 gst_directdraw_sink_interface_supported (GstImplementsInterface * iface,
132     GType type)
133 {
134   g_assert (type == GST_TYPE_X_OVERLAY);
135   return TRUE;
136 }
137
138 static void
139 gst_directdraw_sink_interface_init (GstImplementsInterfaceClass * klass)
140 {
141   klass->supported = gst_directdraw_sink_interface_supported;
142 }
143
144 static void
145 gst_directdraw_sink_set_window_id (GstXOverlay * overlay, ULONG window_id)
146 {
147   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
148
149   GST_OBJECT_LOCK (ddrawsink);
150   /* check if we are already using this window id */
151   if (ddrawsink->video_window == (HWND) window_id) {
152     GST_OBJECT_UNLOCK (ddrawsink);
153     return;
154   }
155
156   if (window_id) {
157     HRESULT hres;
158
159     /* If we had an internal window, close it first */
160     if (ddrawsink->video_window && ddrawsink->our_video_window) {
161       /* Trick to let the event thread know that it has to die silently */
162       ddrawsink->our_video_window = FALSE;
163       /* Post quit message and wait for our event window thread */
164       PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
165     }
166
167     ddrawsink->video_window = (HWND) window_id;
168     ddrawsink->our_video_window = FALSE;
169     if (ddrawsink->setup) {
170       /* update the clipper object with the new window */
171       hres = IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0,
172           ddrawsink->video_window);
173     }
174   }
175   /* FIXME: Handle the case where window_id is 0 and we want the sink to 
176    * create a new window when playback was already started (after set_caps) */
177   GST_OBJECT_UNLOCK (ddrawsink);
178 }
179
180 static void
181 gst_directdraw_sink_expose (GstXOverlay * overlay)
182 {
183   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
184
185   gst_directdraw_sink_show_frame (GST_BASE_SINK (ddrawsink), NULL);
186 }
187
188 static void
189 gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
190 {
191   iface->set_xwindow_id = gst_directdraw_sink_set_window_id;
192   iface->expose = gst_directdraw_sink_expose;
193 }
194
195 static void
196 gst_directdraw_sink_init_interfaces (GType type)
197 {
198   static const GInterfaceInfo iface_info = {
199     (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
200     NULL,
201     NULL,
202   };
203
204   static const GInterfaceInfo xoverlay_info = {
205     (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_interface_init,
206     NULL,
207     NULL,
208   };
209
210   g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
211       &iface_info);
212   g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
213 }
214
215 /* Subclass of GstBuffer which manages buffer_pool surfaces lifetime    */
216 static void gst_ddrawsurface_finalize (GstMiniObject * mini_object);
217 static GstBufferClass *ddrawsurface_parent_class = NULL;
218
219 static void
220 gst_ddrawsurface_init (GstDDrawSurface * surface, gpointer g_class)
221 {
222   surface->surface = NULL;
223   surface->width = 0;
224   surface->height = 0;
225   surface->ddrawsink = NULL;
226   surface->locked = FALSE;
227   surface->system_memory = FALSE;
228   memset (&surface->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
229 }
230
231 static void
232 gst_ddrawsurface_class_init (gpointer g_class, gpointer class_data)
233 {
234   GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
235
236   ddrawsurface_parent_class = g_type_class_peek_parent (g_class);
237
238   mini_object_class->finalize = GST_DEBUG_FUNCPTR (gst_ddrawsurface_finalize);
239 }
240
241 GType
242 gst_ddrawsurface_get_type (void)
243 {
244   static GType _gst_ddrawsurface_type;
245
246   if (G_UNLIKELY (_gst_ddrawsurface_type == 0)) {
247     static const GTypeInfo ddrawsurface_info = {
248       sizeof (GstBufferClass),
249       NULL,
250       NULL,
251       gst_ddrawsurface_class_init,
252       NULL,
253       NULL,
254       sizeof (GstDDrawSurface),
255       0,
256       (GInstanceInitFunc) gst_ddrawsurface_init,
257       NULL
258     };
259     _gst_ddrawsurface_type = g_type_register_static (GST_TYPE_BUFFER,
260         "GstDDrawSurface", &ddrawsurface_info, 0);
261   }
262   return _gst_ddrawsurface_type;
263 }
264
265 static void
266 gst_ddrawsurface_finalize (GstMiniObject * mini_object)
267 {
268   GstDirectDrawSink *ddrawsink = NULL;
269   GstDDrawSurface *surface;
270
271   surface = (GstDDrawSurface *) mini_object;
272
273   ddrawsink = surface->ddrawsink;
274   if (!ddrawsink)
275     goto no_sink;
276
277   /* If our geometry changed we can't reuse that image. */
278   if ((surface->width != ddrawsink->video_width) ||
279       (surface->height != ddrawsink->video_height) ||
280       (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
281               sizeof (DDPIXELFORMAT)) != 0 ||
282           !gst_directdraw_sink_surface_check (ddrawsink, surface))
283       ) {
284     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
285         "destroy image as its size changed %dx%d vs current %dx%d",
286         surface->width, surface->height, ddrawsink->video_width,
287         ddrawsink->video_height);
288     gst_directdraw_sink_surface_destroy (ddrawsink, surface);
289     GST_MINI_OBJECT_CLASS (ddrawsurface_parent_class)->finalize (mini_object);
290   } else {
291     /* In that case we can reuse the image and add it to our image pool. */
292     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
293         "recycling image in pool");
294
295     /* need to increment the refcount again to recycle */
296     gst_buffer_ref (GST_BUFFER (surface));
297
298     g_mutex_lock (ddrawsink->pool_lock);
299     ddrawsink->buffer_pool = g_slist_prepend (ddrawsink->buffer_pool, surface);
300     g_mutex_unlock (ddrawsink->pool_lock);
301   }
302
303   return;
304
305 no_sink:
306   GST_CAT_WARNING (directdrawsink_debug, "no sink found");
307   GST_MINI_OBJECT_CLASS (ddrawsurface_parent_class)->finalize (mini_object);
308   return;
309 }
310
311 /************************************************************************/
312 /* Directdraw sink functions                                            */
313 /************************************************************************/
314 static void
315 gst_directdraw_sink_base_init (gpointer g_class)
316 {
317   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
318
319   gst_element_class_set_details (element_class, &gst_directdraw_sink_details);
320   gst_element_class_add_pad_template (element_class,
321       gst_static_pad_template_get (&directdrawsink_sink_factory));
322 }
323
324 static void
325 gst_directdraw_sink_class_init (GstDirectDrawSinkClass * klass)
326 {
327   GObjectClass *gobject_class;
328   GstElementClass *gstelement_class;
329   GstBaseSinkClass *gstbasesink_class;
330
331   gobject_class = (GObjectClass *) klass;
332   gstbasesink_class = (GstBaseSinkClass *) klass;
333   gstelement_class = (GstElementClass *) klass;
334
335   GST_DEBUG_CATEGORY_INIT (directdrawsink_debug, "directdrawsink", 0,
336       "Directdraw sink");
337
338   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directdraw_sink_finalize);
339   gobject_class->get_property =
340       GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_property);
341   gobject_class->set_property =
342       GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_property);
343   gstelement_class->change_state =
344       GST_DEBUG_FUNCPTR (gst_directdraw_sink_change_state);
345   gstbasesink_class->get_caps =
346       GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_caps);
347   gstbasesink_class->set_caps =
348       GST_DEBUG_FUNCPTR (gst_directdraw_sink_set_caps);
349   gstbasesink_class->preroll =
350       GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
351   gstbasesink_class->render =
352       GST_DEBUG_FUNCPTR (gst_directdraw_sink_show_frame);
353   gstbasesink_class->get_times =
354       GST_DEBUG_FUNCPTR (gst_directdraw_sink_get_times);
355   gstbasesink_class->buffer_alloc =
356       GST_DEBUG_FUNCPTR (gst_directdraw_sink_buffer_alloc);
357
358   /* install properties */
359   /* setup aspect ratio mode */
360   g_object_class_install_property (G_OBJECT_CLASS (klass),
361       PROP_KEEP_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio",
362           "Force aspect ratio",
363           "When enabled, scaling will respect original aspect ratio", FALSE,
364           G_PARAM_READWRITE));
365 }
366
367 static void
368 gst_directdraw_sink_set_property (GObject * object, guint prop_id,
369     const GValue * value, GParamSpec * pspec)
370 {
371   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
372
373   switch (prop_id) {
374     case PROP_KEEP_ASPECT_RATIO:
375       ddrawsink->keep_aspect_ratio = g_value_get_boolean (value);
376       break;
377     default:
378       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
379       break;
380   }
381 }
382
383 static void
384 gst_directdraw_sink_get_property (GObject * object, guint prop_id,
385     GValue * value, GParamSpec * pspec)
386 {
387   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
388
389   switch (prop_id) {
390     case PROP_KEEP_ASPECT_RATIO:
391       g_value_set_boolean (value, ddrawsink->keep_aspect_ratio);
392       break;
393     default:
394       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395       break;
396   }
397 }
398
399 static void
400 gst_directdraw_sink_finalize (GObject * object)
401 {
402   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (object);
403
404   if (ddrawsink->pool_lock) {
405     g_mutex_free (ddrawsink->pool_lock);
406     ddrawsink->pool_lock = NULL;
407   }
408   if (ddrawsink->caps) {
409     gst_caps_unref (ddrawsink->caps);
410     ddrawsink->caps = NULL;
411   }
412   if (ddrawsink->setup) {
413     gst_directdraw_sink_cleanup (ddrawsink);
414   }
415
416   G_OBJECT_CLASS (parent_class)->finalize (object);
417 }
418
419 static void
420 gst_directdraw_sink_init (GstDirectDrawSink * ddrawsink,
421     GstDirectDrawSinkClass * g_class)
422 {
423   /*init members variables */
424   ddrawsink->ddraw_object = NULL;
425   ddrawsink->primary_surface = NULL;
426   ddrawsink->offscreen_surface = NULL;
427   ddrawsink->clipper = NULL;
428   ddrawsink->video_window = NULL;
429   ddrawsink->our_video_window = TRUE;
430   ddrawsink->last_buffer = NULL;
431   ddrawsink->caps = NULL;
432   ddrawsink->window_thread = NULL;
433   ddrawsink->setup = FALSE;
434   ddrawsink->buffer_pool = NULL;
435   ddrawsink->keep_aspect_ratio = FALSE;
436   ddrawsink->pool_lock = g_mutex_new ();
437   ddrawsink->can_blit_between_colorspace = TRUE;
438   ddrawsink->must_recreate_offscreen = FALSE;
439   memset (&ddrawsink->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
440
441   /*video default values */
442   ddrawsink->video_height = 0;
443   ddrawsink->video_width = 0;
444   ddrawsink->fps_n = 0;
445   ddrawsink->fps_d = 0;
446 }
447
448 static GstCaps *
449 gst_directdraw_sink_get_caps (GstBaseSink * bsink)
450 {
451   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
452   GstCaps *caps = NULL;
453
454   if (!ddrawsink->setup) {
455     caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
456             (ddrawsink)));
457     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
458         "getcaps called and we are not setup yet, " "returning template %"
459         GST_PTR_FORMAT, caps);
460   } else {
461     caps = gst_caps_ref (ddrawsink->caps);
462   }
463
464   return caps;
465 }
466
467 static gboolean
468 gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
469 {
470   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
471   GstStructure *structure = NULL;
472   gboolean ret;
473   const GValue *fps;
474
475   structure = gst_caps_get_structure (caps, 0);
476   if (!structure)
477     return FALSE;
478
479   ret = gst_structure_get_int (structure, "width", &ddrawsink->video_width);
480   ret &= gst_structure_get_int (structure, "height", &ddrawsink->video_height);
481   fps = gst_structure_get_value (structure, "framerate");
482   ret &= (fps != NULL);
483   ret &=
484       gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
485       &ddrawsink->dd_pixel_format);
486   if (!ret) {
487     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
488         ("Failed to get caps properties from caps"), (NULL));
489     return FALSE;
490   }
491
492   ddrawsink->fps_n = gst_value_get_fraction_numerator (fps);
493   ddrawsink->fps_d = gst_value_get_fraction_denominator (fps);
494
495   /* Notify application to set window id now */
496   if (!ddrawsink->video_window) {
497     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ddrawsink));
498   }
499
500   /* If we still don't have a window at that stage we create our own */
501   if (!ddrawsink->video_window) {
502     gst_directdraw_sink_create_default_window (ddrawsink);
503   }
504
505   /* if we are rendering to our own window, resize it to video size */
506   if (ddrawsink->video_window && ddrawsink->our_video_window) {
507     SetWindowPos (ddrawsink->video_window, NULL,
508         0, 0, ddrawsink->video_width + (GetSystemMetrics (SM_CXSIZEFRAME) * 2),
509         ddrawsink->video_height + GetSystemMetrics (SM_CYCAPTION) +
510         (GetSystemMetrics (SM_CYSIZEFRAME) * 2), SWP_SHOWWINDOW | SWP_NOMOVE);
511   }
512
513   /* release the surface, we have to recreate it! */
514   if (ddrawsink->offscreen_surface) {
515     IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
516     ddrawsink->offscreen_surface = NULL;
517   }
518
519   /* create an offscreen surface with the caps */
520   ret = gst_directdraw_sink_check_offscreen_surface (ddrawsink);
521   if (!ret) {
522     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
523         ("Can't create a directdraw offscreen surface with the input caps"),
524         (NULL));
525   }
526
527   return ret;
528 }
529
530 static GstStateChangeReturn
531 gst_directdraw_sink_change_state (GstElement * element,
532     GstStateChange transition)
533 {
534   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);
535   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
536
537   switch (transition) {
538     case GST_STATE_CHANGE_NULL_TO_READY:
539       if (!gst_directdraw_sink_setup_ddraw (ddrawsink)) {
540         ret = GST_STATE_CHANGE_FAILURE;
541         goto beach;
542       }
543
544       if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink))) {
545         ret = GST_STATE_CHANGE_FAILURE;
546         goto beach;
547       }
548       break;
549     default:
550       break;
551   }
552
553   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
554
555   switch (transition) {
556     case GST_STATE_CHANGE_PAUSED_TO_READY:
557       ddrawsink->fps_n = 0;
558       ddrawsink->fps_d = 1;
559       ddrawsink->video_width = 0;
560       ddrawsink->video_height = 0;
561       if (ddrawsink->buffer_pool)
562         gst_directdraw_sink_bufferpool_clear (ddrawsink);
563       break;
564     case GST_STATE_CHANGE_READY_TO_NULL:
565       if (ddrawsink->setup)
566         gst_directdraw_sink_cleanup (ddrawsink);
567       break;
568     default:
569       break;
570   }
571
572 beach:
573   return ret;
574 }
575
576 static GstFlowReturn
577 gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
578     guint size, GstCaps * caps, GstBuffer ** buf)
579 {
580   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
581   GstStructure *structure;
582   gint width, height;
583   GstDDrawSurface *surface = NULL;
584   GstFlowReturn ret = GST_FLOW_OK;
585   GstCaps *buffer_caps = caps;
586   gboolean buffercaps_unref = FALSE;
587
588   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
589       "a buffer of %u bytes was requested", size);
590
591   structure = gst_caps_get_structure (caps, 0);
592   if (!gst_structure_get_int (structure, "width", &width) ||
593       !gst_structure_get_int (structure, "height", &height)) {
594     GST_WARNING_OBJECT (ddrawsink, "invalid caps for buffer allocation %"
595         GST_PTR_FORMAT, caps);
596     return GST_FLOW_UNEXPECTED;
597   }
598
599   g_mutex_lock (ddrawsink->pool_lock);
600
601   /* Inspect our buffer pool */
602   while (ddrawsink->buffer_pool) {
603     surface = (GstDDrawSurface *) ddrawsink->buffer_pool->data;
604     if (surface) {
605       /* Removing from the pool */
606       ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
607           ddrawsink->buffer_pool);
608
609       /* If the surface is invalid for our need, destroy */
610       if ((surface->width != width) ||
611           (surface->height != height) ||
612           (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
613                   sizeof (DDPIXELFORMAT)) ||
614               !gst_directdraw_sink_surface_check (ddrawsink, surface))
615           ) {
616         gst_directdraw_sink_surface_destroy (ddrawsink, surface);
617         gst_buffer_unref (surface);
618         surface = NULL;
619       } else {
620         /* We found a suitable surface */
621         break;
622       }
623     }
624   }
625
626   if (!ddrawsink->can_blit_between_colorspace) {
627     /* Hardware doesn't support blit from one colorspace to another.
628      * Check if the colorspace of the current display mode has changed since
629      * the last negociation. If it's the case, we will have to renegociate
630      */
631     guint depth;
632     HRESULT hres;
633     DDSURFACEDESC2 surface_desc;
634     DDSURFACEDESC2 *sd;
635
636     if (!gst_structure_get_int (structure, "depth", &depth)) {
637       GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
638           "Can't get depth from buffer_alloc caps");
639       return GST_FLOW_ERROR;
640     }
641     surface_desc.dwSize = sizeof (surface_desc);
642     sd = &surface_desc;
643     hres =
644         IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
645         (DDSURFACEDESC *) sd);
646     if (hres != DD_OK) {
647       GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
648           "Can't get current display mode (error=%ld)", (glong) hres);
649       return GST_FLOW_ERROR;
650     }
651
652     if (depth != gst_directdraw_sink_get_depth (&surface_desc.ddpfPixelFormat)) {
653       GstCaps *copy_caps = NULL;
654       GstStructure *copy_structure = NULL;
655       GstCaps *display_caps = NULL;
656       GstStructure *display_structure = NULL;
657
658       /* make a copy of the original caps */
659       copy_caps = gst_caps_copy (caps);
660       copy_structure = gst_caps_get_structure (copy_caps, 0);
661
662       display_caps =
663           gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
664       if (display_caps) {
665         display_structure = gst_caps_get_structure (display_caps, 0);
666         if (display_structure) {
667           gint bpp, endianness, red_mask, green_mask, blue_mask;
668
669           /* get new display mode properties */
670           gst_structure_get_int (display_structure, "depth", &depth);
671           gst_structure_get_int (display_structure, "bpp", &bpp);
672           gst_structure_get_int (display_structure, "endianness", &endianness);
673           gst_structure_get_int (display_structure, "red_mask", &red_mask);
674           gst_structure_get_int (display_structure, "green_mask", &green_mask);
675           gst_structure_get_int (display_structure, "blue_mask", &blue_mask);
676
677           /* apply the new display mode changes to the previous caps */
678           gst_structure_set (copy_structure,
679               "bpp", G_TYPE_INT, bpp,
680               "depth", G_TYPE_INT, depth,
681               "endianness", G_TYPE_INT, endianness,
682               "red_mask", G_TYPE_INT, red_mask,
683               "green_mask", G_TYPE_INT, green_mask,
684               "blue_mask", G_TYPE_INT, blue_mask, NULL);
685
686           if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ddrawsink),
687                   copy_caps)) {
688             buffer_caps = copy_caps;
689             buffercaps_unref = TRUE;
690             /* update buffer size needed to store video frames according to new caps */
691             size = width * height * (bpp / 8);
692
693             /* update our member pixel format */
694             gst_ddrawvideosink_get_format_from_caps (ddrawsink, buffer_caps,
695                 &ddrawsink->dd_pixel_format);
696             ddrawsink->must_recreate_offscreen = TRUE;
697
698             GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
699                 " desired caps %s \n\n new caps %s", gst_caps_to_string (caps),
700                 gst_caps_to_string (buffer_caps));
701           } else {
702             GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
703                 "peer refused caps re-negociation "
704                 "and we can't render with the current caps.");
705             ret = GST_FLOW_ERROR;
706           }
707         }
708         gst_caps_unref (display_caps);
709       }
710
711       if (!buffercaps_unref)
712         gst_caps_unref (copy_caps);
713     }
714   }
715
716   /* We haven't found anything, creating a new one */
717   if (!surface) {
718     surface = gst_directdraw_sink_surface_create (ddrawsink, buffer_caps, size);
719   }
720
721   /* Now we should have a surface, set appropriate caps on it */
722   if (surface) {
723     GST_BUFFER_FLAGS (GST_BUFFER (surface)) = 0;
724     gst_buffer_set_caps (GST_BUFFER (surface), buffer_caps);
725   }
726
727   g_mutex_unlock (ddrawsink->pool_lock);
728
729   *buf = GST_BUFFER (surface);
730
731   if (buffercaps_unref)
732     gst_caps_unref (buffer_caps);
733
734   return ret;
735 }
736
737 static void
738 gst_directdraw_sink_draw_borders (GstDirectDrawSink * ddrawsink, RECT dst_rect)
739 {
740   RECT win_rect, fill_rect;
741   POINT win_point;
742   HDC hdc;
743
744   g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
745
746   /* Get the target window rect */
747   win_point.x = 0;
748   win_point.y = 0;
749   ClientToScreen (ddrawsink->video_window, &win_point);
750   GetClientRect (ddrawsink->video_window, &win_rect);
751   OffsetRect (&win_rect, win_point.x, win_point.y);
752
753   /* We acquire a drawing context */
754   if (IDirectDrawSurface7_GetDC (ddrawsink->primary_surface, &hdc) == DD_OK) {
755     HBRUSH brush = CreateSolidBrush (RGB (0, 0, 0));
756
757     /* Left border */
758     if (dst_rect.left > win_rect.left) {
759       fill_rect.left = win_rect.left;
760       fill_rect.top = win_rect.top;
761       fill_rect.bottom = win_rect.bottom;
762       fill_rect.right = dst_rect.left;
763       FillRect (hdc, &fill_rect, brush);
764     }
765     /* Right border */
766     if (dst_rect.right < win_rect.right) {
767       fill_rect.top = win_rect.top;
768       fill_rect.left = dst_rect.right;
769       fill_rect.bottom = win_rect.bottom;
770       fill_rect.right = win_rect.right;
771       FillRect (hdc, &fill_rect, brush);
772     }
773     /* Top border */
774     if (dst_rect.top > win_rect.top) {
775       fill_rect.top = win_rect.top;
776       fill_rect.left = win_rect.left;
777       fill_rect.right = win_rect.right;
778       fill_rect.bottom = dst_rect.top;
779       FillRect (hdc, &fill_rect, brush);
780     }
781     /* Bottom border */
782     if (dst_rect.bottom < win_rect.bottom) {
783       fill_rect.top = dst_rect.bottom;
784       fill_rect.left = win_rect.left;
785       fill_rect.right = win_rect.right;
786       fill_rect.bottom = win_rect.bottom;
787       FillRect (hdc, &fill_rect, brush);
788     }
789     DeleteObject (brush);
790     IDirectDrawSurface7_ReleaseDC (ddrawsink->primary_surface, hdc);
791   }
792 }
793
794 static GstFlowReturn
795 gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
796 {
797   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
798   HRESULT hRes;
799   RECT destsurf_rect, src_rect;
800   POINT dest_surf_point;
801
802   if (buf) {
803     /* save a reference to the input buffer */
804     gst_buffer_ref (buf);
805     if (ddrawsink->last_buffer != NULL)
806       gst_buffer_unref (ddrawsink->last_buffer);
807     ddrawsink->last_buffer = buf;
808   } else {
809     /* use last buffer */
810     buf = ddrawsink->last_buffer;
811   }
812
813   if (buf == NULL) {
814     GST_ERROR_OBJECT (ddrawsink, "No buffer to render.");
815     return GST_FLOW_ERROR;
816   } else if (!ddrawsink->video_window) {
817     GST_WARNING_OBJECT (ddrawsink, "No video window to render to.");
818     return GST_FLOW_ERROR;
819   }
820
821   /* get the video window position */
822   GST_OBJECT_LOCK (ddrawsink);
823   if (G_UNLIKELY (!ddrawsink->video_window)) {
824     GST_OBJECT_UNLOCK (ddrawsink);
825     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
826         "gst_directdraw_sink_show_frame our video window disappeared");
827     GST_ELEMENT_ERROR (ddrawsink, RESOURCE, NOT_FOUND,
828         ("Output window was closed"), (NULL));
829     return GST_FLOW_ERROR;
830   }
831   dest_surf_point.x = 0;
832   dest_surf_point.y = 0;
833   ClientToScreen (ddrawsink->video_window, &dest_surf_point);
834   GetClientRect (ddrawsink->video_window, &destsurf_rect);
835   OffsetRect (&destsurf_rect, dest_surf_point.x, dest_surf_point.y);
836
837   if (ddrawsink->keep_aspect_ratio) {
838     /* center image to dest image keeping aspect ratio */
839     src_rect.top = 0;
840     src_rect.left = 0;
841     src_rect.bottom = ddrawsink->video_height;
842     src_rect.right = ddrawsink->video_width;
843     gst_directdraw_sink_center_rect (ddrawsink, src_rect, destsurf_rect,
844         &destsurf_rect);
845     gst_directdraw_sink_draw_borders (ddrawsink, destsurf_rect);
846   }
847   GST_OBJECT_UNLOCK (ddrawsink);
848
849   if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
850     IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
851     ddrawsink->offscreen_surface = NULL;
852   }
853
854   /* check for surfaces lost */
855   if (!gst_directdraw_sink_check_primary_surface (ddrawsink) ||
856       !gst_directdraw_sink_check_offscreen_surface (ddrawsink)) {
857     return GST_FLOW_ERROR;
858   }
859
860   if (!GST_IS_DDRAWSURFACE (buf) ||
861       ((GST_IS_DDRAWSURFACE (buf)) && (GST_BUFFER (buf)->malloc_data))) {
862     /* We are receiving a system memory buffer so we will copy 
863        to the memory of our offscreen surface and next blit this surface 
864        on the primary surface */
865     LPBYTE data = NULL;
866     guint src_pitch, line;
867     DDSURFACEDESC2 surf_desc;
868     DDSURFACEDESC2 *sd;
869
870     ZeroMemory (&surf_desc, sizeof (surf_desc));
871     surf_desc.dwSize = sizeof (surf_desc);
872     sd = &surf_desc;
873
874     /* Lock the surface */
875     hRes =
876         IDirectDrawSurface7_Lock (ddrawsink->offscreen_surface, NULL,
877         (DDSURFACEDESC *) sd, DDLOCK_WAIT, NULL);
878     if (hRes != DD_OK) {
879       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
880           "gst_directdraw_sink_show_frame failed locking surface %s",
881           DDErrorString (hRes));
882
883       if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK)
884         return GST_FLOW_OK;
885       else
886         return GST_FLOW_ERROR;
887     }
888
889     /* Write each line respecting the destination surface pitch */
890     data = surf_desc.lpSurface;
891     src_pitch = GST_BUFFER_SIZE (buf) / ddrawsink->video_height;
892     for (line = 0; line < surf_desc.dwHeight; line++) {
893       memcpy (data, GST_BUFFER_DATA (buf) + (line * src_pitch), src_pitch);
894       data += surf_desc.lPitch;
895     }
896
897     /* Unlock the surface */
898     hRes = IDirectDrawSurface7_Unlock (ddrawsink->offscreen_surface, NULL);
899     if (hRes != DD_OK) {
900       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
901           "gst_directdraw_sink_show_frame failed unlocking surface %s",
902           DDErrorString (hRes));
903       return GST_FLOW_ERROR;
904     }
905
906     /* blit to primary surface ( Blt will scale the video the dest rect surface
907      * if needed */
908     hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
909         ddrawsink->offscreen_surface, NULL, DDBLT_WAIT, NULL);
910     if (hRes != DD_OK)          /* FIXME: Is it really safe to continue past here ? */
911       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
912           "IDirectDrawSurface7_Blt (object's offscreen surface) " "returned %s",
913           DDErrorString (hRes));
914
915   } else {
916     /* We are receiving a directdraw surface (previously returned by our buffer
917      * pool so we will simply blit it on the primary surface */
918     GstDDrawSurface *surface = NULL;
919
920     surface = GST_DDRAWSURFACE (buf);
921
922     /* Unlocking surface before blit */
923     IDirectDrawSurface7_Unlock (surface->surface, NULL);
924     surface->locked = FALSE;
925
926     /* blit to our primary surface */
927     hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
928         surface->surface, NULL, DDBLT_WAIT, NULL);
929     if (hRes != DD_OK)          /* FIXME: Is it really safe to continue past here ? */
930       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
931           "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
932           "returned %s", DDErrorString (hRes));
933   }
934
935   return GST_FLOW_OK;
936 }
937
938 static void
939 gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
940     GstClockTime * start, GstClockTime * end)
941 {
942   GstDirectDrawSink *ddrawsink;
943
944   ddrawsink = GST_DIRECTDRAW_SINK (bsink);
945
946   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
947     *start = GST_BUFFER_TIMESTAMP (buf);
948     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
949       *end = *start + GST_BUFFER_DURATION (buf);
950     } else {
951       if (ddrawsink->fps_n > 0) {
952         *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
953       }
954     }
955   }
956 }
957
958 /* Utility functions */
959
960 /* this function fill a DDPIXELFORMAT using Gstreamer caps */
961 static gboolean
962 gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
963     GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
964 {
965   GstStructure *structure = NULL;
966   gboolean ret = TRUE;
967
968   /* check params */
969   g_return_val_if_fail (pPixelFormat, FALSE);
970   g_return_val_if_fail (caps, FALSE);
971
972   /* init structure */
973   memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
974   pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
975
976   if (!(structure = gst_caps_get_structure (caps, 0))) {
977     GST_CAT_ERROR_OBJECT (directdrawsink_debug, ddrawsink,
978         "can't get structure pointer from caps");
979     return FALSE;
980   }
981
982   if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
983     gint depth, bitcount, bitmask, endianness;
984
985     pPixelFormat->dwFlags = DDPF_RGB;
986     ret &= gst_structure_get_int (structure, "bpp", &bitcount);
987     pPixelFormat->dwRGBBitCount = bitcount;
988     ret &= gst_structure_get_int (structure, "depth", &depth);
989     ret &= gst_structure_get_int (structure, "red_mask", &bitmask);
990     pPixelFormat->dwRBitMask = bitmask;
991     ret &= gst_structure_get_int (structure, "green_mask", &bitmask);
992     pPixelFormat->dwGBitMask = bitmask;
993     ret &= gst_structure_get_int (structure, "blue_mask", &bitmask);
994     pPixelFormat->dwBBitMask = bitmask;
995
996     gst_structure_get_int (structure, "endianness", &endianness);
997     if (endianness == G_BIG_ENDIAN) {
998       endianness = G_LITTLE_ENDIAN;
999       pPixelFormat->dwRBitMask = GUINT32_TO_BE (pPixelFormat->dwRBitMask);
1000       pPixelFormat->dwGBitMask = GUINT32_TO_BE (pPixelFormat->dwGBitMask);
1001       pPixelFormat->dwBBitMask = GUINT32_TO_BE (pPixelFormat->dwBBitMask);
1002     }
1003   } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
1004     gint fourcc;
1005
1006     pPixelFormat->dwFlags = DDPF_FOURCC;
1007     ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
1008     pPixelFormat->dwFourCC = fourcc;
1009   } else {
1010     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1011         "unknown caps name received %" GST_PTR_FORMAT, caps);
1012     ret = FALSE;
1013   }
1014
1015   return ret;
1016 }
1017
1018 /* This function centers the RECT of source surface to
1019 a dest surface and set the result RECT into result */
1020 static void
1021 gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
1022     RECT dst, RECT * result)
1023 {
1024   gdouble src_ratio, dst_ratio;
1025   long src_width = src.right;
1026   long src_height = src.bottom;
1027   long dst_width = dst.right - dst.left;
1028   long dst_heigth = dst.bottom - dst.top;
1029   long result_width = 0, result_height = 0;
1030
1031   g_return_if_fail (result != NULL);
1032
1033   src_ratio = (gdouble) src_width / src_height;
1034   dst_ratio = (gdouble) dst_width / dst_heigth;
1035
1036   if (src_ratio > dst_ratio) {
1037     /* new height */
1038     result_height = (long) (dst_width / src_ratio);
1039
1040     result->left = dst.left;
1041     result->right = dst.right;
1042     result->top = dst.top + (dst_heigth - result_height) / 2;
1043     result->bottom = result->top + result_height;
1044
1045   } else if (src_ratio < dst_ratio) {
1046     /* new width */
1047     result_width = (long) (dst_heigth * src_ratio);
1048
1049     result->top = dst.top;
1050     result->bottom = dst.bottom;
1051     result->left = dst.left + (dst_width - result_width) / 2;
1052     result->right = result->left + result_width;
1053
1054   } else {
1055     /* same ratio */
1056     memcpy (result, &dst, sizeof (RECT));
1057   }
1058
1059   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1060       "source is %ldx%ld dest is %ldx%ld, result is %ldx%ld with x,y %ldx%ld",
1061       src_width, src_height, dst_width, dst_heigth,
1062       result->right - result->left, result->bottom - result->top, result->left,
1063       result->right);
1064 }
1065
1066 /**
1067  * Get DirectDraw error message.
1068  * @hr: HRESULT code
1069  * Returns: Text representation of the error.
1070  */
1071 char *
1072 DDErrorString (HRESULT hr)
1073 {
1074   switch (hr) {
1075     case DDERR_ALREADYINITIALIZED:
1076       return "DDERR_ALREADYINITIALIZED";
1077     case DDERR_CANNOTATTACHSURFACE:
1078       return "DDERR_CANNOTATTACHSURFACE";
1079     case DDERR_CANNOTDETACHSURFACE:
1080       return "DDERR_CANNOTDETACHSURFACE";
1081     case DDERR_CURRENTLYNOTAVAIL:
1082       return "DDERR_CURRENTLYNOTAVAIL";
1083     case DDERR_EXCEPTION:
1084       return "DDERR_EXCEPTION";
1085     case DDERR_GENERIC:
1086       return "DDERR_GENERIC";
1087     case DDERR_HEIGHTALIGN:
1088       return "DDERR_HEIGHTALIGN";
1089     case DDERR_INCOMPATIBLEPRIMARY:
1090       return "DDERR_INCOMPATIBLEPRIMARY";
1091     case DDERR_INVALIDCAPS:
1092       return "DDERR_INVALIDCAPS";
1093     case DDERR_INVALIDCLIPLIST:
1094       return "DDERR_INVALIDCLIPLIST";
1095     case DDERR_INVALIDMODE:
1096       return "DDERR_INVALIDMODE";
1097     case DDERR_INVALIDOBJECT:
1098       return "DDERR_INVALIDOBJECT";
1099     case DDERR_INVALIDPARAMS:
1100       return "DDERR_INVALIDPARAMS";
1101     case DDERR_INVALIDPIXELFORMAT:
1102       return "DDERR_INVALIDPIXELFORMAT";
1103     case DDERR_INVALIDRECT:
1104       return "DDERR_INVALIDRECT";
1105     case DDERR_LOCKEDSURFACES:
1106       return "DDERR_LOCKEDSURFACES";
1107     case DDERR_NO3D:
1108       return "DDERR_NO3D";
1109     case DDERR_NOALPHAHW:
1110       return "DDERR_NOALPHAHW";
1111     case DDERR_NOCLIPLIST:
1112       return "DDERR_NOCLIPLIST";
1113     case DDERR_NOCOLORCONVHW:
1114       return "DDERR_NOCOLORCONVHW";
1115     case DDERR_NOCOOPERATIVELEVELSET:
1116       return "DDERR_NOCOOPERATIVELEVELSET";
1117     case DDERR_NOCOLORKEY:
1118       return "DDERR_NOCOLORKEY";
1119     case DDERR_NOCOLORKEYHW:
1120       return "DDERR_NOCOLORKEYHW";
1121     case DDERR_NODIRECTDRAWSUPPORT:
1122       return "DDERR_NODIRECTDRAWSUPPORT";
1123     case DDERR_NOEXCLUSIVEMODE:
1124       return "DDERR_NOEXCLUSIVEMODE";
1125     case DDERR_NOFLIPHW:
1126       return "DDERR_NOFLIPHW";
1127     case DDERR_NOGDI:
1128       return "DDERR_NOGDI";
1129     case DDERR_NOMIRRORHW:
1130       return "DDERR_NOMIRRORHW";
1131     case DDERR_NOTFOUND:
1132       return "DDERR_NOTFOUND";
1133     case DDERR_NOOVERLAYHW:
1134       return "DDERR_NOOVERLAYHW";
1135     case DDERR_NORASTEROPHW:
1136       return "DDERR_NORASTEROPHW";
1137     case DDERR_NOROTATIONHW:
1138       return "DDERR_NOROTATIONHW";
1139     case DDERR_NOSTRETCHHW:
1140       return "DDERR_NOSTRETCHHW";
1141     case DDERR_NOT4BITCOLOR:
1142       return "DDERR_NOT4BITCOLOR";
1143     case DDERR_NOT4BITCOLORINDEX:
1144       return "DDERR_NOT4BITCOLORINDEX";
1145     case DDERR_NOT8BITCOLOR:
1146       return "DDERR_NOT8BITCOLOR";
1147     case DDERR_NOTEXTUREHW:
1148       return "DDERR_NOTEXTUREHW";
1149     case DDERR_NOVSYNCHW:
1150       return "DDERR_NOVSYNCHW";
1151     case DDERR_NOZBUFFERHW:
1152       return "DDERR_NOZBUFFERHW";
1153     case DDERR_NOZOVERLAYHW:
1154       return "DDERR_NOZOVERLAYHW";
1155     case DDERR_OUTOFCAPS:
1156       return "DDERR_OUTOFCAPS";
1157     case DDERR_OUTOFMEMORY:
1158       return "DDERR_OUTOFMEMORY";
1159     case DDERR_OUTOFVIDEOMEMORY:
1160       return "DDERR_OUTOFVIDEOMEMORY";
1161     case DDERR_OVERLAYCANTCLIP:
1162       return "DDERR_OVERLAYCANTCLIP";
1163     case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
1164       return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE";
1165     case DDERR_PALETTEBUSY:
1166       return "DDERR_PALETTEBUSY";
1167     case DDERR_COLORKEYNOTSET:
1168       return "DDERR_COLORKEYNOTSET";
1169     case DDERR_SURFACEALREADYATTACHED:
1170       return "DDERR_SURFACEALREADYATTACHED";
1171     case DDERR_SURFACEALREADYDEPENDENT:
1172       return "DDERR_SURFACEALREADYDEPENDENT";
1173     case DDERR_SURFACEBUSY:
1174       return "DDERR_SURFACEBUSY";
1175     case DDERR_CANTLOCKSURFACE:
1176       return "DDERR_CANTLOCKSURFACE";
1177     case DDERR_SURFACEISOBSCURED:
1178       return "DDERR_SURFACEISOBSCURED";
1179     case DDERR_SURFACELOST:
1180       return "DDERR_SURFACELOST";
1181     case DDERR_SURFACENOTATTACHED:
1182       return "DDERR_SURFACENOTATTACHED";
1183     case DDERR_TOOBIGHEIGHT:
1184       return "DDERR_TOOBIGHEIGHT";
1185     case DDERR_TOOBIGSIZE:
1186       return "DDERR_TOOBIGSIZE";
1187     case DDERR_TOOBIGWIDTH:
1188       return "DDERR_TOOBIGWIDTH";
1189     case DDERR_UNSUPPORTED:
1190       return "DDERR_UNSUPPORTED";
1191     case DDERR_UNSUPPORTEDFORMAT:
1192       return "DDERR_UNSUPPORTEDFORMAT";
1193     case DDERR_UNSUPPORTEDMASK:
1194       return "DDERR_UNSUPPORTEDMASK";
1195     case DDERR_VERTICALBLANKINPROGRESS:
1196       return "DDERR_VERTICALBLANKINPROGRESS";
1197     case DDERR_WASSTILLDRAWING:
1198       return "DDERR_WASSTILLDRAWING";
1199     case DDERR_XALIGN:
1200       return "DDERR_XALIGN";
1201     case DDERR_INVALIDDIRECTDRAWGUID:
1202       return "DDERR_INVALIDDIRECTDRAWGUID";
1203     case DDERR_DIRECTDRAWALREADYCREATED:
1204       return "DDERR_DIRECTDRAWALREADYCREATED";
1205     case DDERR_NODIRECTDRAWHW:
1206       return "DDERR_NODIRECTDRAWHW";
1207     case DDERR_PRIMARYSURFACEALREADYEXISTS:
1208       return "DDERR_PRIMARYSURFACEALREADYEXISTS";
1209     case DDERR_NOEMULATION:
1210       return "DDERR_NOEMULATION";
1211     case DDERR_REGIONTOOSMALL:
1212       return "DDERR_REGIONTOOSMALL";
1213     case DDERR_CLIPPERISUSINGHWND:
1214       return "DDERR_CLIPPERISUSINGHWND";
1215     case DDERR_NOCLIPPERATTACHED:
1216       return "DDERR_NOCLIPPERATTACHED";
1217     case DDERR_NOHWND:
1218       return "DDERR_NOHWND";
1219     case DDERR_HWNDSUBCLASSED:
1220       return "DDERR_HWNDSUBCLASSED";
1221     case DDERR_HWNDALREADYSET:
1222       return "DDERR_HWNDALREADYSET";
1223     case DDERR_NOPALETTEATTACHED:
1224       return "DDERR_NOPALETTEATTACHED";
1225     case DDERR_NOPALETTEHW:
1226       return "DDERR_NOPALETTEHW";
1227     case DDERR_BLTFASTCANTCLIP:
1228       return "DDERR_BLTFASTCANTCLIP";
1229     case DDERR_NOBLTHW:
1230       return "DDERR_NOBLTHW";
1231     case DDERR_NODDROPSHW:
1232       return "DDERR_NODDROPSHW";
1233     case DDERR_OVERLAYNOTVISIBLE:
1234       return "DDERR_OVERLAYNOTVISIBLE";
1235     case DDERR_NOOVERLAYDEST:
1236       return "DDERR_NOOVERLAYDEST";
1237     case DDERR_INVALIDPOSITION:
1238       return "DDERR_INVALIDPOSITION";
1239     case DDERR_NOTAOVERLAYSURFACE:
1240       return "DDERR_NOTAOVERLAYSURFACE";
1241     case DDERR_EXCLUSIVEMODEALREADYSET:
1242       return "DDERR_EXCLUSIVEMODEALREADYSET";
1243     case DDERR_NOTFLIPPABLE:
1244       return "DDERR_NOTFLIPPABLE";
1245     case DDERR_CANTDUPLICATE:
1246       return "DDERR_CANTDUPLICATE";
1247     case DDERR_NOTLOCKED:
1248       return "DDERR_NOTLOCKED";
1249     case DDERR_CANTCREATEDC:
1250       return "DDERR_CANTCREATEDC";
1251     case DDERR_NODC:
1252       return "DDERR_NODC";
1253     case DDERR_WRONGMODE:
1254       return "DDERR_WRONGMODE";
1255     case DDERR_IMPLICITLYCREATED:
1256       return "DDERR_IMPLICITLYCREATED";
1257     case DDERR_NOTPALETTIZED:
1258       return "DDERR_NOTPALETTIZED";
1259     case DDERR_UNSUPPORTEDMODE:
1260       return "DDERR_UNSUPPORTEDMODE";
1261     case DDERR_NOMIPMAPHW:
1262       return "DDERR_NOMIPMAPHW";
1263     case DDERR_INVALIDSURFACETYPE:
1264       return "DDERR_INVALIDSURFACETYPE";
1265     case DDERR_DCALREADYCREATED:
1266       return "DDERR_DCALREADYCREATED";
1267     case DDERR_CANTPAGELOCK:
1268       return "DDERR_CANTPAGELOCK";
1269     case DDERR_CANTPAGEUNLOCK:
1270       return "DDERR_CANTPAGEUNLOCK";
1271     case DDERR_NOTPAGELOCKED:
1272       return "DDERR_NOTPAGELOCKED";
1273     case DDERR_NOTINITIALIZED:
1274       return "DDERR_NOTINITIALIZED";
1275   }
1276   return "Unknown Error";
1277 }
1278
1279 static gboolean
1280 gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
1281 {
1282   gboolean bRet = TRUE;
1283   HRESULT hRes;
1284
1285   /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first
1286    * parameter to force Directdraw to use the hardware emulation layer */
1287   hRes = DirectDrawCreateEx ( /*DDCREATE_EMULATIONONLY */ 0,
1288       (void **) &ddrawsink->ddraw_object, &IID_IDirectDraw7, NULL);
1289   if (hRes != DD_OK || ddrawsink->ddraw_object == NULL) {
1290     GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1291         ("Failed to create the DirectDraw object error=%s",
1292             DDErrorString (hRes)), (NULL));
1293     return FALSE;
1294   }
1295
1296   /* set cooperative level */
1297   hRes = IDirectDraw7_SetCooperativeLevel (ddrawsink->ddraw_object,
1298       NULL, DDSCL_NORMAL);
1299   if (hRes != DD_OK) {
1300     GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1301         ("Failed to set the set the cooperative level error=%s",
1302             DDErrorString (hRes)), (NULL));
1303     return FALSE;
1304   }
1305
1306   /* setup the clipper object */
1307   hRes = IDirectDraw7_CreateClipper (ddrawsink->ddraw_object, 0,
1308       &ddrawsink->clipper, NULL);
1309
1310   if (hRes == DD_OK && ddrawsink->video_window)
1311     IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
1312
1313   /* create our primary surface */
1314   if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
1315     return FALSE;
1316
1317   /* directdraw objects are setup */
1318   ddrawsink->setup = TRUE;
1319
1320   return bRet;
1321 }
1322
1323 long FAR PASCAL
1324 WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1325 {
1326   switch (message) {
1327     case WM_ERASEBKGND:
1328       return TRUE;
1329     case WM_CLOSE:
1330       DestroyWindow (hWnd);
1331     case WM_DESTROY:
1332       PostQuitMessage (0);
1333       return 0;
1334   }
1335
1336   return DefWindowProc (hWnd, message, wParam, lParam);
1337 }
1338
1339 static gpointer
1340 gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
1341 {
1342   WNDCLASS WndClass;
1343   MSG msg;
1344
1345   memset (&WndClass, 0, sizeof (WNDCLASS));
1346   WndClass.style = CS_HREDRAW | CS_VREDRAW;
1347   WndClass.hInstance = GetModuleHandle (NULL);
1348   WndClass.lpszClassName = "GStreamer-DirectDraw";
1349   WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
1350   WndClass.cbClsExtra = 0;
1351   WndClass.cbWndExtra = 0;
1352   WndClass.lpfnWndProc = WndProc;
1353   WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
1354   RegisterClass (&WndClass);
1355
1356   ddrawsink->video_window = CreateWindowEx (0, "GStreamer-DirectDraw",
1357       "GStreamer-DirectDraw sink default window",
1358       WS_OVERLAPPEDWINDOW | WS_SIZEBOX, 0, 0, 640, 480, NULL, NULL,
1359       WndClass.hInstance, NULL);
1360   if (ddrawsink->video_window == NULL)
1361     return NULL;
1362
1363   /* Set the clipper on that window */
1364   IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
1365
1366   /* signal application we created a window */
1367   gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ddrawsink),
1368       (gulong) ddrawsink->video_window);
1369
1370   ReleaseSemaphore (ddrawsink->window_created_signal, 1, NULL);
1371
1372   /* start message loop processing our default window messages */
1373   while (GetMessage (&msg, NULL, 0, 0) != FALSE) {
1374     TranslateMessage (&msg);
1375     DispatchMessage (&msg);
1376   }
1377
1378   GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1379       "our window received WM_QUIT or error.");
1380   /* The window could have changed, if it is not ours anymore we don't
1381    * overwrite the current video window with NULL */
1382   if (ddrawsink->our_video_window) {
1383     GST_OBJECT_LOCK (ddrawsink);
1384     ddrawsink->video_window = NULL;
1385     GST_OBJECT_UNLOCK (ddrawsink);
1386   }
1387
1388   return NULL;
1389 }
1390
1391 static gboolean
1392 gst_directdraw_sink_create_default_window (GstDirectDrawSink * ddrawsink)
1393 {
1394   ddrawsink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);
1395   if (ddrawsink->window_created_signal == NULL)
1396     return FALSE;
1397
1398   ddrawsink->window_thread = g_thread_create (
1399       (GThreadFunc) gst_directdraw_sink_window_thread, ddrawsink, TRUE, NULL);
1400
1401   if (ddrawsink->window_thread == NULL)
1402     goto failed;
1403
1404   /* wait maximum 10 seconds for windows creating */
1405   if (WaitForSingleObject (ddrawsink->window_created_signal,
1406           10000) != WAIT_OBJECT_0)
1407     goto failed;
1408
1409   CloseHandle (ddrawsink->window_created_signal);
1410   return TRUE;
1411
1412 failed:
1413   CloseHandle (ddrawsink->window_created_signal);
1414   GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1415       ("Error creating our default window"), (NULL));
1416
1417   return FALSE;
1418 }
1419
1420 static gboolean
1421 gst_directdraw_sink_check_primary_surface (GstDirectDrawSink * ddrawsink)
1422 {
1423   HRESULT hres;
1424   DDSURFACEDESC2 dd_surface_desc;
1425   DDSURFACEDESC2 *sd;
1426
1427   /* if our primary surface already exist, check if it's not lost */
1428   if (ddrawsink->primary_surface) {
1429     if (IDirectDrawSurface7_IsLost (ddrawsink->primary_surface) == DD_OK) {
1430       /* no problem with our primary surface */
1431       return TRUE;
1432     } else {
1433       /* our primary surface was lost, try to restore it */
1434       if (IDirectDrawSurface7_Restore (ddrawsink->primary_surface) == DD_OK) {
1435         /* restore is done */
1436         GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1437             "Our primary surface" " was restored after lost");
1438         return TRUE;
1439       } else {
1440         /* failed to restore our primary surface, 
1441          * probably because the display mode was changed. 
1442          * Release this surface and recreate a new one.
1443          */
1444         GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1445             "Our primary surface"
1446             " was lost and display mode has changed. Destroy and recreate our surface.");
1447         IDirectDrawSurface7_Release (ddrawsink->primary_surface);
1448         ddrawsink->primary_surface = NULL;
1449
1450         /* also release offscreen surface */
1451         IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1452         ddrawsink->offscreen_surface = NULL;
1453       }
1454     }
1455   }
1456
1457   /* create our primary surface */
1458   memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
1459   dd_surface_desc.dwSize = sizeof (dd_surface_desc);
1460   dd_surface_desc.dwFlags = DDSD_CAPS;
1461   dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
1462   sd = &dd_surface_desc;
1463   hres =
1464       IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
1465       &ddrawsink->primary_surface, NULL);
1466   if (hres != DD_OK) {
1467     GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
1468         ("Failed to create our primary surface error=%s", DDErrorString (hres)),
1469         (NULL));
1470     return FALSE;
1471   }
1472
1473   /* attach our clipper object to the new primary surface */
1474   if (ddrawsink->clipper) {
1475     hres = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
1476         ddrawsink->clipper);
1477   }
1478
1479   return TRUE;
1480 }
1481
1482 static gboolean
1483 gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink * ddrawsink)
1484 {
1485   DDSURFACEDESC2 dd_surface_desc;
1486   DDSURFACEDESC2 *sd;
1487   HRESULT hres;
1488
1489   /* if our offscreen surface already exist, check if it's not lost */
1490   if (ddrawsink->offscreen_surface) {
1491     if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK) {
1492       /* no problem with our offscreen surface */
1493       return TRUE;
1494     } else {
1495       /* our offscreen surface was lost, try to restore it */
1496       if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
1497         /* restore is done */
1498         GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1499             "Our offscreen surface" " was restored after lost");
1500         return TRUE;
1501       } else {
1502         /* failed to restore our offscreen surface, 
1503          * probably because the display mode was changed. 
1504          * Release this surface and recreate a new one.
1505          */
1506         GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
1507             "Our offscreen surface"
1508             " was lost and display mode has changed. Destroy and recreate our surface.");
1509         IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1510         ddrawsink->offscreen_surface = NULL;
1511       }
1512     }
1513   }
1514
1515   memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
1516   dd_surface_desc.dwSize = sizeof (dd_surface_desc);
1517   dd_surface_desc.dwFlags =
1518       DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
1519   dd_surface_desc.dwHeight = ddrawsink->video_height;
1520   dd_surface_desc.dwWidth = ddrawsink->video_width;
1521   memcpy (&(dd_surface_desc.ddpfPixelFormat), &ddrawsink->dd_pixel_format,
1522       sizeof (DDPIXELFORMAT));
1523
1524   dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
1525   sd = &dd_surface_desc;
1526   hres =
1527       IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, (DDSURFACEDESC *) sd,
1528       &ddrawsink->offscreen_surface, NULL);
1529   if (hres != DD_OK) {
1530     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1531         "create_ddraw_surface:CreateSurface (offscreen surface for buffer_pool) failed %s",
1532         DDErrorString (hres));
1533     return FALSE;
1534   }
1535
1536   ddrawsink->must_recreate_offscreen = FALSE;
1537
1538   return TRUE;
1539 }
1540
1541 static int
1542 gst_directdraw_sink_get_depth (LPDDPIXELFORMAT lpddpfPixelFormat)
1543 {
1544   gint order = 0, binary;
1545
1546   binary =
1547       lpddpfPixelFormat->dwRBitMask | lpddpfPixelFormat->
1548       dwGBitMask | lpddpfPixelFormat->dwBBitMask | lpddpfPixelFormat->
1549       dwRGBAlphaBitMask;
1550   while (binary != 0) {
1551     if ((binary % 2) == 1)
1552       order++;
1553     binary = binary >> 1;
1554   }
1555   return order;
1556 }
1557
1558 HRESULT WINAPI
1559 EnumModesCallback2 (LPDDSURFACEDESC lpDDSurfaceDesc, LPVOID lpContext)
1560 {
1561   GstDirectDrawSink *ddrawsink = (GstDirectDrawSink *) lpContext;
1562   GstCaps *format_caps = NULL;
1563   LPDDSURFACEDESC2 sd;
1564
1565   if (!ddrawsink || !lpDDSurfaceDesc)
1566     return DDENUMRET_CANCEL;
1567
1568   sd = (LPDDSURFACEDESC2) lpDDSurfaceDesc;
1569   if ((sd->dwFlags & DDSD_PIXELFORMAT) != DDSD_PIXELFORMAT) {
1570     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1571         "Display mode found with DDSD_PIXELFORMAT not set");
1572     return DDENUMRET_OK;
1573   }
1574
1575   if ((sd->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1576     return DDENUMRET_OK;
1577
1578   format_caps = gst_directdraw_sink_create_caps_from_surfacedesc (sd);
1579
1580   if (format_caps) {
1581     gst_caps_append (ddrawsink->caps, format_caps);
1582   }
1583
1584   return DDENUMRET_OK;
1585 }
1586
1587 static GstCaps *
1588 gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc)
1589 {
1590   GstCaps *caps = NULL;
1591   gint endianness = G_LITTLE_ENDIAN;
1592   gint depth;
1593
1594   if ((desc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
1595     return NULL;
1596
1597   depth = gst_directdraw_sink_get_depth (&desc->ddpfPixelFormat);
1598
1599   if (desc->ddpfPixelFormat.dwRGBBitCount == 24 ||
1600       desc->ddpfPixelFormat.dwRGBBitCount == 32) {
1601     /* ffmpegcolorspace handles 24/32 bpp RGB as big-endian. */
1602     endianness = G_BIG_ENDIAN;
1603     desc->ddpfPixelFormat.dwRBitMask =
1604         GUINT32_TO_BE (desc->ddpfPixelFormat.dwRBitMask);
1605     desc->ddpfPixelFormat.dwGBitMask =
1606         GUINT32_TO_BE (desc->ddpfPixelFormat.dwGBitMask);
1607     desc->ddpfPixelFormat.dwBBitMask =
1608         GUINT32_TO_BE (desc->ddpfPixelFormat.dwBBitMask);
1609     if (desc->ddpfPixelFormat.dwRGBBitCount == 24) {
1610       desc->ddpfPixelFormat.dwRBitMask >>= 8;
1611       desc->ddpfPixelFormat.dwGBitMask >>= 8;
1612       desc->ddpfPixelFormat.dwBBitMask >>= 8;
1613     }
1614   }
1615
1616   caps = gst_caps_new_simple ("video/x-raw-rgb",
1617       "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1618       "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1619       "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
1620       "bpp", G_TYPE_INT, desc->ddpfPixelFormat.dwRGBBitCount,
1621       "depth", G_TYPE_INT, depth,
1622       "endianness", G_TYPE_INT, endianness,
1623       "red_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwRBitMask,
1624       "green_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwGBitMask,
1625       "blue_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwBBitMask, NULL);
1626
1627   return caps;
1628 }
1629
1630 static GstCaps *
1631 gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
1632 {
1633   HRESULT hRes = S_OK;
1634   DDCAPS ddcaps_hardware;
1635   DDCAPS ddcaps_emulation;
1636   GstCaps *format_caps = NULL;
1637
1638   ddrawsink->caps = gst_caps_new_empty ();
1639   if (!ddrawsink->caps)
1640     return FALSE;
1641
1642   /* get hardware caps */
1643   ddcaps_hardware.dwSize = sizeof (DDCAPS);
1644   ddcaps_emulation.dwSize = sizeof (DDCAPS);
1645   IDirectDraw7_GetCaps (ddrawsink->ddraw_object, &ddcaps_hardware,
1646       &ddcaps_emulation);
1647
1648   /* we don't test for DDCAPS_BLTSTRETCH on the hardware as the directdraw 
1649    * emulation layer can do it */
1650   if (!(ddcaps_hardware.dwCaps & DDCAPS_BLTFOURCC)) {
1651     DDSURFACEDESC2 surface_desc;
1652     DDSURFACEDESC2 *sd;
1653
1654     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1655         "hardware doesn't support blit from one colorspace to another one. "
1656         "so we will create a caps with only the current display mode");
1657
1658     /* save blit caps */
1659     ddrawsink->can_blit_between_colorspace = FALSE;
1660
1661     surface_desc.dwSize = sizeof (surface_desc);
1662     sd = &surface_desc;
1663     hRes =
1664         IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object,
1665         (DDSURFACEDESC *) sd);
1666     if (hRes != DD_OK) {
1667       GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1668           ("Error getting the current display mode error=%s",
1669               DDErrorString (hRes)), (NULL));
1670       return NULL;
1671     }
1672
1673     format_caps =
1674         gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
1675     if (format_caps) {
1676       gst_caps_append (ddrawsink->caps, format_caps);
1677     }
1678
1679     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
1680         gst_caps_to_string (ddrawsink->caps));
1681     return ddrawsink->caps;
1682   }
1683
1684   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1685       "the hardware can blit from one colorspace to another, "
1686       "then enumerate the colorspace supported by the hardware");
1687
1688   /* save blit caps */
1689   ddrawsink->can_blit_between_colorspace = TRUE;
1690
1691   /* enumerate display modes exposed by directdraw object 
1692      to know supported RGB modes */
1693   hRes =
1694       IDirectDraw7_EnumDisplayModes (ddrawsink->ddraw_object,
1695       DDEDM_REFRESHRATES, NULL, ddrawsink, EnumModesCallback2);
1696   if (hRes != DD_OK) {
1697     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1698         ("Error enumerating display modes error=%s", DDErrorString (hRes)),
1699         (NULL));
1700
1701     return NULL;
1702   }
1703
1704   if (gst_caps_is_empty (ddrawsink->caps)) {
1705     gst_caps_unref (ddrawsink->caps);
1706     ddrawsink->caps = NULL;
1707     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
1708         ("No supported caps found."), (NULL));
1709     return NULL;
1710   }
1711
1712   /*GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s",
1713    * gst_caps_to_string (ddrawsink->caps)); */
1714
1715   return ddrawsink->caps;
1716 }
1717
1718 /* Creates miniobject and our internal surface */
1719 static GstDDrawSurface *
1720 gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
1721     GstCaps * caps, size_t size)
1722 {
1723   GstDDrawSurface *surface = NULL;
1724   GstStructure *structure = NULL;
1725   gint pitch;
1726
1727 #if 0
1728   HRESULT hRes;
1729 #endif
1730   DDSURFACEDESC2 surf_desc, surf_lock_desc;
1731
1732   g_return_val_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink), NULL);
1733
1734   /*init structures */
1735   memset (&surf_desc, 0, sizeof (surf_desc));
1736   memset (&surf_lock_desc, 0, sizeof (surf_desc));
1737   surf_desc.dwSize = sizeof (surf_desc);
1738   surf_lock_desc.dwSize = sizeof (surf_lock_desc);
1739
1740   /*create miniobject and initialize it */
1741   surface = (GstDDrawSurface *) gst_mini_object_new (GST_TYPE_DDRAWSURFACE);
1742   surface->locked = FALSE;
1743
1744   structure = gst_caps_get_structure (caps, 0);
1745   if (!gst_structure_get_int (structure, "width", &surface->width) ||
1746       !gst_structure_get_int (structure, "height", &surface->height)) {
1747     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1748         "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
1749   }
1750
1751   pitch = GST_ROUND_UP_8 (size / surface->height);
1752   if (!gst_ddrawvideosink_get_format_from_caps (ddrawsink, caps,
1753           &surface->dd_pixel_format)) {
1754     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
1755         "failed getting pixel format from caps %" GST_PTR_FORMAT, caps);
1756   }
1757
1758   /* disable return of directdraw surface to buffer alloc because actually I
1759    * have no solution to handle display mode changes. The problem is that when
1760    * the display mode is changed surface's memory is freed then the upstream
1761    * filter would crash trying to write to this memory. Directdraw has a system
1762    * lock (DDLOCK_NOSYSLOCK to disable it) to prevent display mode changes 
1763    * when a surface memory is locked but we need to disable this lock to return
1764    * multiple buffers (surfaces) and do not lock directdraw API calls.
1765    */
1766 #if 0
1767 /*  if (ddrawsink->ddraw_object) {*/
1768   /* Creating an internal surface which will be used as GstBuffer, we used
1769      the detected pixel format and video dimensions */
1770
1771   surf_desc.ddsCaps.dwCaps =
1772       DDSCAPS_OFFSCREENPLAIN /* | DDSCAPS_SYSTEMMEMORY */ ;
1773   surf_desc.dwFlags =
1774       DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT | DDSD_PITCH;
1775   surf_desc.dwHeight = surface->height;
1776   surf_desc.dwWidth = surface->width;
1777   memcpy (&(surf_desc.ddpfPixelFormat), &surface->dd_pixel_format,
1778       sizeof (DDPIXELFORMAT));
1779
1780   hRes = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &surf_desc,
1781       &surface->surface, NULL);
1782   if (hRes != DD_OK) {
1783     goto surface_pitch_bad;
1784   }
1785
1786   /* Locking the surface to acquire the memory pointer.
1787      Use DDLOCK_NOSYSLOCK to disable syslock which can cause a deadlock 
1788      if directdraw api is used while a buffer is lock */
1789 lock:
1790   hRes = IDirectDrawSurface7_Lock (surface->surface, NULL, &surf_lock_desc,
1791       DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
1792   if (hRes == DDERR_SURFACELOST) {
1793     IDirectDrawSurface7_Restore (surface->surface);
1794     goto lock;
1795   }
1796   surface->locked = TRUE;
1797
1798   if (surf_lock_desc.lPitch != pitch) {
1799     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1800         "DDraw stride/pitch %ld isn't as expected value %d, let's continue allocating a system memory buffer.",
1801         surf_lock_desc.lPitch, pitch);
1802
1803     /*Unlock the surface as we will change it to use system memory with a GStreamer compatible pitch */
1804     hRes = IDirectDrawSurface_Unlock (surface->surface, NULL);
1805     goto surface_pitch_bad;
1806   }
1807   GST_BUFFER_DATA (surface) = surf_lock_desc.lpSurface;
1808   GST_BUFFER_SIZE (surface) = surf_lock_desc.lPitch * surface->height;
1809   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1810       "allocating a surface of %d bytes (stride=%ld)\n", size,
1811       surf_lock_desc.lPitch);
1812
1813 surface_pitch_bad:
1814 #else
1815   GST_BUFFER (surface)->malloc_data = g_malloc (size);
1816   GST_BUFFER_DATA (surface) = GST_BUFFER (surface)->malloc_data;
1817   GST_BUFFER_SIZE (surface) = size;
1818   surface->surface = NULL;
1819   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
1820       "allocating a system memory buffer of %d bytes", size);
1821
1822 #endif
1823
1824   /* Keep a ref to our sink */
1825   surface->ddrawsink = gst_object_ref (ddrawsink);
1826
1827   return surface;
1828 }
1829
1830 /* We are called from the finalize method of miniobject, the object will be
1831  * destroyed so we just have to clean our internal stuff */
1832 static void
1833 gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
1834     GstDDrawSurface * surface)
1835 {
1836   g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
1837
1838   /* Release our internal surface */
1839   if (surface->surface) {
1840     if (surface->locked) {
1841       IDirectDrawSurface7_Unlock (surface->surface, NULL);
1842       surface->locked = FALSE;
1843     }
1844     IDirectDrawSurface7_Release (surface->surface);
1845     surface->surface = NULL;
1846   }
1847
1848   if (GST_BUFFER (surface)->malloc_data) {
1849     g_free (GST_BUFFER (surface)->malloc_data);
1850     GST_BUFFER (surface)->malloc_data = NULL;
1851   }
1852
1853   if (!surface->ddrawsink) {
1854     goto no_sink;
1855   }
1856
1857   /* Release the ref to our sink */
1858   surface->ddrawsink = NULL;
1859   gst_object_unref (ddrawsink);
1860
1861   return;
1862
1863 no_sink:
1864   GST_WARNING ("no sink found in surface");
1865   return;
1866 }
1867
1868 static gboolean
1869 gst_directdraw_sink_surface_check (GstDirectDrawSink * ddrawsink,
1870     GstDDrawSurface * surface)
1871 {
1872   if (!surface->surface)
1873     return TRUE;                /* system memory buffer */
1874
1875   if (IDirectDrawSurface7_IsLost (surface->surface) == DD_OK) {
1876     /* no problem with this surface */
1877     return TRUE;
1878   } else {
1879     /* this surface was lost, try to restore it */
1880     if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
1881       /* restore is done */
1882       GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink, "A surface from our"
1883           " bufferpool was restored after lost");
1884       return TRUE;
1885     }
1886   }
1887
1888   return FALSE;
1889 }
1890
1891 static void
1892 gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink * ddrawsink)
1893 {
1894   g_mutex_lock (ddrawsink->pool_lock);
1895   while (ddrawsink->buffer_pool) {
1896     GstDDrawSurface *surface = ddrawsink->buffer_pool->data;
1897
1898     ddrawsink->buffer_pool = g_slist_delete_link (ddrawsink->buffer_pool,
1899         ddrawsink->buffer_pool);
1900     gst_directdraw_sink_surface_destroy (ddrawsink, surface);
1901     gst_buffer_unref (surface);
1902   }
1903   g_mutex_unlock (ddrawsink->pool_lock);
1904 }
1905
1906 static void
1907 gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink)
1908 {
1909   /* Post quit message and wait for our event window thread */
1910   if (ddrawsink->video_window && ddrawsink->our_video_window)
1911     PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
1912
1913   if (ddrawsink->window_thread) {
1914     g_thread_join (ddrawsink->window_thread);
1915     ddrawsink->window_thread = NULL;
1916   }
1917
1918   if (ddrawsink->buffer_pool) {
1919     gst_directdraw_sink_bufferpool_clear (ddrawsink);
1920     ddrawsink->buffer_pool = NULL;
1921   }
1922
1923   if (ddrawsink->offscreen_surface) {
1924     IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
1925     ddrawsink->offscreen_surface = NULL;
1926   }
1927
1928   if (ddrawsink->clipper) {
1929     IDirectDrawClipper_Release (ddrawsink->clipper);
1930     ddrawsink->clipper = NULL;
1931   }
1932
1933   if (ddrawsink->primary_surface) {
1934     IDirectDrawSurface7_Release (ddrawsink->primary_surface);
1935     ddrawsink->primary_surface = NULL;
1936   }
1937
1938   if (ddrawsink->ddraw_object) {
1939     IDirectDraw7_Release (ddrawsink->ddraw_object);
1940     ddrawsink->ddraw_object = NULL;
1941   }
1942
1943   if (ddrawsink->last_buffer) {
1944     gst_buffer_unref (ddrawsink->last_buffer);
1945     ddrawsink->last_buffer = NULL;
1946   }
1947
1948   ddrawsink->setup = FALSE;
1949 }