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