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