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