d3dvideosink: destroy device just before final d3d release call
[platform/upstream/gstreamer.git] / sys / d3dvideosink / d3dhelpers.c
1 /* GStreamer
2  * Copyright (C) 2012 Roland Krikava <info@bluedigits.com>
3  * Copyright (C) 2010-2011 David Hoyt <dhoyt@hoytsoft.org>
4  * Copyright (C) 2010 Andoni Morales <ylatuya@gmail.com>
5  * Copyright (C) 2012 Sebastian Dröge <sebastian.droege@collabora.co.uk>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "d3dvideosink.h"
27 #include "d3dhelpers.h"
28 #include "gstd3d9overlay.h"
29
30 #include <stdio.h>
31
32 typedef enum
33 {
34   WINDOW_VISIBILITY_FULL = 1,
35   WINDOW_VISIBILITY_PARTIAL = 2,
36   WINDOW_VISIBILITY_HIDDEN = 3,
37   WINDOW_VISIBILITY_ERROR = 4
38 } WindowHandleVisibility;
39
40 /* FWD DECLS */
41
42 static gboolean d3d_hidden_window_thread (GstD3DVideoSinkClass * klass);
43 static gboolean d3d_window_wndproc_set (GstD3DVideoSink * sink);
44 static void d3d_window_wndproc_unset (GstD3DVideoSink * sink);
45 static gboolean d3d_init_swap_chain (GstD3DVideoSink * sink, HWND hWnd);
46 static gboolean d3d_release_swap_chain (GstD3DVideoSink * sink);
47 static gboolean d3d_resize_swap_chain (GstD3DVideoSink * sink);
48 static gboolean d3d_present_swap_chain (GstD3DVideoSink * sink);
49 static gboolean d3d_copy_buffer (GstD3DVideoSink * sink,
50     GstBuffer * from, GstBuffer * to);
51 static gboolean d3d_stretch_and_copy (GstD3DVideoSink * sink,
52     LPDIRECT3DSURFACE9 back_buffer);
53 static HWND d3d_create_internal_window (GstD3DVideoSink * sink);
54
55 static void d3d_class_notify_device_lost (GstD3DVideoSink * sink);
56
57 static void d3d_class_display_device_destroy (GstD3DVideoSinkClass * klass);
58 static gboolean d3d_class_display_device_create (GstD3DVideoSinkClass * klass,
59     UINT adapter);
60
61 static LRESULT APIENTRY d3d_wnd_proc_internal (HWND hWnd, UINT message,
62     WPARAM wParam, LPARAM lParam);
63 static LRESULT APIENTRY d3d_wnd_proc (HWND hWnd, UINT message, WPARAM wParam,
64     LPARAM lParam);
65
66
67 GST_DEBUG_CATEGORY_EXTERN (gst_d3dvideosink_debug);
68 #define GST_CAT_DEFAULT gst_d3dvideosink_debug
69
70 static gint WM_D3DVIDEO_NOTIFY_DEVICE_LOST = 0;
71 #define IDT_DEVICE_RESET_TIMER 0
72
73 #define WM_QUIT_THREAD  WM_USER+0
74
75 /* Helpers */
76
77 #define ERROR_CHECK_HR(hr)                          \
78   if(hr != S_OK) {                                  \
79     const gchar * str_err=NULL, *t1=NULL;           \
80     gchar tmp[128]="";                              \
81     switch(hr)
82 #define CASE_HR_ERR(hr_err)                         \
83       case hr_err: str_err = #hr_err; break;
84 #define CASE_HR_DBG_ERR_END(sink, gst_err_msg, level) \
85       default:                                      \
86         t1=gst_err_msg;                             \
87       sprintf(tmp, "HR-SEV:%u HR-FAC:%u HR-CODE:%u", (guint)HRESULT_SEVERITY(hr), (guint)HRESULT_FACILITY(hr), (guint)HRESULT_CODE(hr)); \
88         str_err = tmp;                              \
89     } /* end switch */                              \
90     GST_CAT_LEVEL_LOG(GST_CAT_DEFAULT, level, sink, "%s HRESULT: %s", t1?t1:"", str_err);
91 #define CASE_HR_ERR_END(sink, gst_err_msg)          \
92   CASE_HR_DBG_ERR_END(sink, gst_err_msg, GST_LEVEL_ERROR)
93 #define CASE_HR_DBG_END(sink, gst_err_msg)          \
94   CASE_HR_DBG_ERR_END(sink, gst_err_msg, GST_LEVEL_DEBUG)
95
96 #define CHECK_REF_COUNT(klass, sink, goto_label)                        \
97   if(!klass->d3d.refs) {                                                \
98     GST_ERROR_OBJECT(sink, "Direct3D object ref count = 0");            \
99     goto goto_label;                                                    \
100   }
101 #define CHECK_D3D_DEVICE(klass, sink, goto_label)                       \
102   if(!klass->d3d.d3d || !klass->d3d.device.d3d_device) {                \
103     GST_ERROR_OBJECT(sink, "Direct3D device or object does not exist"); \
104     goto goto_label;                                                    \
105   }
106 #define CHECK_D3D_SWAPCHAIN(sink, goto_label)                       \
107   if(!sink->d3d.swapchain) {                                        \
108     GST_ERROR_OBJECT(sink, "Direct3D swap chain does not exist");   \
109     goto goto_label;                                                \
110   }
111 #define CHECK_D3D_SURFACE(sink, goto_label)                 \
112   if(!sink->d3d.surface) {                                  \
113     GST_ERROR_OBJECT(sink, "NULL D3D offscreen surface");   \
114     goto goto_label;                                        \
115   }
116 #define CHECK_WINDOW_HANDLE(sink, goto_label, is_error)             \
117   if(!sink->d3d.window_handle) {                                    \
118     GST_CAT_LEVEL_LOG(GST_CAT_DEFAULT,                              \
119                       (is_error?GST_LEVEL_ERROR:GST_LEVEL_DEBUG),   \
120                       sink, "No window handle is set");             \
121     goto goto_label;                                                \
122   }
123
124 #ifndef D3DFMT_YV12
125 #define D3DFMT_YV12 MAKEFOURCC ('Y', 'V', '1', '2')
126 #endif
127 #ifndef D3DFMT_NV12
128 #define D3DFMT_NV12 MAKEFOURCC ('N', 'V', '1', '2')
129 #endif
130
131 /* FORMATS */
132
133 #define CASE(x) case x: return #x;
134 static const gchar *
135 d3d_format_to_string (D3DFORMAT format)
136 {
137   /* Self defined up above */
138   if (format == D3DFMT_YV12)
139     return "D3DFMT_YV12";
140   else if (format == D3DFMT_NV12)
141     return "D3DFMT_NV12";
142
143   switch (format) {
144       /* From D3D enum */
145       CASE (D3DFMT_UNKNOWN);
146       CASE (D3DFMT_X8R8G8B8);
147       CASE (D3DFMT_YUY2);
148       CASE (D3DFMT_A8R8G8B8);
149       CASE (D3DFMT_UYVY);
150       CASE (D3DFMT_R8G8B8);
151       CASE (D3DFMT_R5G6B5);
152       CASE (D3DFMT_X1R5G5B5);
153       CASE (D3DFMT_A1R5G5B5);
154       CASE (D3DFMT_A4R4G4B4);
155       CASE (D3DFMT_R3G3B2);
156       CASE (D3DFMT_A8);
157       CASE (D3DFMT_A8R3G3B2);
158       CASE (D3DFMT_X4R4G4B4);
159       CASE (D3DFMT_A2B10G10R10);
160       CASE (D3DFMT_A8B8G8R8);
161       CASE (D3DFMT_X8B8G8R8);
162       CASE (D3DFMT_G16R16);
163       CASE (D3DFMT_A2R10G10B10);
164       CASE (D3DFMT_A16B16G16R16);
165       CASE (D3DFMT_A8P8);
166       CASE (D3DFMT_P8);
167       CASE (D3DFMT_L8);
168       CASE (D3DFMT_A8L8);
169       CASE (D3DFMT_A4L4);
170       CASE (D3DFMT_V8U8);
171       CASE (D3DFMT_L6V5U5);
172       CASE (D3DFMT_X8L8V8U8);
173       CASE (D3DFMT_Q8W8V8U8);
174       CASE (D3DFMT_V16U16);
175       CASE (D3DFMT_A2W10V10U10);
176       CASE (D3DFMT_DXT1);
177       CASE (D3DFMT_DXT2);
178       CASE (D3DFMT_DXT3);
179       CASE (D3DFMT_DXT4);
180       CASE (D3DFMT_DXT5);
181       CASE (D3DFMT_MULTI2_ARGB8);
182       CASE (D3DFMT_G8R8_G8B8);
183       CASE (D3DFMT_R8G8_B8G8);
184       CASE (D3DFMT_D16_LOCKABLE);
185       CASE (D3DFMT_D32);
186       CASE (D3DFMT_D15S1);
187       CASE (D3DFMT_D24S8);
188       CASE (D3DFMT_D24X8);
189       CASE (D3DFMT_D24X4S4);
190       CASE (D3DFMT_D16);
191       CASE (D3DFMT_L16);
192       CASE (D3DFMT_D32F_LOCKABLE);
193       CASE (D3DFMT_D24FS8);
194       CASE (D3DFMT_VERTEXDATA);
195       CASE (D3DFMT_INDEX16);
196       CASE (D3DFMT_INDEX32);
197       CASE (D3DFMT_Q16W16V16U16);
198       CASE (D3DFMT_R16F);
199       CASE (D3DFMT_G16R16F);
200       CASE (D3DFMT_A16B16G16R16F);
201       CASE (D3DFMT_R32F);
202       CASE (D3DFMT_G32R32F);
203       CASE (D3DFMT_A32B32G32R32F);
204       CASE (D3DFMT_CxV8U8);
205       CASE (D3DFMT_FORCE_DWORD);
206     default:
207       break;
208   }
209
210   return "UNKNOWN";
211 }
212
213 #undef CASE
214
215 static const struct
216 {
217   GstVideoFormat gst_format;
218   D3DFORMAT d3d_format;
219 } gst_d3d_format_map[] = {
220   {
221   GST_VIDEO_FORMAT_BGRx, D3DFMT_X8R8G8B8}, {
222   GST_VIDEO_FORMAT_RGBx, D3DFMT_X8B8G8R8}, {
223   GST_VIDEO_FORMAT_BGRA, D3DFMT_A8R8G8B8}, {
224   GST_VIDEO_FORMAT_RGBA, D3DFMT_A8B8G8R8}, {
225   GST_VIDEO_FORMAT_BGR, D3DFMT_R8G8B8}, {
226   GST_VIDEO_FORMAT_RGB16, D3DFMT_R5G6B5}, {
227   GST_VIDEO_FORMAT_RGB15, D3DFMT_X1R5G5B5}, {
228   GST_VIDEO_FORMAT_I420, D3DFMT_YV12}, {
229   GST_VIDEO_FORMAT_YV12, D3DFMT_YV12}, {
230   GST_VIDEO_FORMAT_NV12, D3DFMT_NV12}, {
231   GST_VIDEO_FORMAT_YUY2, D3DFMT_YUY2}, {
232   GST_VIDEO_FORMAT_UYVY, D3DFMT_UYVY}
233 };
234
235 static D3DFORMAT
236 gst_video_format_to_d3d_format (GstVideoFormat format)
237 {
238   gint i;
239
240   for (i = 0; i < G_N_ELEMENTS (gst_d3d_format_map); i++)
241     if (gst_d3d_format_map[i].gst_format == format)
242       return gst_d3d_format_map[i].d3d_format;
243   return D3DFMT_UNKNOWN;
244 }
245
246 static gboolean
247 gst_video_d3d_format_check (GstD3DVideoSink * sink, D3DFORMAT fmt)
248 {
249   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
250   HRESULT hr;
251   gboolean ret = FALSE;
252
253   hr = IDirect3D9_CheckDeviceFormat (klass->d3d.d3d,
254       klass->d3d.device.adapter,
255       D3DDEVTYPE_HAL, klass->d3d.device.format, 0, D3DRTYPE_SURFACE, fmt);
256   if (hr == D3D_OK) {
257     /* test whether device can perform color-conversion
258      * from that format to target format
259      */
260     hr = IDirect3D9_CheckDeviceFormatConversion (klass->d3d.d3d,
261         klass->d3d.device.adapter,
262         D3DDEVTYPE_HAL, fmt, klass->d3d.device.format);
263     if (hr == D3D_OK)
264       ret = TRUE;
265   }
266   GST_DEBUG_OBJECT (sink, "Checking: %s - %s", d3d_format_to_string (fmt),
267       ret ? "TRUE" : "FALSE");
268
269   return ret;
270 }
271
272 static gboolean
273 gst_video_query_d3d_format (GstD3DVideoSink * sink, D3DFORMAT d3dformat)
274 {
275   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
276
277   /* If it's the display adapter format we don't need to probe */
278   if (d3dformat == klass->d3d.device.format)
279     return TRUE;
280
281   if (gst_video_d3d_format_check (sink, d3dformat))
282     return TRUE;
283
284   return FALSE;
285 }
286
287 typedef struct
288 {
289   GstVideoFormat fmt;
290   D3DFORMAT d3d_fmt;
291   gboolean display;
292 } D3DFormatComp;
293
294 static void
295 d3d_format_comp_free (D3DFormatComp * comp)
296 {
297   g_slice_free (D3DFormatComp, comp);
298 }
299
300 static gint
301 d3d_format_comp_rate (const D3DFormatComp * comp)
302 {
303   gint points = 0;
304   const GstVideoFormatInfo *info;
305
306   info = gst_video_format_get_info (comp->fmt);
307
308   if (comp->display)
309     points += 10;
310   if (GST_VIDEO_FORMAT_INFO_IS_YUV (info))
311     points += 5;
312   else if (GST_VIDEO_FORMAT_INFO_IS_RGB (info)) {
313     guint i, bit_depth = 0;
314     for (i = 0; i < GST_VIDEO_FORMAT_INFO_N_COMPONENTS (info); i++)
315       bit_depth += GST_VIDEO_FORMAT_INFO_DEPTH (info, i);
316     if (bit_depth >= 24)
317       points += 1;
318   }
319
320   return points;
321 }
322
323 static gint
324 d3d_format_comp_compare (gconstpointer a, gconstpointer b)
325 {
326   gint ptsa = 0, ptsb = 0;
327
328   ptsa = d3d_format_comp_rate ((const D3DFormatComp *) a);
329   ptsb = d3d_format_comp_rate ((const D3DFormatComp *) b);
330
331   if (ptsa < ptsb)
332     return -1;
333   else if (ptsa == ptsb)
334     return 0;
335   else
336     return 1;
337 }
338
339 #define GST_D3D_SURFACE_MEMORY_NAME "D3DSurface"
340
341 typedef struct
342 {
343   GstMemory mem;
344
345   GstD3DVideoSink *sink;
346
347   GMutex lock;
348   gint map_count;
349
350   LPDIRECT3DSURFACE9 surface;
351   D3DLOCKED_RECT lr;
352   gint x, y, width, height;
353 } GstD3DSurfaceMemory;
354
355 static GstMemory *
356 gst_d3d_surface_memory_allocator_alloc (GstAllocator * allocator, gsize size,
357     GstAllocationParams * params)
358 {
359   g_assert_not_reached ();
360   return NULL;
361 }
362
363 static void
364 gst_d3d_surface_memory_allocator_free (GstAllocator * allocator,
365     GstMemory * mem)
366 {
367   GstD3DSurfaceMemory *dmem = (GstD3DSurfaceMemory *) mem;
368
369   /* If this is a sub-memory, do nothing */
370   if (mem->parent)
371     return;
372
373   if (dmem->lr.pBits)
374     g_warning ("d3dvideosink: Freeing memory that is still mapped");
375
376   IDirect3DSurface9_Release (dmem->surface);
377   gst_object_unref (dmem->sink);
378   g_mutex_clear (&dmem->lock);
379   g_slice_free (GstD3DSurfaceMemory, dmem);
380 }
381
382 static gpointer
383 gst_d3d_surface_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
384 {
385   GstD3DSurfaceMemory *parent;
386   gpointer ret = NULL;
387
388   /* find the real parent */
389   if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
390     parent = (GstD3DSurfaceMemory *) mem;
391
392   g_mutex_lock (&parent->lock);
393   if (!parent->map_count
394       && IDirect3DSurface9_LockRect (parent->surface, &parent->lr, NULL,
395           0) != D3D_OK) {
396     ret = NULL;
397     goto done;
398   }
399
400   ret = parent->lr.pBits;
401   parent->map_count++;
402
403 done:
404   g_mutex_unlock (&parent->lock);
405
406   return ret;
407 }
408
409 static void
410 gst_d3d_surface_memory_unmap (GstMemory * mem)
411 {
412   GstD3DSurfaceMemory *parent;
413
414   /* find the real parent */
415   if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
416     parent = (GstD3DSurfaceMemory *) mem;
417
418   g_mutex_lock (&parent->lock);
419   parent->map_count--;
420   if (parent->map_count == 0) {
421     IDirect3DSurface9_UnlockRect (parent->surface);
422     memset (&parent->lr, 0, sizeof (parent->lr));
423   }
424
425   g_mutex_unlock (&parent->lock);
426 }
427
428 static GstMemory *
429 gst_d3d_surface_memory_share (GstMemory * mem, gssize offset, gssize size)
430 {
431   GstD3DSurfaceMemory *sub;
432   GstD3DSurfaceMemory *parent;
433
434   /* find the real parent */
435   if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
436     parent = (GstD3DSurfaceMemory *) mem;
437
438   if (size == -1)
439     size = mem->size - offset;
440
441   sub = g_slice_new0 (GstD3DSurfaceMemory);
442   /* the shared memory is always readonly */
443   gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) |
444       GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->allocator,
445       GST_MEMORY_CAST (parent), mem->maxsize, mem->align, mem->offset + offset,
446       size);
447
448   return GST_MEMORY_CAST (sub);
449 }
450
451 typedef struct
452 {
453   GstAllocator parent;
454 } GstD3DSurfaceMemoryAllocator;
455
456 typedef struct
457 {
458   GstAllocatorClass parent_class;
459 } GstD3DSurfaceMemoryAllocatorClass;
460
461 GType gst_d3d_surface_memory_allocator_get_type (void);
462 G_DEFINE_TYPE (GstD3DSurfaceMemoryAllocator, gst_d3d_surface_memory_allocator,
463     GST_TYPE_ALLOCATOR);
464
465 #define GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR   (gst_d3d_surface_memory_allocator_get_type())
466 #define GST_IS_D3D_SURFACE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR))
467
468 static void
469 gst_d3d_surface_memory_allocator_class_init (GstD3DSurfaceMemoryAllocatorClass *
470     klass)
471 {
472   GstAllocatorClass *allocator_class;
473
474   allocator_class = (GstAllocatorClass *) klass;
475
476   allocator_class->alloc = gst_d3d_surface_memory_allocator_alloc;
477   allocator_class->free = gst_d3d_surface_memory_allocator_free;
478 }
479
480 static void
481 gst_d3d_surface_memory_allocator_init (GstD3DSurfaceMemoryAllocator * allocator)
482 {
483   GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
484
485   alloc->mem_type = GST_D3D_SURFACE_MEMORY_NAME;
486   alloc->mem_map = gst_d3d_surface_memory_map;
487   alloc->mem_unmap = gst_d3d_surface_memory_unmap;
488   alloc->mem_share = gst_d3d_surface_memory_share;
489   /* fallback copy */
490
491   GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
492 }
493
494 G_DEFINE_TYPE (GstD3DSurfaceBufferPool, gst_d3dsurface_buffer_pool,
495     GST_TYPE_VIDEO_BUFFER_POOL);
496
497 GstBufferPool *
498 gst_d3dsurface_buffer_pool_new (GstD3DVideoSink * sink)
499 {
500   GstD3DSurfaceBufferPool *pool;
501
502   pool = g_object_new (GST_TYPE_D3DSURFACE_BUFFER_POOL, NULL);
503   gst_object_ref_sink (pool);
504   pool->sink = gst_object_ref (sink);
505
506   GST_LOG_OBJECT (pool, "new buffer pool %p", pool);
507
508   return GST_BUFFER_POOL_CAST (pool);
509 }
510
511 static void
512 gst_d3dsurface_buffer_pool_finalize (GObject * object)
513 {
514   GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (object);
515
516   GST_LOG_OBJECT (pool, "finalize buffer pool %p", pool);
517
518   gst_object_unref (pool->sink);
519   if (pool->allocator)
520     gst_object_unref (pool->allocator);
521
522   G_OBJECT_CLASS (gst_d3dsurface_buffer_pool_parent_class)->finalize (object);
523 }
524
525 static const gchar **
526 gst_d3dsurface_buffer_pool_get_options (GstBufferPool * pool)
527 {
528   static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
529
530   return options;
531 }
532
533 /* Calculate actual required buffer size from D3DLOCKED_RECT structure.
534  * Note that D3D could require larger Pitch value than minimum required one in theory.
535  * See also
536  * https://docs.microsoft.com/en-us/windows/desktop/direct3d9/width-vs--pitch */
537 static gboolean
538 d3d_calculate_buffer_size (GstVideoInfo * info, D3DLOCKED_RECT * lr,
539     gsize * offset, gint * stride, gsize * size)
540 {
541   switch (GST_VIDEO_INFO_FORMAT (info)) {
542     case GST_VIDEO_FORMAT_BGR:
543     case GST_VIDEO_FORMAT_BGRx:
544     case GST_VIDEO_FORMAT_RGBx:
545     case GST_VIDEO_FORMAT_BGRA:
546     case GST_VIDEO_FORMAT_RGBA:
547     case GST_VIDEO_FORMAT_RGB16:
548     case GST_VIDEO_FORMAT_RGB15:
549     case GST_VIDEO_FORMAT_YUY2:
550     case GST_VIDEO_FORMAT_UYVY:
551       offset[0] = 0;
552       stride[0] = lr->Pitch;
553       *size = lr->Pitch * GST_VIDEO_INFO_HEIGHT (info);
554       break;
555     case GST_VIDEO_FORMAT_I420:
556     case GST_VIDEO_FORMAT_YV12:
557       offset[0] = 0;
558       stride[0] = lr->Pitch;
559       if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_YV12) {
560         offset[1] =
561             offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
562         stride[1] = lr->Pitch / 2;
563         offset[2] =
564             offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
565         stride[2] = lr->Pitch / 2;
566         *size = offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (info, 2);
567       } else {
568         offset[2] =
569             offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
570         stride[2] = lr->Pitch / 2;
571         offset[1] =
572             offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (info, 2);
573         stride[1] = lr->Pitch / 2;
574         *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
575       }
576       break;
577     case GST_VIDEO_FORMAT_NV12:
578       offset[0] = 0;
579       stride[0] = lr->Pitch;
580       offset[1] = offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
581       stride[1] = lr->Pitch;
582       *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
583       break;
584     default:
585       return FALSE;
586   }
587
588   GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT
589       " (%s %dx%d, Pitch %d)", *size,
590       gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)),
591       GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), lr->Pitch);
592
593   return TRUE;
594 }
595
596 static gboolean
597 gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool,
598     GstStructure * config)
599 {
600   GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool);
601   GstD3DVideoSink *sink = pool->sink;
602   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
603   GstCaps *caps;
604   GstVideoInfo info;
605   LPDIRECT3DSURFACE9 surface;
606   D3DFORMAT d3dformat;
607   gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
608   gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
609   D3DLOCKED_RECT lr;
610   HRESULT hr;
611   gsize size;
612
613   if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
614       || !caps) {
615     GST_ERROR_OBJECT (pool, "Buffer pool configuration without caps");
616     return FALSE;
617   }
618
619   /* now parse the caps from the config */
620   if (!gst_video_info_from_caps (&info, caps)) {
621     GST_ERROR_OBJECT (pool, "Failed to parse caps %" GST_PTR_FORMAT, caps);
622     return FALSE;
623   }
624
625   d3dformat = gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&info));
626   if (d3dformat == D3DFMT_UNKNOWN) {
627     GST_ERROR_OBJECT (pool, "Unsupported video format in caps %" GST_PTR_FORMAT,
628         caps);
629     return FALSE;
630   }
631
632   GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
633       caps);
634
635   /* Create a surface to get exact buffer size */
636   LOCK_CLASS (sink, klass);
637   CHECK_REF_COUNT (klass, sink, error);
638   CHECK_D3D_DEVICE (klass, sink, error);
639   hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d.
640       device.d3d_device, GST_VIDEO_INFO_WIDTH (&info),
641       GST_VIDEO_INFO_HEIGHT (&info), d3dformat, D3DPOOL_DEFAULT, &surface,
642       NULL);
643   UNLOCK_CLASS (sink, klass);
644   if (hr != D3D_OK) {
645     GST_ERROR_OBJECT (sink, "Failed to create D3D surface");
646     return FALSE;
647   }
648
649   IDirect3DSurface9_LockRect (surface, &lr, NULL, 0);
650   if (!lr.pBits) {
651     GST_ERROR_OBJECT (sink, "Failed to lock D3D surface");
652     IDirect3DSurface9_Release (surface);
653     return FALSE;
654   }
655
656   if (!d3d_calculate_buffer_size (&info, &lr, offset, stride, &size)) {
657     GST_ERROR_OBJECT (sink, "Failed to get buffer size");
658     IDirect3DSurface9_UnlockRect (surface);
659     IDirect3DSurface9_Release (surface);
660     return FALSE;
661   }
662
663   IDirect3DSurface9_UnlockRect (surface);
664   IDirect3DSurface9_Release (surface);
665
666   pool->info = info;
667
668   pool->add_metavideo =
669       gst_buffer_pool_config_has_option (config,
670       GST_BUFFER_POOL_OPTION_VIDEO_META);
671
672   if (pool->add_metavideo) {
673     pool->allocator =
674         g_object_new (GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR, NULL);
675     gst_object_ref_sink (pool->allocator);
676   }
677
678   gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
679
680   return GST_BUFFER_POOL_CLASS
681       (gst_d3dsurface_buffer_pool_parent_class)->set_config (bpool, config);
682
683 error:
684   UNLOCK_CLASS (sink, klass);
685   return FALSE;
686 }
687
688 static GstFlowReturn
689 gst_d3dsurface_buffer_pool_alloc_buffer (GstBufferPool * bpool,
690     GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
691 {
692   GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool);
693   GstD3DVideoSink *sink = pool->sink;
694   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
695   GstD3DSurfaceMemory *mem;
696   LPDIRECT3DSURFACE9 surface;
697   D3DFORMAT d3dformat;
698   gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
699   gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
700   D3DLOCKED_RECT lr;
701   HRESULT hr;
702   gsize size = 0;
703
704   *buffer = NULL;
705   if (!pool->add_metavideo) {
706     GST_DEBUG_OBJECT (pool, "No video meta allowed, fallback alloc");
707     goto fallback;
708   }
709
710   d3dformat =
711       gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&pool->info));
712   LOCK_CLASS (sink, klass);
713   CHECK_REF_COUNT (klass, sink, error);
714   CHECK_D3D_DEVICE (klass, sink, error);
715   hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d.
716       device.d3d_device, GST_VIDEO_INFO_WIDTH (&pool->info),
717       GST_VIDEO_INFO_HEIGHT (&pool->info), d3dformat, D3DPOOL_DEFAULT, &surface,
718       NULL);
719   UNLOCK_CLASS (sink, klass);
720   if (hr != D3D_OK) {
721     GST_ERROR_OBJECT (sink, "Failed to create D3D surface");
722     goto fallback;
723   }
724
725   IDirect3DSurface9_LockRect (surface, &lr, NULL, 0);
726   if (!lr.pBits) {
727     GST_ERROR_OBJECT (sink, "Failed to lock D3D surface");
728     IDirect3DSurface9_Release (surface);
729     goto fallback;
730   }
731
732   if (!d3d_calculate_buffer_size (&pool->info, &lr, offset, stride, &size)) {
733     GST_ERROR_OBJECT (sink, "Failed to get buffer size");
734     IDirect3DSurface9_UnlockRect (surface);
735     IDirect3DSurface9_Release (surface);
736     return GST_FLOW_ERROR;
737   }
738
739   IDirect3DSurface9_UnlockRect (surface);
740
741   *buffer = gst_buffer_new ();
742
743   gst_buffer_add_video_meta_full (*buffer, GST_VIDEO_FRAME_FLAG_NONE,
744       GST_VIDEO_INFO_FORMAT (&pool->info), GST_VIDEO_INFO_WIDTH (&pool->info),
745       GST_VIDEO_INFO_HEIGHT (&pool->info),
746       GST_VIDEO_INFO_N_PLANES (&pool->info), offset, stride);
747
748   mem = g_slice_new0 (GstD3DSurfaceMemory);
749   gst_memory_init (GST_MEMORY_CAST (mem), 0, pool->allocator, NULL, size, 0, 0,
750       size);
751
752   mem->surface = surface;
753   mem->sink = gst_object_ref (sink);
754   mem->x = mem->y = 0;
755   mem->width = GST_VIDEO_INFO_WIDTH (&pool->info);
756   mem->height = GST_VIDEO_INFO_HEIGHT (&pool->info);
757   g_mutex_init (&mem->lock);
758
759   gst_buffer_append_memory (*buffer, GST_MEMORY_CAST (mem));
760
761   return GST_FLOW_OK;
762
763 fallback:
764   {
765     return
766         GST_BUFFER_POOL_CLASS
767         (gst_d3dsurface_buffer_pool_parent_class)->alloc_buffer (bpool, buffer,
768         params);
769   }
770 error:
771   UNLOCK_CLASS (sink, klass);
772   return GST_FLOW_ERROR;
773 }
774
775 static void
776 gst_d3dsurface_buffer_pool_class_init (GstD3DSurfaceBufferPoolClass * klass)
777 {
778   GObjectClass *gobject_class = (GObjectClass *) klass;
779   GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
780
781   gobject_class->finalize = gst_d3dsurface_buffer_pool_finalize;
782
783   gstbufferpool_class->get_options = gst_d3dsurface_buffer_pool_get_options;
784   gstbufferpool_class->set_config = gst_d3dsurface_buffer_pool_set_config;
785   gstbufferpool_class->alloc_buffer = gst_d3dsurface_buffer_pool_alloc_buffer;
786 }
787
788 static void
789 gst_d3dsurface_buffer_pool_init (GstD3DSurfaceBufferPool * pool)
790 {
791 }
792
793 GstCaps *
794 d3d_supported_caps (GstD3DVideoSink * sink)
795 {
796   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
797   int i;
798   GList *fmts = NULL, *l;
799   GstCaps *caps = NULL;
800   GstVideoFormat gst_format;
801   D3DFORMAT d3d_format;
802   GValue va = { 0, };
803   GValue v = { 0, };
804
805   LOCK_SINK (sink);
806
807   if (sink->supported_caps) {
808     caps = gst_caps_ref (sink->supported_caps);
809     goto unlock;
810   }
811
812   LOCK_CLASS (sink, klass);
813   if (klass->d3d.refs == 0) {
814     UNLOCK_CLASS (sink, klass);
815     goto unlock;
816   }
817   UNLOCK_CLASS (sink, klass);
818
819   for (i = 0; i < G_N_ELEMENTS (gst_d3d_format_map); i++) {
820     D3DFormatComp *comp;
821
822     gst_format = gst_d3d_format_map[i].gst_format;
823     d3d_format = gst_d3d_format_map[i].d3d_format;
824     if (!gst_video_query_d3d_format (sink, d3d_format))
825       continue;
826
827     comp = g_slice_new0 (D3DFormatComp);
828     comp->fmt = (GstVideoFormat) gst_format;
829     comp->d3d_fmt = d3d_format;
830     comp->display = (d3d_format == klass->d3d.device.format);
831     fmts = g_list_insert_sorted (fmts, comp, d3d_format_comp_compare);
832   }
833
834   GST_DEBUG_OBJECT (sink, "Supported Caps:");
835
836   g_value_init (&va, GST_TYPE_LIST);
837   g_value_init (&v, G_TYPE_STRING);
838
839   for (l = fmts; l; l = g_list_next (l)) {
840     D3DFormatComp *comp = (D3DFormatComp *) l->data;
841
842     GST_DEBUG_OBJECT (sink, "%s -> %s %s",
843         gst_video_format_to_string (comp->fmt),
844         d3d_format_to_string (comp->d3d_fmt), comp->display ? "[display]" : "");
845     g_value_set_string (&v, gst_video_format_to_string (comp->fmt));
846     gst_value_list_append_value (&va, &v);
847   }
848
849   caps =
850       gst_caps_make_writable (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
851           (sink)));
852
853   gst_caps_set_value (caps, "format", &va);
854   g_value_unset (&v);
855   g_value_unset (&va);
856   g_list_free_full (fmts, (GDestroyNotify) d3d_format_comp_free);
857
858   sink->supported_caps = gst_caps_ref (caps);
859
860 #ifndef GST_DISABLE_GST_DEBUG
861   {
862     GST_DEBUG_OBJECT (sink, "Supported caps: %" GST_PTR_FORMAT, caps);
863   }
864 #endif
865
866 unlock:
867   UNLOCK_SINK (sink);
868
869   return caps;
870 }
871
872 gboolean
873 d3d_set_render_format (GstD3DVideoSink * sink)
874 {
875   D3DFORMAT fmt;
876   gboolean ret = FALSE;
877
878   LOCK_SINK (sink);
879
880   fmt = gst_video_format_to_d3d_format (sink->format);
881   if (fmt == D3DFMT_UNKNOWN) {
882     GST_ERROR_OBJECT (sink, "Unsupported video format %s",
883         gst_video_format_to_string (sink->format));
884     goto end;
885   }
886
887   if (!gst_video_query_d3d_format (sink, fmt)) {
888     GST_ERROR_OBJECT (sink, "Failed to query a D3D render format for %s",
889         gst_video_format_to_string (sink->format));
890     goto end;
891   }
892
893   GST_DEBUG_OBJECT (sink, "Selected %s -> %s",
894       gst_video_format_to_string (sink->format), d3d_format_to_string (fmt));
895
896   sink->d3d.format = fmt;
897
898   ret = TRUE;
899
900 end:
901   UNLOCK_SINK (sink);
902
903   return ret;
904 }
905
906 gboolean
907 d3d_get_hwnd_window_size (HWND hwnd, gint * width, gint * height)
908 {
909   RECT sz;
910
911   g_return_val_if_fail (width != NULL, FALSE);
912   g_return_val_if_fail (height != NULL, FALSE);
913
914   *width = 0;
915   *height = 0;
916
917   if (!hwnd)
918     return FALSE;
919
920   GetClientRect (hwnd, &sz);
921
922   *width = MAX (1, ABS (sz.right - sz.left));
923   *height = MAX (1, ABS (sz.bottom - sz.top));
924
925   return TRUE;
926 }
927
928 static gboolean
929 d3d_get_render_rects (GstVideoRectangle * rr, RECT * dst, RECT * src)
930 {
931   if (!rr)
932     return FALSE;
933
934   /* Rect on target */
935   if (dst) {
936     dst->left = rr->x;
937     dst->top = rr->y;
938     dst->right = rr->x + rr->w;
939     dst->bottom = rr->y + rr->h;
940   }
941
942   /* Rect on source */
943   if (src) {
944     src->left = 0;
945     src->top = 0;
946     src->right = rr->w;
947     src->bottom = rr->h;
948   }
949
950   return TRUE;
951 }
952
953 static gboolean
954 d3d_get_render_coordinates (GstD3DVideoSink * sink, gint in_x, gint in_y,
955     gdouble * out_x, gdouble * out_y)
956 {
957   GstVideoRectangle r_area;
958   gdouble tmp;
959   gboolean ret = FALSE;
960
961   g_return_val_if_fail (out_x != NULL, FALSE);
962   g_return_val_if_fail (out_y != NULL, FALSE);
963
964   LOCK_SINK (sink);
965   CHECK_WINDOW_HANDLE (sink, end, FALSE);
966
967   /* Get renderable area of the window */
968   if (sink->d3d.render_rect) {
969     memcpy (&r_area, sink->d3d.render_rect, sizeof (r_area));
970   } else {
971     memset (&r_area, 0, sizeof (r_area));
972     d3d_get_hwnd_window_size (sink->d3d.window_handle, &r_area.w, &r_area.h);
973   }
974
975   /* If window coords outside render area.. return */
976   if (in_x < r_area.x || in_x > r_area.x + r_area.w ||
977       in_y < r_area.y || in_y > r_area.y + r_area.h)
978     goto end;
979
980   /* Convert window coordinates to source frame pixel coordinates */
981   if (sink->force_aspect_ratio) {
982     GstVideoRectangle tmp = { 0, 0, 0, 0 };
983     GstVideoRectangle dst = { 0, 0, 0, 0 };
984
985     tmp.w = GST_VIDEO_SINK_WIDTH (sink);
986     tmp.h = GST_VIDEO_SINK_HEIGHT (sink);
987     gst_video_sink_center_rect (tmp, r_area, &dst, TRUE);
988
989     r_area.x = r_area.x + dst.x;
990     r_area.y = r_area.y + dst.y;
991     r_area.w = dst.w;
992     r_area.h = dst.h;
993
994     /* If window coords outside render area.. return */
995     if (in_x < r_area.x || in_x > (r_area.x + r_area.w) ||
996         in_y < r_area.y || in_y > (r_area.y + r_area.h))
997       goto end;
998   }
999
1000   tmp = in_x - r_area.x;
1001   if (r_area.w == GST_VIDEO_SINK_WIDTH (sink))
1002     *out_x = tmp;
1003   else if (r_area.w > GST_VIDEO_SINK_WIDTH (sink))
1004     *out_x =
1005         ((gdouble) tmp / ((gdouble) r_area.w /
1006             (gdouble) GST_VIDEO_SINK_WIDTH (sink)));
1007   else
1008     *out_x =
1009         ((gdouble) GST_VIDEO_SINK_WIDTH (sink) / (gdouble) r_area.w) *
1010         (gdouble) tmp;
1011
1012   tmp = in_y - r_area.y;
1013   if (r_area.h == GST_VIDEO_SINK_HEIGHT (sink))
1014     *out_y = tmp;
1015   else if (r_area.h > GST_VIDEO_SINK_HEIGHT (sink))
1016     *out_y =
1017         ((gdouble) tmp / ((gdouble) r_area.h /
1018             (gdouble) GST_VIDEO_SINK_HEIGHT (sink)));
1019   else
1020     *out_y =
1021         ((gdouble) GST_VIDEO_SINK_HEIGHT (sink) / (gdouble) r_area.h) *
1022         (gdouble) tmp;
1023
1024   ret = TRUE;
1025 end:
1026   UNLOCK_SINK (sink);
1027   return ret;
1028 }
1029
1030 /* Windows for rendering (User Set or Internal) */
1031
1032 static void
1033 d3d_window_wndproc_unset (GstD3DVideoSink * sink)
1034 {
1035   WNDPROC cur_wnd_proc = NULL;
1036
1037   LOCK_SINK (sink);
1038
1039   GST_DEBUG_OBJECT (sink, " ");
1040
1041   if (sink->d3d.window_handle == NULL) {
1042     GST_WARNING_OBJECT (sink, "D3D window_handle is NULL");
1043     goto end;
1044   }
1045
1046   cur_wnd_proc =
1047       (WNDPROC) GetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC);
1048
1049   if (cur_wnd_proc != d3d_wnd_proc) {
1050     GST_WARNING_OBJECT (sink, "D3D window proc is not set on current window");
1051     goto end;
1052   }
1053
1054   if (sink->d3d.orig_wnd_proc == NULL) {
1055     GST_WARNING_OBJECT (sink, "D3D orig window proc is NULL, can not restore");
1056     goto end;
1057   }
1058
1059   /* Restore original WndProc for window_handle */
1060   if (!SetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC,
1061           (LONG_PTR) sink->d3d.orig_wnd_proc)) {
1062     GST_WARNING_OBJECT (sink, "D3D failed to set original WndProc");
1063     goto end;
1064   }
1065
1066 end:
1067   sink->d3d.orig_wnd_proc = NULL;
1068   sink->d3d.window_handle = NULL;
1069
1070   UNLOCK_SINK (sink);
1071 }
1072
1073 static gboolean
1074 d3d_window_wndproc_set (GstD3DVideoSink * sink)
1075 {
1076   WNDPROC cur_wnd_proc;
1077   gboolean ret = FALSE;
1078
1079   LOCK_SINK (sink);
1080
1081   cur_wnd_proc =
1082       (WNDPROC) GetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC);
1083
1084   if (cur_wnd_proc != NULL && cur_wnd_proc == d3d_wnd_proc) {
1085     GST_DEBUG_OBJECT (sink,
1086         "D3D window proc func is already set on the current window");
1087     ret = TRUE;
1088     goto end;
1089   }
1090
1091   /* Store the original window proc function */
1092   sink->d3d.orig_wnd_proc =
1093       (WNDPROC) SetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC,
1094       (LONG_PTR) d3d_wnd_proc);
1095
1096   /* Note: If the window belongs to another process this will fail */
1097   if (sink->d3d.orig_wnd_proc == NULL) {
1098     GST_ERROR_OBJECT (sink,
1099         "Failed to set WndProc function on window. Error: %d",
1100         (gint) GetLastError ());
1101     goto end;
1102   }
1103
1104   /* Make sink accessible to d3d_wnd_proc */
1105   SetProp (sink->d3d.window_handle, TEXT ("GstD3DVideoSink"), sink);
1106
1107   ret = TRUE;
1108
1109 end:
1110   UNLOCK_SINK (sink);
1111   return ret;
1112 }
1113
1114 static void
1115 d3d_prepare_render_window (GstD3DVideoSink * sink)
1116 {
1117   LOCK_SINK (sink);
1118
1119   if (sink->d3d.window_handle == NULL) {
1120     GST_DEBUG_OBJECT (sink, "No window handle has been set.");
1121     goto end;
1122   }
1123
1124   if (sink->d3d.device_lost) {
1125     GST_DEBUG_OBJECT (sink, "Device is lost, waiting for reset.");
1126     goto end;
1127   }
1128
1129   if (d3d_init_swap_chain (sink, sink->d3d.window_handle)) {
1130     d3d_window_wndproc_set (sink);
1131     sink->d3d.renderable = TRUE;
1132     GST_DEBUG_OBJECT (sink, "Prepared window for render [HWND:%p]",
1133         sink->d3d.window_handle);
1134   } else {
1135     GST_ERROR_OBJECT (sink, "Failed preparing window for render [HWND:%p]",
1136         sink->d3d.window_handle);
1137   }
1138
1139 end:
1140   UNLOCK_SINK (sink);
1141
1142 }
1143
1144 void
1145 d3d_set_window_handle (GstD3DVideoSink * sink, guintptr window_id,
1146     gboolean is_internal)
1147 {
1148   LOCK_SINK (sink);
1149
1150   if (sink->d3d.window_handle == (HWND) window_id) {
1151     GST_WARNING_OBJECT (sink, "Window HWND already set to: %" G_GUINTPTR_FORMAT,
1152         window_id);
1153     goto end;
1154   }
1155
1156   /* Unset current window  */
1157   if (sink->d3d.window_handle != NULL) {
1158     PostMessage (sink->d3d.window_handle, WM_QUIT_THREAD, 0, 0);
1159     GST_DEBUG_OBJECT (sink, "Unsetting window [HWND:%p]",
1160         sink->d3d.window_handle);
1161     d3d_window_wndproc_unset (sink);
1162     d3d_release_swap_chain (sink);
1163     sink->d3d.window_handle = NULL;
1164     sink->d3d.window_is_internal = FALSE;
1165     sink->d3d.renderable = FALSE;
1166   }
1167
1168   /* Set new one */
1169   if (window_id) {
1170     sink->d3d.window_handle = (HWND) window_id;
1171     sink->d3d.window_is_internal = is_internal;
1172     if (!is_internal)
1173       sink->d3d.external_window_handle = sink->d3d.window_handle;
1174     /* If caps have been set.. prepare window */
1175     if (sink->format != 0)
1176       d3d_prepare_render_window (sink);
1177   }
1178
1179 end:
1180   UNLOCK_SINK (sink);
1181 }
1182
1183 void
1184 d3d_set_render_rectangle (GstD3DVideoSink * sink)
1185 {
1186   LOCK_SINK (sink);
1187   /* Setting the pointer lets us know render rect is set */
1188   sink->d3d.render_rect = &sink->render_rect;
1189   d3d_resize_swap_chain (sink);
1190   d3d_present_swap_chain (sink);
1191   UNLOCK_SINK (sink);
1192 }
1193
1194 void
1195 d3d_expose_window (GstD3DVideoSink * sink)
1196 {
1197   GST_DEBUG_OBJECT (sink, "EXPOSE");
1198   d3d_present_swap_chain (sink);
1199 }
1200
1201 gboolean
1202 d3d_prepare_window (GstD3DVideoSink * sink)
1203 {
1204   HWND hWnd;
1205   gboolean ret = FALSE;
1206
1207   LOCK_SINK (sink);
1208
1209   /* if we already had an external window, then use it again */
1210   if (sink->d3d.external_window_handle)
1211     sink->d3d.window_handle = sink->d3d.external_window_handle;
1212
1213   /* Give the app a last chance to set a window id */
1214   if (!sink->d3d.window_handle)
1215     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1216
1217   /* If the user did not set a window id .. check if we should create one */
1218   if (!sink->d3d.window_handle) {
1219     if (sink->create_internal_window) {
1220       if ((hWnd = d3d_create_internal_window (sink))) {
1221         GST_DEBUG_OBJECT (sink,
1222             "No window id was set.. creating internal window");
1223         d3d_set_window_handle (sink, (guintptr) hWnd, TRUE);
1224       } else {
1225         GST_ERROR_OBJECT (sink, "Failed to create internal window");
1226         goto end;
1227       }
1228     } else {
1229       GST_DEBUG_OBJECT (sink, "No window id is set..");
1230       goto end;
1231     }
1232   } else {
1233     d3d_prepare_render_window (sink);
1234   }
1235
1236   ret = TRUE;
1237
1238 end:
1239   UNLOCK_SINK (sink);
1240
1241   return ret;
1242 }
1243
1244 gboolean
1245 d3d_stop (GstD3DVideoSink * sink)
1246 {
1247   if (sink->pool)
1248     gst_buffer_pool_set_active (sink->pool, FALSE);
1249   if (sink->fallback_pool)
1250     gst_buffer_pool_set_active (sink->fallback_pool, FALSE);
1251   gst_object_replace ((GstObject **) & sink->pool, NULL);
1252   gst_object_replace ((GstObject **) & sink->fallback_pool, NULL);
1253   gst_buffer_replace (&sink->fallback_buffer, NULL);
1254
1255   /* Release D3D resources */
1256   d3d_set_window_handle (sink, 0, FALSE);
1257
1258   if (sink->internal_window_thread) {
1259     g_thread_join (sink->internal_window_thread);
1260     sink->internal_window_thread = NULL;
1261   }
1262
1263   return TRUE;
1264 }
1265
1266 /* D3D Lost and Reset Device */
1267
1268 static void
1269 d3d_notify_device_lost (GstD3DVideoSink * sink)
1270 {
1271   gboolean notify = FALSE;
1272
1273   g_return_if_fail (GST_IS_D3DVIDEOSINK (sink));
1274
1275   LOCK_SINK (sink);
1276
1277   if (!sink->d3d.device_lost) {
1278     GST_WARNING_OBJECT (sink, "D3D Device has been lost. Clean up resources.");
1279
1280     /* Stream will continue with GST_FLOW_OK, until device has been reset */
1281     sink->d3d.device_lost = TRUE;
1282
1283     /* First we clean up all resources in this d3dvideo instance */
1284     d3d_release_swap_chain (sink);
1285
1286     /* Notify our hidden thread */
1287     notify = TRUE;
1288   }
1289
1290   UNLOCK_SINK (sink);
1291
1292   if (notify)
1293     d3d_class_notify_device_lost (sink);
1294 }
1295
1296 static void
1297 d3d_notify_device_reset (GstD3DVideoSink * sink)
1298 {
1299   LOCK_SINK (sink);
1300
1301   if (sink->d3d.device_lost) {
1302     GST_DEBUG_OBJECT (sink,
1303         "D3D Device has been reset. Re-init swap chain if still streaming");
1304     /* If we're still streaming.. reset swap chain */
1305     if (sink->d3d.window_handle != NULL)
1306       d3d_init_swap_chain (sink, sink->d3d.window_handle);
1307     sink->d3d.device_lost = FALSE;
1308   }
1309
1310   UNLOCK_SINK (sink);
1311 }
1312
1313 /* Swap Chains */
1314
1315 static gboolean
1316 d3d_init_swap_chain (GstD3DVideoSink * sink, HWND hWnd)
1317 {
1318   D3DPRESENT_PARAMETERS present_params;
1319   LPDIRECT3DSWAPCHAIN9 d3d_swapchain = NULL;
1320   D3DTEXTUREFILTERTYPE d3d_filtertype;
1321   HRESULT hr;
1322   GstD3DVideoSinkClass *klass;
1323   gboolean ret = FALSE;
1324
1325   g_return_val_if_fail (sink != NULL, FALSE);
1326   klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1327   g_return_val_if_fail (klass != NULL, FALSE);
1328
1329   LOCK_SINK (sink);
1330   LOCK_CLASS (sink, klass);
1331
1332   /* We need a display device */
1333   CHECK_D3D_DEVICE (klass, sink, error);
1334
1335   GST_DEBUG ("Initializing Direct3D swap chain");
1336
1337   GST_DEBUG ("Direct3D back buffer size: %dx%d", GST_VIDEO_SINK_WIDTH (sink),
1338       GST_VIDEO_SINK_HEIGHT (sink));
1339
1340   /* When windowed, width and height determined by HWND */
1341   ZeroMemory (&present_params, sizeof (present_params));
1342   present_params.Windowed = TRUE;
1343   present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;    /* D3DSWAPEFFECT_COPY */
1344   present_params.hDeviceWindow = hWnd;
1345   present_params.BackBufferFormat = klass->d3d.device.format;
1346
1347   hr = IDirect3DDevice9_CreateAdditionalSwapChain (klass->d3d.device.d3d_device,
1348       &present_params, &d3d_swapchain);
1349   ERROR_CHECK_HR (hr) {
1350     CASE_HR_ERR (D3DERR_NOTAVAILABLE);
1351     CASE_HR_ERR (D3DERR_DEVICELOST);
1352     CASE_HR_ERR (D3DERR_INVALIDCALL);
1353     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1354     CASE_HR_ERR (E_OUTOFMEMORY);
1355     CASE_HR_ERR_END (sink, "Error creating D3D swapchian");
1356     goto error;
1357   }
1358
1359   /* Determine texture filtering support. If it's supported for this format,
1360    * use the filter type determined when we created the dev and checked the
1361    * dev caps.
1362    */
1363   hr = IDirect3D9_CheckDeviceFormat (klass->d3d.d3d,
1364       klass->d3d.device.adapter,
1365       D3DDEVTYPE_HAL,
1366       klass->d3d.device.format,
1367       D3DUSAGE_QUERY_FILTER, D3DRTYPE_TEXTURE, sink->d3d.format);
1368   if (hr == D3D_OK)
1369     d3d_filtertype = klass->d3d.device.filter_type;
1370   else
1371     d3d_filtertype = D3DTEXF_NONE;
1372
1373   GST_DEBUG ("Direct3D stretch rect texture filter: %d", d3d_filtertype);
1374
1375   sink->d3d.filtertype = d3d_filtertype;
1376
1377   if (sink->d3d.swapchain != NULL)
1378     IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1379
1380   sink->d3d.swapchain = d3d_swapchain;
1381
1382   ret = TRUE;
1383
1384 error:
1385   if (!ret) {
1386     if (d3d_swapchain)
1387       IDirect3DSwapChain9_Release (d3d_swapchain);
1388   }
1389
1390   UNLOCK_CLASS (sink, klass);
1391   UNLOCK_SINK (sink);
1392
1393   return ret;
1394 }
1395
1396 static gboolean
1397 d3d_release_swap_chain (GstD3DVideoSink * sink)
1398 {
1399   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1400   int ref_count;
1401   gboolean ret = FALSE;
1402
1403   LOCK_SINK (sink);
1404
1405   GST_DEBUG_OBJECT (sink, "Releasing Direct3D swap chain");
1406
1407   CHECK_D3D_DEVICE (klass, sink, end);
1408
1409   if (!sink->d3d.swapchain) {
1410     ret = TRUE;
1411     goto end;
1412   }
1413
1414   gst_buffer_replace (&sink->fallback_buffer, NULL);
1415   if (sink->fallback_pool)
1416     gst_buffer_pool_set_active (sink->fallback_pool, FALSE);
1417
1418   if (sink->d3d.swapchain) {
1419     ref_count = IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1420     sink->d3d.swapchain = NULL;
1421     GST_DEBUG_OBJECT (sink, "D3D swapchain released. Ref count: %d", ref_count);
1422   }
1423
1424   if (sink->d3d.surface) {
1425     ref_count = IDirect3DSurface9_Release (sink->d3d.surface);
1426     sink->d3d.surface = NULL;
1427     GST_DEBUG_OBJECT (sink, "D3D surface released. Ref count: %d", ref_count);
1428   }
1429
1430   gst_d3d9_overlay_free (sink);
1431   ret = TRUE;
1432
1433 end:
1434   UNLOCK_SINK (sink);
1435
1436   return ret;
1437 }
1438
1439 static gboolean
1440 d3d_resize_swap_chain (GstD3DVideoSink * sink)
1441 {
1442   GstD3DVideoSinkClass *klass;
1443   D3DPRESENT_PARAMETERS d3d_pp;
1444   LPDIRECT3DSWAPCHAIN9 swapchain = NULL;
1445   gint w = 0, h = 0, ref_count = 0;
1446   gboolean ret = FALSE;
1447   HRESULT hr;
1448   gboolean need_new = FALSE;
1449   int clip_ret;
1450   HDC handle_hdc;
1451   RECT clip_rectangle;
1452
1453   g_return_val_if_fail (sink != NULL, FALSE);
1454   klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1455   g_return_val_if_fail (klass != NULL, FALSE);
1456
1457   LOCK_SINK (sink);
1458
1459   if (!sink->d3d.renderable || sink->d3d.device_lost) {
1460     UNLOCK_SINK (sink);
1461     return FALSE;
1462   }
1463
1464   LOCK_CLASS (sink, klass);
1465
1466   CHECK_WINDOW_HANDLE (sink, end, FALSE);
1467   CHECK_D3D_DEVICE (klass, sink, end);
1468   CHECK_D3D_SWAPCHAIN (sink, end);
1469
1470   handle_hdc = GetDC (sink->d3d.window_handle);
1471   clip_ret = GetClipBox (handle_hdc, &clip_rectangle);
1472   ReleaseDC (sink->d3d.window_handle, handle_hdc);
1473   if (clip_ret == NULLREGION) {
1474     GST_DEBUG_OBJECT (sink, "Window is hidden, not resizing swapchain");
1475     UNLOCK_CLASS (sink, klass);
1476     UNLOCK_SINK (sink);
1477     return TRUE;
1478   }
1479
1480   d3d_get_hwnd_window_size (sink->d3d.window_handle, &w, &h);
1481   ZeroMemory (&d3d_pp, sizeof (d3d_pp));
1482
1483   /* Get the parameters used to create this swap chain */
1484   hr = IDirect3DSwapChain9_GetPresentParameters (sink->d3d.swapchain, &d3d_pp);
1485   if (hr != D3D_OK) {
1486     GST_ERROR_OBJECT (sink,
1487         "Unable to determine Direct3D present parameters for swap chain");
1488     goto end;
1489   }
1490
1491   /* Reisze needed? */
1492   if (d3d_pp.BackBufferWidth != w || d3d_pp.BackBufferHeight != h)
1493     need_new = TRUE;
1494 #if 0
1495   /* Render rect set or unset? */
1496   if ((d3d_pp.SwapEffect != D3DSWAPEFFECT_COPY && sink->d3d.render_rect) ||
1497       (d3d_pp.SwapEffect != D3DSWAPEFFECT_DISCARD
1498           && sink->d3d.render_rect == NULL)) {
1499     d3d_pp.SwapEffect =
1500         (sink->d3d.render_rect ==
1501         NULL) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
1502     GST_DEBUG_OBJECT (sink, "Setting SwapEffect: %s",
1503         sink->d3d.render_rect ? "COPY" : "DISCARD");
1504     need_new = TRUE;
1505   }
1506 #endif
1507   if (!need_new) {
1508     ret = TRUE;
1509     goto end;
1510   }
1511
1512   GST_DEBUG_OBJECT (sink, "Resizing swapchain %dx%d to %dx%d",
1513       d3d_pp.BackBufferWidth, d3d_pp.BackBufferHeight, w, h);
1514
1515
1516   /* As long as present params windowed == TRUE, width or height
1517    * of 0 will force use of HWND's size.
1518    */
1519   d3d_pp.BackBufferWidth = 0;
1520   d3d_pp.BackBufferHeight = 0;
1521
1522   /* Release current swapchain */
1523   if (sink->d3d.swapchain != NULL) {
1524     ref_count = IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1525     if (ref_count > 0) {
1526       GST_WARNING_OBJECT (sink, "Release swapchain refcount: %d", ref_count);
1527     }
1528     sink->d3d.swapchain = NULL;
1529   }
1530
1531   hr = IDirect3DDevice9_CreateAdditionalSwapChain (klass->d3d.device.d3d_device,
1532       &d3d_pp, &swapchain);
1533   ERROR_CHECK_HR (hr) {
1534     CASE_HR_ERR (D3DERR_NOTAVAILABLE);
1535     CASE_HR_ERR (D3DERR_DEVICELOST);
1536     CASE_HR_ERR (D3DERR_INVALIDCALL);
1537     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1538     CASE_HR_ERR (E_OUTOFMEMORY);
1539     CASE_HR_ERR_END (sink, "Error creating swapchian");
1540     goto end;
1541   }
1542
1543   sink->d3d.swapchain = swapchain;
1544   sink->d3d.overlay_needs_resize = TRUE;
1545   ret = TRUE;
1546
1547 end:
1548   UNLOCK_CLASS (sink, klass);
1549   UNLOCK_SINK (sink);
1550
1551   return ret;
1552 }
1553
1554 static gboolean
1555 d3d_copy_buffer (GstD3DVideoSink * sink, GstBuffer * from, GstBuffer * to)
1556 {
1557   gboolean ret = FALSE;
1558   GstVideoFrame from_frame, to_frame;
1559
1560   memset (&from_frame, 0, sizeof (from_frame));
1561   memset (&to_frame, 0, sizeof (to_frame));
1562
1563   LOCK_SINK (sink);
1564
1565   if (!sink->d3d.renderable || sink->d3d.device_lost)
1566     goto end;
1567
1568   if (!gst_video_frame_map (&from_frame, &sink->info, from, GST_MAP_READ) ||
1569       !gst_video_frame_map (&to_frame, &sink->info, to, GST_MAP_WRITE)) {
1570     GST_ERROR_OBJECT (sink, "NULL GstBuffer");
1571     goto end;
1572   }
1573
1574   switch (sink->format) {
1575     case GST_VIDEO_FORMAT_YUY2:
1576     case GST_VIDEO_FORMAT_UYVY:{
1577       const guint8 *src;
1578       guint8 *dst;
1579       gint dststride, srcstride;
1580       gint i, h, w;
1581
1582       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1583       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1584       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1585       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1586       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1587       w = GST_ROUND_UP_4 (GST_VIDEO_FRAME_WIDTH (&from_frame) * 2);
1588
1589       for (i = 0; i < h; i++) {
1590         memcpy (dst, src, w);
1591         dst += dststride;
1592         src += srcstride;
1593       }
1594
1595       break;
1596     }
1597     case GST_VIDEO_FORMAT_I420:
1598     case GST_VIDEO_FORMAT_YV12:{
1599       const guint8 *src;
1600       guint8 *dst;
1601       gint srcstride, dststride;
1602       gint i, j, h_, w_;
1603
1604       for (i = 0; i < 3; i++) {
1605         src = GST_VIDEO_FRAME_COMP_DATA (&from_frame, i);
1606         dst = GST_VIDEO_FRAME_COMP_DATA (&to_frame, i);
1607         srcstride = GST_VIDEO_FRAME_COMP_STRIDE (&from_frame, i);
1608         dststride = GST_VIDEO_FRAME_COMP_STRIDE (&to_frame, i);
1609         h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
1610         w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
1611
1612         for (j = 0; j < h_; j++) {
1613           memcpy (dst, src, w_);
1614           dst += dststride;
1615           src += srcstride;
1616         }
1617       }
1618
1619       break;
1620     }
1621     case GST_VIDEO_FORMAT_NV12:{
1622       const guint8 *src;
1623       guint8 *dst;
1624       gint srcstride, dststride;
1625       gint i, j, h_, w_;
1626
1627       for (i = 0; i < 2; i++) {
1628         src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, i);
1629         dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, i);
1630         srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, i);
1631         dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, i);
1632         h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
1633         w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
1634
1635         for (j = 0; j < h_; j++) {
1636           memcpy (dst, src, w_ * 2);
1637           dst += dststride;
1638           src += srcstride;
1639         }
1640       }
1641
1642       break;
1643     }
1644     case GST_VIDEO_FORMAT_BGRA:
1645     case GST_VIDEO_FORMAT_RGBA:
1646     case GST_VIDEO_FORMAT_BGRx:
1647     case GST_VIDEO_FORMAT_RGBx:{
1648       const guint8 *src;
1649       guint8 *dst;
1650       gint srcstride, dststride;
1651       gint i, h, w;
1652
1653       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1654       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1655       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1656       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1657       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1658       w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 4;
1659
1660       for (i = 0; i < h; i++) {
1661         memcpy (dst, src, w);
1662         dst += dststride;
1663         src += srcstride;
1664       }
1665
1666       break;
1667     }
1668     case GST_VIDEO_FORMAT_BGR:{
1669       const guint8 *src;
1670       guint8 *dst;
1671       gint srcstride, dststride;
1672       gint i, h, w;
1673
1674       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1675       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1676       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1677       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1678       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1679       w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 3;
1680
1681       for (i = 0; i < h; i++) {
1682         memcpy (dst, src, w);
1683         dst += dststride;
1684         src += srcstride;
1685       }
1686
1687       break;
1688     }
1689     case GST_VIDEO_FORMAT_RGB16:
1690     case GST_VIDEO_FORMAT_RGB15:{
1691       const guint8 *src;
1692       guint8 *dst;
1693       gint srcstride, dststride;
1694       gint i, h, w;
1695
1696       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1697       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1698       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1699       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1700       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1701       w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 2;
1702
1703       for (i = 0; i < h; i++) {
1704         memcpy (dst, src, w);
1705         dst += dststride;
1706         src += srcstride;
1707       }
1708
1709       break;
1710     }
1711     default:
1712       goto unhandled_format;
1713   }
1714
1715   ret = TRUE;
1716
1717 end:
1718   if (from_frame.buffer)
1719     gst_video_frame_unmap (&from_frame);
1720   if (to_frame.buffer)
1721     gst_video_frame_unmap (&to_frame);
1722
1723   UNLOCK_SINK (sink);
1724   return ret;
1725
1726 unhandled_format:
1727   GST_ERROR_OBJECT (sink,
1728       "Unhandled format '%s' -> '%s' (should not get here)",
1729       gst_video_format_to_string (sink->format),
1730       d3d_format_to_string (sink->d3d.format));
1731   ret = FALSE;
1732   goto end;
1733 }
1734
1735 static gboolean
1736 d3d_present_swap_chain (GstD3DVideoSink * sink)
1737 {
1738   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1739   LPDIRECT3DSURFACE9 back_buffer = NULL;
1740   gboolean ret = FALSE;
1741   HRESULT hr;
1742   RECT dstr, srcr, *pDestRect = NULL, *pSrcRect = NULL;
1743
1744   LOCK_SINK (sink);
1745
1746   if (!sink->d3d.renderable || sink->d3d.device_lost) {
1747     UNLOCK_SINK (sink);
1748     return FALSE;
1749   }
1750
1751   LOCK_CLASS (sink, klass);
1752
1753   CHECK_WINDOW_HANDLE (sink, end, FALSE);
1754   CHECK_D3D_DEVICE (klass, sink, end);
1755   CHECK_D3D_SWAPCHAIN (sink, end);
1756
1757   /* Set the render target to our swap chain */
1758   hr = IDirect3DSwapChain9_GetBackBuffer (sink->d3d.swapchain, 0,
1759       D3DBACKBUFFER_TYPE_MONO, &back_buffer);
1760   ERROR_CHECK_HR (hr) {
1761     CASE_HR_ERR (D3DERR_INVALIDCALL);
1762     CASE_HR_ERR_END (sink, "IDirect3DSwapChain9_GetBackBuffer");
1763     goto end;
1764   }
1765   hr = IDirect3DDevice9_SetRenderTarget (klass->d3d.device.d3d_device, 0,
1766       back_buffer);
1767   ERROR_CHECK_HR (hr) {
1768     CASE_HR_ERR (D3DERR_INVALIDCALL);
1769     CASE_HR_ERR_END (sink, "IDirect3DDevice9_SetRenderTarget");
1770     goto end;
1771   }
1772   hr = IDirect3DSurface9_Release (back_buffer);
1773   ERROR_CHECK_HR (hr) {
1774     CASE_HR_ERR (D3DERR_INVALIDCALL);
1775     CASE_HR_ERR_END (sink, "IDirect3DSurface9_Release");
1776     goto end;
1777   }
1778
1779   /* Clear the target */
1780   hr = IDirect3DDevice9_Clear (klass->d3d.device.d3d_device, 0, NULL,
1781       D3DCLEAR_TARGET, D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0);
1782   ERROR_CHECK_HR (hr) {
1783     CASE_HR_ERR (D3DERR_INVALIDCALL);
1784     CASE_HR_ERR_END (sink, "IDirect3DDevice9_Clear");
1785     goto end;
1786   }
1787
1788   hr = IDirect3DDevice9_BeginScene (klass->d3d.device.d3d_device);
1789   ERROR_CHECK_HR (hr) {
1790     CASE_HR_ERR (D3DERR_INVALIDCALL);
1791     CASE_HR_ERR_END (sink, "IDirect3DDevice9_BeginScene");
1792     goto end;
1793   }
1794
1795   if (!gst_d3d9_overlay_set_render_state (sink)) {
1796     IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1797     goto end;
1798   }
1799
1800   /* Stretch and blit ops, to copy offscreen surface buffer
1801    * to Display back buffer.
1802    */
1803   if (!d3d_stretch_and_copy (sink, back_buffer) ||
1804       !gst_d3d9_overlay_render (sink)) {
1805     IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1806     goto end;
1807   }
1808
1809   hr = IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1810   ERROR_CHECK_HR (hr) {
1811     CASE_HR_ERR (D3DERR_INVALIDCALL);
1812     CASE_HR_ERR_END (sink, "IDirect3DDevice9_EndScene");
1813     goto end;
1814   }
1815
1816   if (d3d_get_render_rects (sink->d3d.render_rect, &dstr, &srcr)) {
1817     pDestRect = &dstr;
1818     pSrcRect = &srcr;
1819   }
1820
1821   /*
1822    * Swap back and front buffers on video card and present to the user
1823    */
1824   hr = IDirect3DSwapChain9_Present (sink->d3d.swapchain, pSrcRect, pDestRect,
1825       NULL, NULL, 0);
1826   if (hr == D3DERR_DEVICELOST) {
1827     d3d_notify_device_lost (sink);
1828     ret = TRUE;
1829     goto end;
1830   }
1831   ERROR_CHECK_HR (hr) {
1832     CASE_HR_ERR (D3DERR_DEVICELOST);
1833     CASE_HR_ERR (D3DERR_DRIVERINTERNALERROR);
1834     CASE_HR_ERR (D3DERR_INVALIDCALL);
1835     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1836     CASE_HR_ERR (E_OUTOFMEMORY);
1837     CASE_HR_DBG_END (sink, "IDirect3DSwapChain9_Present failure");
1838     goto end;
1839   }
1840
1841   ret = TRUE;
1842
1843 end:
1844   UNLOCK_SINK (sink);
1845   UNLOCK_CLASS (sink, klass);
1846   return ret;
1847 }
1848
1849 static gboolean
1850 d3d_stretch_and_copy (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 back_buffer)
1851 {
1852   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1853   GstVideoRectangle *render_rect = NULL;
1854   RECT r, s;
1855   RECT *r_p = NULL;
1856   HRESULT hr;
1857   gboolean ret = FALSE;
1858
1859   LOCK_SINK (sink);
1860
1861   CHECK_WINDOW_HANDLE (sink, end, FALSE);
1862   CHECK_D3D_DEVICE (klass, sink, end);
1863   CHECK_D3D_SURFACE (sink, end);
1864
1865   render_rect = sink->d3d.render_rect;
1866
1867   if (sink->force_aspect_ratio) {
1868     gint window_width;
1869     gint window_height;
1870     GstVideoRectangle src;
1871     GstVideoRectangle dst;
1872     GstVideoRectangle result;
1873
1874     memset (&dst, 0, sizeof (dst));
1875     memset (&src, 0, sizeof (src));
1876
1877     /* Set via GstXOverlay set_render_rect */
1878     if (render_rect) {
1879       memcpy (&dst, render_rect, sizeof (dst));
1880     } else {
1881       d3d_get_hwnd_window_size (sink->d3d.window_handle, &window_width,
1882           &window_height);
1883       dst.w = window_width;
1884       dst.h = window_height;
1885     }
1886
1887     src.w = GST_VIDEO_SINK_WIDTH (sink);
1888     src.h = GST_VIDEO_SINK_HEIGHT (sink);
1889
1890     gst_video_sink_center_rect (src, dst, &result, TRUE);
1891
1892     r.left = result.x;
1893     r.top = result.y;
1894     r.right = result.x + result.w;
1895     r.bottom = result.y + result.h;
1896     r_p = &r;
1897   } else if (render_rect) {
1898     r.left = 0;
1899     r.top = 0;
1900     r.right = render_rect->w;
1901     r.bottom = render_rect->h;
1902     r_p = &r;
1903   }
1904
1905   s.left = sink->crop_rect.x;
1906   s.top = sink->crop_rect.y;
1907   s.right = sink->crop_rect.x + sink->crop_rect.w;
1908   s.bottom = sink->crop_rect.y + sink->crop_rect.h;
1909
1910   /* TODO: StretchRect returns error if the dest rect is outside
1911    * the backbuffer area. So we need to calc how much of the src
1912    * surface is being scaled / copied to the render rect..
1913    */
1914
1915   hr = IDirect3DDevice9_StretchRect (klass->d3d.device.d3d_device, sink->d3d.surface,   /* Source Surface */
1916       &s,                       /* Source Surface Rect (NULL: Whole) */
1917       back_buffer,              /* Dest Surface */
1918       r_p,                      /* Dest Surface Rect (NULL: Whole) */
1919       klass->d3d.device.filter_type);
1920
1921   if (hr == D3D_OK) {
1922     ret = TRUE;
1923   } else {
1924     GST_ERROR_OBJECT (sink, "Failure calling Direct3DDevice9_StretchRect");
1925   }
1926
1927 end:
1928   UNLOCK_SINK (sink);
1929
1930   return ret;
1931 }
1932
1933 GstFlowReturn
1934 d3d_render_buffer (GstD3DVideoSink * sink, GstBuffer * buf)
1935 {
1936   WindowHandleVisibility handle_visibility = WINDOW_VISIBILITY_ERROR;
1937   int clip_ret;
1938   HDC handle_hdc;
1939   RECT handle_rectangle;
1940   RECT clip_rectangle;
1941
1942   GstFlowReturn ret = GST_FLOW_OK;
1943   GstMemory *mem;
1944   LPDIRECT3DSURFACE9 surface = NULL;
1945   GstVideoCropMeta *crop = NULL;
1946
1947   LOCK_SINK (sink);
1948
1949   if (!sink->d3d.window_handle) {
1950     if (sink->stream_stop_on_close) {
1951       /* Handle window deletion by posting an error on the bus */
1952       GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
1953           ("Output window was closed"), (NULL));
1954       ret = GST_FLOW_ERROR;
1955     }
1956     goto end;
1957   }
1958
1959   if (sink->d3d.device_lost) {
1960     GST_LOG_OBJECT (sink, "Device lost, waiting for reset..");
1961     goto end;
1962   }
1963
1964   /* check for window handle visibility, if hidden skip frame rendering  */
1965
1966   handle_hdc = GetDC (sink->d3d.window_handle);
1967   GetClientRect (sink->d3d.window_handle, &handle_rectangle);
1968   clip_ret = GetClipBox (handle_hdc, &clip_rectangle);
1969   ReleaseDC (sink->d3d.window_handle, handle_hdc);
1970
1971   switch (clip_ret) {
1972     case NULLREGION:
1973       handle_visibility = WINDOW_VISIBILITY_HIDDEN;
1974       break;
1975     case SIMPLEREGION:
1976       if (EqualRect (&clip_rectangle, &handle_rectangle))
1977         handle_visibility = WINDOW_VISIBILITY_FULL;
1978       else
1979         handle_visibility = WINDOW_VISIBILITY_PARTIAL;
1980       break;
1981     case COMPLEXREGION:
1982       handle_visibility = WINDOW_VISIBILITY_PARTIAL;
1983       break;
1984     default:
1985       handle_visibility = WINDOW_VISIBILITY_ERROR;
1986       break;
1987   }
1988
1989   if (handle_visibility == WINDOW_VISIBILITY_HIDDEN) {
1990     GST_DEBUG_OBJECT (sink, "Hidden hwnd, skipping frame rendering...");
1991     goto end;
1992   }
1993
1994   GST_INFO_OBJECT (sink, "%s %" GST_TIME_FORMAT,
1995       (sink->d3d.window_handle != NULL) ? "Render" : "No Win",
1996       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1997
1998   crop = gst_buffer_get_video_crop_meta (buf);
1999   if (crop) {
2000     sink->crop_rect.x = crop->x;
2001     sink->crop_rect.y = crop->y;
2002     sink->crop_rect.w = crop->width;
2003     sink->crop_rect.h = crop->height;
2004   } else {
2005     sink->crop_rect.x = 0;
2006     sink->crop_rect.y = 0;
2007     sink->crop_rect.w = sink->info.width;
2008     sink->crop_rect.h = sink->info.height;
2009   }
2010
2011   /* Resize swapchain if needed */
2012   if (!d3d_resize_swap_chain (sink)) {
2013     ret = GST_FLOW_ERROR;
2014     goto end;
2015   }
2016
2017   if (gst_buffer_n_memory (buf) != 1 ||
2018       (mem = gst_buffer_peek_memory (buf, 0)) == 0 ||
2019       !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
2020     GstBuffer *tmp;
2021     GstBufferPoolAcquireParams params = { 0, };
2022
2023     if (!sink->fallback_pool
2024         || !gst_buffer_pool_set_active (sink->fallback_pool, TRUE)) {
2025       ret = GST_FLOW_NOT_NEGOTIATED;
2026       goto end;
2027     }
2028
2029     /* take a buffer from our pool, if there is no buffer in the pool something
2030      * is seriously wrong, waiting for the pool here might deadlock when we try
2031      * to go to PAUSED because we never flush the pool. */
2032     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
2033     ret = gst_buffer_pool_acquire_buffer (sink->fallback_pool, &tmp, &params);
2034     if (ret != GST_FLOW_OK)
2035       goto end;
2036
2037     if (sink->fallback_buffer) {
2038       gst_buffer_unref (sink->fallback_buffer);
2039       sink->fallback_buffer = NULL;
2040     }
2041
2042     mem = gst_buffer_peek_memory (tmp, 0);
2043     if (!mem || !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
2044       ret = GST_FLOW_ERROR;
2045       gst_buffer_unref (tmp);
2046       goto end;
2047     }
2048     d3d_copy_buffer (sink, buf, tmp);
2049     buf = tmp;
2050
2051     surface = ((GstD3DSurfaceMemory *) mem)->surface;
2052
2053     /* Need to keep an additional ref until the next buffer
2054      * to make sure it isn't reused until then */
2055     sink->fallback_buffer = buf;
2056   } else {
2057     mem = gst_buffer_peek_memory (buf, 0);
2058     surface = ((GstD3DSurfaceMemory *) mem)->surface;
2059
2060     if (sink->fallback_buffer) {
2061       gst_buffer_unref (sink->fallback_buffer);
2062       sink->fallback_buffer = NULL;
2063     }
2064   }
2065
2066   if (sink->d3d.surface)
2067     IDirect3DSurface9_Release (sink->d3d.surface);
2068   IDirect3DSurface9_AddRef (surface);
2069   sink->d3d.surface = surface;
2070
2071   if (!d3d_present_swap_chain (sink)) {
2072     ret = GST_FLOW_ERROR;
2073     goto end;
2074   }
2075
2076 end:
2077   UNLOCK_SINK (sink);
2078   return ret;
2079 }
2080
2081
2082 /* D3D Window Proc Functions */
2083
2084 static LRESULT APIENTRY
2085 d3d_wnd_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2086 {
2087   GstD3DVideoSink *sink =
2088       (GstD3DVideoSink *) GetProp (hWnd, TEXT ("GstD3DVideoSink"));
2089   WNDPROC proc;
2090   LRESULT ret = 0;
2091
2092   /* d3dvideosink object might not available yet.
2093    * The thread for message queue starts earlier than SetProp... */
2094   if (!sink)
2095     return DefWindowProc (hWnd, message, wParam, lParam);
2096
2097   LOCK_SINK (sink);
2098   proc = sink->d3d.orig_wnd_proc;
2099   UNLOCK_SINK (sink);
2100
2101   switch (message) {
2102     case WM_ERASEBKGND:
2103       return TRUE;
2104     case WM_PAINT:{
2105       if (proc)
2106         ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2107       /* Call this afterwards to ensure that our paint happens last */
2108       d3d_present_swap_chain (sink);
2109       goto end;
2110     }
2111     case WM_SIZE:{
2112       if (proc)
2113         ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2114
2115       /* Don't resize if the window is being minimized. Recreating the
2116        * swap chain will fail if the window is minimized
2117        */
2118       if (wParam != SIZE_MINIMIZED)
2119         d3d_resize_swap_chain (sink);
2120       goto end;
2121     }
2122     case WM_KEYDOWN:
2123     case WM_KEYUP:
2124       if (sink->enable_navigation_events) {
2125         gunichar2 wcrep[128];
2126         if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) {
2127           gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
2128           if (utfrep) {
2129             if (message == WM_KEYDOWN)
2130               gst_navigation_send_key_event (GST_NAVIGATION (sink), "key-press",
2131                   utfrep);
2132             else if (message == WM_KEYUP)
2133               gst_navigation_send_key_event (GST_NAVIGATION (sink),
2134                   "key-release", utfrep);
2135             g_free (utfrep);
2136           }
2137         }
2138       }
2139       break;
2140     case WM_LBUTTONDOWN:
2141     case WM_LBUTTONUP:
2142     case WM_RBUTTONDOWN:
2143     case WM_RBUTTONUP:
2144     case WM_MBUTTONDOWN:
2145     case WM_MBUTTONUP:
2146     case WM_MOUSEMOVE:{
2147       gdouble x = 0, y = 0;
2148       if (sink->enable_navigation_events
2149           && d3d_get_render_coordinates (sink, LOWORD (lParam), HIWORD (lParam),
2150               &x, &y)) {
2151         gint button;
2152         const gchar *action = NULL;
2153         switch (message) {
2154           case WM_MOUSEMOVE:
2155             button = 0;
2156             action = "mouse-move";
2157             break;
2158           case WM_LBUTTONDOWN:
2159             button = 1;
2160             action = "mouse-button-press";
2161             break;
2162           case WM_LBUTTONUP:
2163             button = 1;
2164             action = "mouse-button-release";
2165             break;
2166           case WM_RBUTTONDOWN:
2167             button = 2;
2168             action = "mouse-button-press";
2169             break;
2170           case WM_RBUTTONUP:
2171             button = 2;
2172             action = "mouse-button-release";
2173             break;
2174           case WM_MBUTTONDOWN:
2175             button = 3;
2176             action = "mouse-button-press";
2177             break;
2178           case WM_MBUTTONUP:
2179             button = 3;
2180             action = "mouse-button-release";
2181             break;
2182           default:
2183             break;
2184         }
2185         if (action) {
2186           /* GST_DEBUG_OBJECT(sink, "%s: %lfx%lf", action, x, y); */
2187           gst_navigation_send_mouse_event (GST_NAVIGATION (sink), action,
2188               button, x, y);
2189         }
2190       }
2191       break;
2192     }
2193     case WM_CLOSE:
2194       d3d_set_window_handle (sink, 0, FALSE);
2195       break;
2196     default:
2197       break;
2198   }
2199
2200   if (proc)
2201     ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2202   else
2203     ret = DefWindowProc (hWnd, message, wParam, lParam);
2204
2205 end:
2206   return ret;
2207 }
2208
2209 /* Internal Window */
2210
2211 static LRESULT APIENTRY
2212 d3d_wnd_proc_internal (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2213 {
2214   switch (message) {
2215     case WM_DESTROY:
2216       GST_DEBUG ("Internal window: WM_DESTROY");
2217       /* Tell the internal window thread to shut down */
2218       PostQuitMessage (0);
2219       GST_DEBUG ("Posted quit..");
2220       break;
2221   }
2222
2223   return DefWindowProc (hWnd, message, wParam, lParam);
2224 }
2225
2226 static HWND
2227 _d3d_create_internal_window (GstD3DVideoSink * sink)
2228 {
2229   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2230   int width, height;
2231   int offx, offy;
2232   DWORD exstyle, style;
2233   HWND video_window;
2234   RECT rect;
2235   int screenwidth;
2236   int screenheight;
2237
2238   /*
2239    * GST_VIDEO_SINK_WIDTH() is the aspect-ratio-corrected size of the video.
2240    * GetSystemMetrics() returns the width of the dialog's border (doubled
2241    * b/c of left and right borders).
2242    */
2243   width = GST_VIDEO_SINK_WIDTH (sink) + GetSystemMetrics (SM_CXSIZEFRAME) * 2;
2244   height =
2245       GST_VIDEO_SINK_HEIGHT (sink) + GetSystemMetrics (SM_CYCAPTION) +
2246       (GetSystemMetrics (SM_CYSIZEFRAME) * 2);
2247
2248   SystemParametersInfo (SPI_GETWORKAREA, 0, &rect, 0);
2249   screenwidth = rect.right - rect.left;
2250   screenheight = rect.bottom - rect.top;
2251   offx = rect.left;
2252   offy = rect.top;
2253
2254   /* Make it fit into the screen without changing the aspect ratio. */
2255   if (width > screenwidth) {
2256     double ratio = (double) screenwidth / (double) width;
2257     width = screenwidth;
2258     height = (int) (height * ratio);
2259   }
2260
2261   if (height > screenheight) {
2262     double ratio = (double) screenheight / (double) height;
2263     height = screenheight;
2264     width = (int) (width * ratio);
2265   }
2266
2267   style = WS_OVERLAPPEDWINDOW;  /* Normal top-level window */
2268   exstyle = 0;
2269   video_window = CreateWindowEx (exstyle,
2270       klass->d3d.wnd_class.lpszClassName,
2271       TEXT ("GStreamer D3D video sink (internal window)"),
2272       style, offx, offy, width, height,
2273       NULL, NULL, klass->d3d.wnd_class.hInstance, sink);
2274
2275   if (video_window == NULL) {
2276     GST_ERROR_OBJECT (sink, "Failed to create internal window: %lu",
2277         GetLastError ());
2278     return NULL;
2279   }
2280
2281   /* Now show the window, as appropriate */
2282   ShowWindow (video_window, SW_SHOWNORMAL);
2283
2284   /* Trigger the initial paint of the window */
2285   UpdateWindow (video_window);
2286
2287   return video_window;
2288 }
2289
2290 typedef struct
2291 {
2292   GstD3DVideoSink *sink;
2293   gboolean error;
2294   HWND hWnd;
2295   GMutex lock;
2296   GCond cond;
2297 } D3DInternalWindowDat;
2298
2299 static gpointer
2300 d3d_internal_window_thread (D3DInternalWindowDat * dat)
2301 {
2302   GstD3DVideoSink *sink;
2303   HWND hWnd;
2304   MSG msg;
2305
2306   g_return_val_if_fail (dat != NULL, NULL);
2307
2308   sink = dat->sink;
2309   GST_DEBUG_OBJECT (sink, "Entering internal window thread: %p",
2310       g_thread_self ());
2311
2312   /* Create internal window */
2313   g_mutex_lock (&dat->lock);
2314   hWnd = _d3d_create_internal_window (sink);
2315   if (!hWnd) {
2316     GST_ERROR_OBJECT (sink, "Failed to create internal window");
2317     dat->error = TRUE;
2318     g_cond_signal (&dat->cond);
2319     g_mutex_unlock (&dat->lock);
2320     goto end;
2321   }
2322
2323   dat->hWnd = hWnd;
2324   g_cond_signal (&dat->cond);
2325   g_mutex_unlock (&dat->lock);
2326
2327   /*
2328    * Internal window message loop
2329    */
2330
2331   while (GetMessage (&msg, NULL, 0, 0)) {
2332     if (msg.message == WM_QUIT_THREAD)
2333       break;
2334     TranslateMessage (&msg);
2335     DispatchMessage (&msg);
2336   }
2337
2338 end:
2339   GST_DEBUG_OBJECT (sink, "Exiting internal window thread: %p",
2340       g_thread_self ());
2341   return NULL;
2342 }
2343
2344 static HWND
2345 d3d_create_internal_window (GstD3DVideoSink * sink)
2346 {
2347   GThread *thread;
2348   D3DInternalWindowDat dat;
2349   gint64 end_time;
2350   gboolean timeout = FALSE;
2351
2352   dat.sink = sink;
2353   dat.error = FALSE;
2354   dat.hWnd = 0;
2355   g_mutex_init (&dat.lock);
2356   g_cond_init (&dat.cond);
2357
2358   g_mutex_lock (&dat.lock);
2359   thread =
2360       g_thread_new ("d3dvideosink-window-thread",
2361       (GThreadFunc) d3d_internal_window_thread, &dat);
2362   if (!thread) {
2363     g_mutex_unlock (&dat.lock);
2364     GST_ERROR ("Failed to created internal window thread");
2365     goto clear;
2366   }
2367
2368   sink->internal_window_thread = thread;
2369
2370   end_time = g_get_monotonic_time () + 10 * G_TIME_SPAN_SECOND;
2371   /* Wait 10 seconds for window proc loop to start up */
2372   while (!dat.error && !dat.hWnd) {
2373     if (!g_cond_wait_until (&dat.cond, &dat.lock, end_time)) {
2374       timeout = TRUE;
2375       break;
2376     }
2377   }
2378   g_mutex_unlock (&dat.lock);
2379
2380   GST_DEBUG_OBJECT (sink, "Created window: %p (error: %d, timeout: %d)",
2381       dat.hWnd, dat.error, timeout);
2382
2383 clear:
2384   {
2385     g_mutex_clear (&dat.lock);
2386     g_cond_clear (&dat.cond);
2387   }
2388
2389   return dat.hWnd;
2390 }
2391
2392 /* D3D Video Class Methods */
2393
2394 gboolean
2395 d3d_class_init (GstD3DVideoSink * sink)
2396 {
2397   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2398   gulong timeout_interval = 10000;      /* 10 ms interval */
2399   gulong intervals = (10000000 / timeout_interval);     /* 10 secs */
2400   gboolean ret = FALSE;
2401   gulong i;
2402
2403   g_return_val_if_fail (klass != NULL, FALSE);
2404
2405   LOCK_CLASS (sink, klass);
2406
2407   klass->d3d.refs += 1;
2408   GST_DEBUG ("D3D class init [refs:%u]", klass->d3d.refs);
2409   klass->d3d.sink_list = g_list_append (klass->d3d.sink_list, sink);
2410
2411   if (klass->d3d.refs > 1)
2412     goto end;
2413
2414   WM_D3DVIDEO_NOTIFY_DEVICE_LOST =
2415       RegisterWindowMessage ("WM_D3DVIDEO_NOTIFY_DEVICE_LOST");
2416
2417   klass->d3d.d3d = Direct3DCreate9 (D3D_SDK_VERSION);
2418   if (!klass->d3d.d3d) {
2419     GST_ERROR ("Unable to create Direct3D interface");
2420     goto error;
2421   }
2422
2423   /* Register Window Class for internal Windows */
2424   memset (&klass->d3d.wnd_class, 0, sizeof (WNDCLASS));
2425   klass->d3d.wnd_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
2426   klass->d3d.wnd_class.hInstance = GetModuleHandle (NULL);
2427   klass->d3d.wnd_class.lpszClassName = TEXT ("GstD3DVideoSinkInternalWindow");
2428   klass->d3d.wnd_class.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
2429   klass->d3d.wnd_class.hCursor = LoadCursor (NULL, IDC_ARROW);
2430   klass->d3d.wnd_class.hIcon = LoadIcon (NULL, IDI_APPLICATION);
2431   klass->d3d.wnd_class.cbClsExtra = 0;
2432   klass->d3d.wnd_class.cbWndExtra = 0;
2433   klass->d3d.wnd_class.lpfnWndProc = d3d_wnd_proc_internal;
2434
2435   if (RegisterClass (&klass->d3d.wnd_class) == 0) {
2436     GST_ERROR ("Failed to register window class: %lu", GetLastError ());
2437     goto error;
2438   }
2439
2440   klass->d3d.running = FALSE;
2441   klass->d3d.error_exit = FALSE;
2442   UNLOCK_CLASS (sink, klass);
2443   klass->d3d.thread =
2444       g_thread_new ("d3dvideosink-window-thread",
2445       (GThreadFunc) d3d_hidden_window_thread, klass);
2446   LOCK_CLASS (sink, klass);
2447
2448   if (!klass->d3d.thread) {
2449     GST_ERROR ("Failed to created hidden window thread");
2450     goto error;
2451   }
2452
2453   UNLOCK_CLASS (sink, klass);
2454   /* Wait 10 seconds for window proc loop to start up */
2455   for (i = 0; klass->d3d.running == FALSE && i < intervals; i++) {
2456     g_usleep (timeout_interval);
2457   }
2458   LOCK_CLASS (sink, klass);
2459
2460   if (klass->d3d.error_exit)
2461     goto error;
2462
2463   if (!klass->d3d.running) {
2464     GST_ERROR ("Waited %lu ms, window proc loop has not started",
2465         (timeout_interval * intervals) / 1000);
2466     goto error;
2467   }
2468
2469   GST_DEBUG ("Hidden window message loop is running..");
2470
2471 end:
2472   ret = TRUE;
2473 error:
2474   UNLOCK_CLASS (sink, klass);
2475
2476   if (!ret)
2477     d3d_class_destroy (sink);
2478
2479   return ret;
2480 }
2481
2482 void
2483 d3d_class_destroy (GstD3DVideoSink * sink)
2484 {
2485   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2486
2487   g_return_if_fail (klass != NULL);
2488
2489   LOCK_CLASS (sink, klass);
2490
2491   klass->d3d.refs -= 1;
2492
2493   GST_DEBUG ("D3D class destroy [refs:%u]", klass->d3d.refs);
2494
2495   klass->d3d.sink_list = g_list_remove (klass->d3d.sink_list, sink);
2496
2497   if (klass->d3d.refs >= 1)
2498     goto end;
2499
2500   UNLOCK_CLASS (sink, klass);
2501
2502   if (klass->d3d.running) {
2503     GST_DEBUG ("Shutting down window proc thread, waiting to join..");
2504     PostMessage (klass->d3d.hidden_window, WM_QUIT, 0, 0);
2505     g_thread_join (klass->d3d.thread);
2506     GST_DEBUG ("Joined..");
2507   }
2508
2509   LOCK_CLASS (sink, klass);
2510
2511   d3d_class_display_device_destroy (klass);
2512   if (klass->d3d.d3d) {
2513     int ref_count;
2514     ref_count = IDirect3D9_Release (klass->d3d.d3d);
2515     GST_DEBUG ("Direct3D object released. Reference count: %d", ref_count);
2516   }
2517
2518   UnregisterClass (klass->d3d.wnd_class.lpszClassName,
2519       klass->d3d.wnd_class.hInstance);
2520
2521   memset (&klass->d3d, 0, sizeof (GstD3DDataClass));
2522
2523 end:
2524   UNLOCK_CLASS (sink, klass);
2525 }
2526
2527 static gboolean
2528 d3d_class_display_device_create (GstD3DVideoSinkClass * klass, UINT adapter)
2529 {
2530   LPDIRECT3D9 d3d;
2531   GstD3DDisplayDevice *device;
2532   HWND hwnd;
2533   D3DCAPS9 caps;
2534   D3DDISPLAYMODE disp_mode;
2535   DWORD create_mask = 0;
2536   HRESULT hr;
2537   gboolean ret = FALSE;
2538
2539   g_return_val_if_fail (klass != NULL, FALSE);
2540
2541   GST_DEBUG (" ");
2542
2543   LOCK_CLASS (NULL, klass);
2544
2545   d3d = klass->d3d.d3d;
2546   device = &klass->d3d.device;
2547   hwnd = klass->d3d.hidden_window;
2548
2549   memset (&caps, 0, sizeof (caps));
2550   memset (&disp_mode, 0, sizeof (disp_mode));
2551   memset (&device->present_params, 0, sizeof (device->present_params));
2552
2553   device->adapter = adapter;
2554
2555   if (IDirect3D9_GetAdapterDisplayMode (d3d, adapter, &disp_mode) != D3D_OK) {
2556     GST_ERROR ("Unable to request adapter[%u] display mode", adapter);
2557     goto error;
2558   }
2559
2560   if (IDirect3D9_GetDeviceCaps (d3d, adapter, D3DDEVTYPE_HAL, &caps) != D3D_OK) {
2561     GST_ERROR ("Unable to request adapter[%u] device caps", adapter);
2562     goto error;
2563   }
2564
2565   /* Ask DirectX to please not clobber the FPU state when making DirectX
2566    * API calls. This can cause libraries such as cairo to misbehave in
2567    * certain scenarios.
2568    */
2569   create_mask = 0 | D3DCREATE_FPU_PRESERVE;
2570
2571   /* Make sure that device access is threadsafe */
2572   create_mask |= D3DCREATE_MULTITHREADED;
2573
2574   /* Determine vertex processing capabilities. Some cards have issues
2575    * using software vertex processing. Courtesy:
2576    * http://www.chadvernon.com/blog/resources/directx9/improved-direct3d-initialization/
2577    */
2578   if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ==
2579       D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
2580     create_mask |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
2581     /* if ((d3dcaps.DevCaps & D3DDEVCAPS_PUREDEVICE) == D3DDEVCAPS_PUREDEVICE) */
2582     /*  d3dcreate |= D3DCREATE_PUREDEVICE; */
2583   } else {
2584     create_mask |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
2585   }
2586
2587   /* Check the filter type. */
2588   if ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) ==
2589       D3DPTFILTERCAPS_MINFLINEAR
2590       || (caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) ==
2591       D3DPTFILTERCAPS_MAGFLINEAR) {
2592     device->filter_type = D3DTEXF_LINEAR;
2593   } else {
2594     device->filter_type = D3DTEXF_NONE;
2595   }
2596
2597   /* Setup the display mode format. */
2598   device->format = disp_mode.Format;
2599
2600   /* present_params.Flags = D3DPRESENTFLAG_VIDEO; */
2601   device->present_params.Windowed = TRUE;
2602   device->present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
2603   device->present_params.BackBufferCount = 1;
2604   device->present_params.BackBufferFormat = device->format;
2605   device->present_params.BackBufferWidth = 1;
2606   device->present_params.BackBufferHeight = 1;
2607   device->present_params.MultiSampleType = D3DMULTISAMPLE_NONE;
2608   device->present_params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;    /* D3DPRESENT_INTERVAL_IMMEDIATE; */
2609
2610   GST_DEBUG ("Creating Direct3D device for hidden window %p", hwnd);
2611
2612   if ((hr = IDirect3D9_CreateDevice (d3d, adapter, D3DDEVTYPE_HAL, hwnd,
2613               create_mask, &device->present_params,
2614               &device->d3d_device)) != D3D_OK) {
2615     GST_ERROR ("Unable to create Direct3D device. Result: %ld (0x%lx)", hr, hr);
2616     goto error;
2617   }
2618
2619   GST_DEBUG ("Display Device format: %s",
2620       d3d_format_to_string (disp_mode.Format));
2621
2622   ret = TRUE;
2623   goto end;
2624 error:
2625   memset (device, 0, sizeof (GstD3DDisplayDevice));
2626 end:
2627   UNLOCK_CLASS (NULL, klass);
2628
2629   return ret;
2630 }
2631
2632 static void
2633 d3d_class_display_device_destroy (GstD3DVideoSinkClass * klass)
2634 {
2635   g_return_if_fail (klass != NULL);
2636
2637   LOCK_CLASS (NULL, klass);
2638   if (klass->d3d.device.d3d_device) {
2639     int ref_count;
2640     ref_count = IDirect3DDevice9_Release (klass->d3d.device.d3d_device);
2641     GST_DEBUG ("Direct3D device [adapter:%u] released. Reference count: %d",
2642         klass->d3d.device.adapter, ref_count);
2643   }
2644   memset (&klass->d3d.device, 0, sizeof (GstD3DDisplayDevice));
2645   UNLOCK_CLASS (NULL, klass);
2646 }
2647
2648 static void
2649 d3d_class_notify_device_lost (GstD3DVideoSink * sink)
2650 {
2651   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2652   PostMessage (klass->d3d.hidden_window, WM_D3DVIDEO_NOTIFY_DEVICE_LOST, 0, 0);
2653 }
2654
2655 static void
2656 d3d_class_notify_device_lost_all (GstD3DVideoSinkClass * klass)
2657 {
2658   g_return_if_fail (klass != NULL);
2659
2660   LOCK_CLASS (NULL, klass);
2661   if (!klass->d3d.device_lost) {
2662     GList *lst, *clst;
2663     klass->d3d.device_lost = TRUE;
2664
2665     GST_DEBUG ("Notifying all instances of device loss");
2666
2667     clst = g_list_copy (klass->d3d.sink_list);
2668     UNLOCK_CLASS (NULL, klass);
2669
2670     for (lst = clst; lst != NULL; lst = lst->next) {
2671       GstD3DVideoSink *sink = (GstD3DVideoSink *) lst->data;
2672       if (!sink)
2673         continue;
2674       d3d_notify_device_lost (sink);
2675     }
2676     g_list_free (clst);
2677     LOCK_CLASS (NULL, klass);
2678
2679     /* Set timer to try reset at given interval */
2680     SetTimer (klass->d3d.hidden_window, IDT_DEVICE_RESET_TIMER, 500, NULL);
2681   }
2682   UNLOCK_CLASS (NULL, klass);
2683 }
2684
2685 static void
2686 d3d_class_reset_display_device (GstD3DVideoSinkClass * klass)
2687 {
2688   HRESULT hr;
2689
2690   g_return_if_fail (klass != NULL);
2691
2692   LOCK_CLASS (NULL, klass);
2693   hr = IDirect3DDevice9_Reset (klass->d3d.device.d3d_device,
2694       &klass->d3d.device.present_params);
2695   ERROR_CHECK_HR (hr) {
2696     CASE_HR_ERR (D3DERR_DEVICELOST);
2697     CASE_HR_ERR (D3DERR_DEVICEREMOVED);
2698     CASE_HR_ERR (D3DERR_DRIVERINTERNALERROR);
2699     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
2700     CASE_HR_DBG_END (NULL, "Attempt device reset.. failed");
2701     goto end;
2702   }
2703
2704   GST_INFO ("Attempt device reset.. success");
2705
2706   klass->d3d.device_lost = FALSE;
2707   KillTimer (klass->d3d.hidden_window, IDT_DEVICE_RESET_TIMER);
2708
2709   g_list_foreach (klass->d3d.sink_list, (GFunc) d3d_notify_device_reset, NULL);
2710 end:;
2711   UNLOCK_CLASS (NULL, klass);
2712 }
2713
2714 /* Hidden Window Loop Thread */
2715
2716 static LRESULT APIENTRY
2717 D3DHiddenWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2718 {
2719   switch (message) {
2720     case WM_TIMER:
2721       switch (wParam) {
2722         case IDT_DEVICE_RESET_TIMER:
2723           d3d_class_reset_display_device ((GstD3DVideoSinkClass *)
2724               GetWindowLongPtr (hWnd, GWLP_USERDATA));
2725           break;
2726         default:;
2727       }
2728       return 0;
2729     case WM_DESTROY:
2730       PostQuitMessage (0);
2731       return 0;
2732     default:
2733       /* non constants */
2734       if (message == WM_D3DVIDEO_NOTIFY_DEVICE_LOST) {
2735         d3d_class_notify_device_lost_all ((GstD3DVideoSinkClass *)
2736             GetWindowLongPtr (hWnd, GWLP_USERDATA));
2737         return 0;
2738       }
2739   }
2740
2741   return DefWindowProc (hWnd, message, wParam, lParam);
2742 }
2743
2744 static gboolean
2745 d3d_hidden_window_thread (GstD3DVideoSinkClass * klass)
2746 {
2747   WNDCLASS WndClass;
2748   gboolean reged = FALSE;
2749   HWND hWnd = 0;
2750   gboolean ret = FALSE;
2751
2752   g_return_val_if_fail (klass != NULL, FALSE);
2753
2754   memset (&WndClass, 0, sizeof (WNDCLASS));
2755   WndClass.hInstance = GetModuleHandle (NULL);
2756   WndClass.lpszClassName = TEXT ("gstd3dvideo-hidden-window-class");
2757   WndClass.lpfnWndProc = D3DHiddenWndProc;
2758
2759   if (!RegisterClass (&WndClass)) {
2760     GST_ERROR ("Unable to register Direct3D hidden window class");
2761     goto error;
2762   }
2763   reged = TRUE;
2764
2765   hWnd = CreateWindowEx (0,
2766       WndClass.lpszClassName,
2767       TEXT ("GStreamer Direct3D hidden window"),
2768       WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, WndClass.hInstance, klass);
2769
2770   if (hWnd == NULL) {
2771     GST_ERROR ("Failed to create Direct3D hidden window");
2772     goto error;
2773   }
2774
2775   GST_DEBUG ("Direct3D hidden window handle: %p", hWnd);
2776
2777   klass->d3d.hidden_window = hWnd;
2778
2779   /* TODO: Multi-monitor setup? */
2780   if (!d3d_class_display_device_create (klass, D3DADAPTER_DEFAULT)) {
2781     GST_ERROR ("Failed to initiazlize adapter: %u", D3DADAPTER_DEFAULT);
2782     goto error;
2783   }
2784
2785   /* Attach data to window */
2786   SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) klass);
2787
2788   GST_DEBUG ("Entering Direct3D hidden window message loop");
2789
2790   klass->d3d.running = TRUE;
2791
2792   /* Hidden Window Message Loop */
2793   while (1) {
2794     MSG msg;
2795     while (GetMessage (&msg, NULL, 0, 0)) {
2796       TranslateMessage (&msg);
2797       DispatchMessage (&msg);
2798     }
2799     if (msg.message == WM_QUIT || msg.message == WM_CLOSE)
2800       break;
2801   }
2802
2803   klass->d3d.running = FALSE;
2804
2805   GST_DEBUG ("Leaving Direct3D hidden window message loop");
2806
2807   ret = TRUE;
2808
2809 error:
2810   if (!ret)
2811     klass->d3d.error_exit = TRUE;
2812   if (hWnd) {
2813     PostMessage (hWnd, WM_DESTROY, 0, 0);
2814     DestroyWindow (hWnd);
2815     klass->d3d.hidden_window = 0;
2816   }
2817   if (reged)
2818     UnregisterClass (WndClass.lpszClassName, WndClass.hInstance);
2819
2820   return ret;
2821 }