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