dd93e82ca15f52c018a5d0e87c558184628471e8
[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   ret = TRUE;
1431
1432 end:
1433   UNLOCK_SINK (sink);
1434
1435   return ret;
1436 }
1437
1438 static gboolean
1439 d3d_resize_swap_chain (GstD3DVideoSink * sink)
1440 {
1441   GstD3DVideoSinkClass *klass;
1442   D3DPRESENT_PARAMETERS d3d_pp;
1443   LPDIRECT3DSWAPCHAIN9 swapchain = NULL;
1444   gint w = 0, h = 0, ref_count = 0;
1445   gboolean ret = FALSE;
1446   HRESULT hr;
1447   gboolean need_new = FALSE;
1448   int clip_ret;
1449   HDC handle_hdc;
1450   RECT clip_rectangle;
1451
1452   g_return_val_if_fail (sink != NULL, FALSE);
1453   klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1454   g_return_val_if_fail (klass != NULL, FALSE);
1455
1456   LOCK_SINK (sink);
1457
1458   if (!sink->d3d.renderable || sink->d3d.device_lost) {
1459     UNLOCK_SINK (sink);
1460     return FALSE;
1461   }
1462
1463   LOCK_CLASS (sink, klass);
1464
1465   CHECK_WINDOW_HANDLE (sink, end, FALSE);
1466   CHECK_D3D_DEVICE (klass, sink, end);
1467   CHECK_D3D_SWAPCHAIN (sink, end);
1468
1469   handle_hdc = GetDC (sink->d3d.window_handle);
1470   clip_ret = GetClipBox (handle_hdc, &clip_rectangle);
1471   ReleaseDC (sink->d3d.window_handle, handle_hdc);
1472   if (clip_ret == NULLREGION) {
1473     GST_DEBUG_OBJECT (sink, "Window is hidden, not resizing swapchain");
1474     UNLOCK_CLASS (sink, klass);
1475     UNLOCK_SINK (sink);
1476     return TRUE;
1477   }
1478
1479   d3d_get_hwnd_window_size (sink->d3d.window_handle, &w, &h);
1480   ZeroMemory (&d3d_pp, sizeof (d3d_pp));
1481
1482   /* Get the parameters used to create this swap chain */
1483   hr = IDirect3DSwapChain9_GetPresentParameters (sink->d3d.swapchain, &d3d_pp);
1484   if (hr != D3D_OK) {
1485     GST_ERROR_OBJECT (sink,
1486         "Unable to determine Direct3D present parameters for swap chain");
1487     goto end;
1488   }
1489
1490   /* Reisze needed? */
1491   if (d3d_pp.BackBufferWidth != w || d3d_pp.BackBufferHeight != h)
1492     need_new = TRUE;
1493 #if 0
1494   /* Render rect set or unset? */
1495   if ((d3d_pp.SwapEffect != D3DSWAPEFFECT_COPY && sink->d3d.render_rect) ||
1496       (d3d_pp.SwapEffect != D3DSWAPEFFECT_DISCARD
1497           && sink->d3d.render_rect == NULL)) {
1498     d3d_pp.SwapEffect =
1499         (sink->d3d.render_rect ==
1500         NULL) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
1501     GST_DEBUG_OBJECT (sink, "Setting SwapEffect: %s",
1502         sink->d3d.render_rect ? "COPY" : "DISCARD");
1503     need_new = TRUE;
1504   }
1505 #endif
1506   if (!need_new) {
1507     ret = TRUE;
1508     goto end;
1509   }
1510
1511   GST_DEBUG_OBJECT (sink, "Resizing swapchain %dx%d to %dx%d",
1512       d3d_pp.BackBufferWidth, d3d_pp.BackBufferHeight, w, h);
1513
1514
1515   /* As long as present params windowed == TRUE, width or height
1516    * of 0 will force use of HWND's size.
1517    */
1518   d3d_pp.BackBufferWidth = 0;
1519   d3d_pp.BackBufferHeight = 0;
1520
1521   /* Release current swapchain */
1522   if (sink->d3d.swapchain != NULL) {
1523     ref_count = IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1524     if (ref_count > 0) {
1525       GST_WARNING_OBJECT (sink, "Release swapchain refcount: %d", ref_count);
1526     }
1527     sink->d3d.swapchain = NULL;
1528   }
1529
1530   hr = IDirect3DDevice9_CreateAdditionalSwapChain (klass->d3d.device.d3d_device,
1531       &d3d_pp, &swapchain);
1532   ERROR_CHECK_HR (hr) {
1533     CASE_HR_ERR (D3DERR_NOTAVAILABLE);
1534     CASE_HR_ERR (D3DERR_DEVICELOST);
1535     CASE_HR_ERR (D3DERR_INVALIDCALL);
1536     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1537     CASE_HR_ERR (E_OUTOFMEMORY);
1538     CASE_HR_ERR_END (sink, "Error creating swapchian");
1539     goto end;
1540   }
1541
1542   sink->d3d.swapchain = swapchain;
1543   sink->d3d.overlay_needs_resize = TRUE;
1544   ret = TRUE;
1545
1546 end:
1547   UNLOCK_CLASS (sink, klass);
1548   UNLOCK_SINK (sink);
1549
1550   return ret;
1551 }
1552
1553 static gboolean
1554 d3d_copy_buffer (GstD3DVideoSink * sink, GstBuffer * from, GstBuffer * to)
1555 {
1556   gboolean ret = FALSE;
1557   GstVideoFrame from_frame, to_frame;
1558
1559   memset (&from_frame, 0, sizeof (from_frame));
1560   memset (&to_frame, 0, sizeof (to_frame));
1561
1562   LOCK_SINK (sink);
1563
1564   if (!sink->d3d.renderable || sink->d3d.device_lost)
1565     goto end;
1566
1567   if (!gst_video_frame_map (&from_frame, &sink->info, from, GST_MAP_READ) ||
1568       !gst_video_frame_map (&to_frame, &sink->info, to, GST_MAP_WRITE)) {
1569     GST_ERROR_OBJECT (sink, "NULL GstBuffer");
1570     goto end;
1571   }
1572
1573   switch (sink->format) {
1574     case GST_VIDEO_FORMAT_YUY2:
1575     case GST_VIDEO_FORMAT_UYVY:{
1576       const guint8 *src;
1577       guint8 *dst;
1578       gint dststride, srcstride;
1579       gint i, h, w;
1580
1581       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1582       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1583       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1584       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1585       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1586       w = GST_ROUND_UP_4 (GST_VIDEO_FRAME_WIDTH (&from_frame) * 2);
1587
1588       for (i = 0; i < h; i++) {
1589         memcpy (dst, src, w);
1590         dst += dststride;
1591         src += srcstride;
1592       }
1593
1594       break;
1595     }
1596     case GST_VIDEO_FORMAT_I420:
1597     case GST_VIDEO_FORMAT_YV12:{
1598       const guint8 *src;
1599       guint8 *dst;
1600       gint srcstride, dststride;
1601       gint i, j, h_, w_;
1602
1603       for (i = 0; i < 3; i++) {
1604         src = GST_VIDEO_FRAME_COMP_DATA (&from_frame, i);
1605         dst = GST_VIDEO_FRAME_COMP_DATA (&to_frame, i);
1606         srcstride = GST_VIDEO_FRAME_COMP_STRIDE (&from_frame, i);
1607         dststride = GST_VIDEO_FRAME_COMP_STRIDE (&to_frame, i);
1608         h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
1609         w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
1610
1611         for (j = 0; j < h_; j++) {
1612           memcpy (dst, src, w_);
1613           dst += dststride;
1614           src += srcstride;
1615         }
1616       }
1617
1618       break;
1619     }
1620     case GST_VIDEO_FORMAT_NV12:{
1621       const guint8 *src;
1622       guint8 *dst;
1623       gint srcstride, dststride;
1624       gint i, j, h_, w_;
1625
1626       for (i = 0; i < 2; i++) {
1627         src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, i);
1628         dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, i);
1629         srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, i);
1630         dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, i);
1631         h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
1632         w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
1633
1634         for (j = 0; j < h_; j++) {
1635           memcpy (dst, src, w_ * 2);
1636           dst += dststride;
1637           src += srcstride;
1638         }
1639       }
1640
1641       break;
1642     }
1643     case GST_VIDEO_FORMAT_BGRA:
1644     case GST_VIDEO_FORMAT_RGBA:
1645     case GST_VIDEO_FORMAT_BGRx:
1646     case GST_VIDEO_FORMAT_RGBx:{
1647       const guint8 *src;
1648       guint8 *dst;
1649       gint srcstride, dststride;
1650       gint i, h, w;
1651
1652       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1653       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1654       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1655       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1656       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1657       w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 4;
1658
1659       for (i = 0; i < h; i++) {
1660         memcpy (dst, src, w);
1661         dst += dststride;
1662         src += srcstride;
1663       }
1664
1665       break;
1666     }
1667     case GST_VIDEO_FORMAT_BGR:{
1668       const guint8 *src;
1669       guint8 *dst;
1670       gint srcstride, dststride;
1671       gint i, h, w;
1672
1673       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1674       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1675       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1676       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1677       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1678       w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 3;
1679
1680       for (i = 0; i < h; i++) {
1681         memcpy (dst, src, w);
1682         dst += dststride;
1683         src += srcstride;
1684       }
1685
1686       break;
1687     }
1688     case GST_VIDEO_FORMAT_RGB16:
1689     case GST_VIDEO_FORMAT_RGB15:{
1690       const guint8 *src;
1691       guint8 *dst;
1692       gint srcstride, dststride;
1693       gint i, h, w;
1694
1695       src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1696       dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1697       srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1698       dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1699       h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1700       w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 2;
1701
1702       for (i = 0; i < h; i++) {
1703         memcpy (dst, src, w);
1704         dst += dststride;
1705         src += srcstride;
1706       }
1707
1708       break;
1709     }
1710     default:
1711       goto unhandled_format;
1712   }
1713
1714   ret = TRUE;
1715
1716 end:
1717   if (from_frame.buffer)
1718     gst_video_frame_unmap (&from_frame);
1719   if (to_frame.buffer)
1720     gst_video_frame_unmap (&to_frame);
1721
1722   UNLOCK_SINK (sink);
1723   return ret;
1724
1725 unhandled_format:
1726   GST_ERROR_OBJECT (sink,
1727       "Unhandled format '%s' -> '%s' (should not get here)",
1728       gst_video_format_to_string (sink->format),
1729       d3d_format_to_string (sink->d3d.format));
1730   ret = FALSE;
1731   goto end;
1732 }
1733
1734 static gboolean
1735 d3d_present_swap_chain (GstD3DVideoSink * sink)
1736 {
1737   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1738   LPDIRECT3DSURFACE9 back_buffer = NULL;
1739   gboolean ret = FALSE;
1740   HRESULT hr;
1741   RECT dstr, srcr, *pDestRect = NULL, *pSrcRect = NULL;
1742
1743   LOCK_SINK (sink);
1744
1745   if (!sink->d3d.renderable || sink->d3d.device_lost) {
1746     UNLOCK_SINK (sink);
1747     return FALSE;
1748   }
1749
1750   LOCK_CLASS (sink, klass);
1751
1752   CHECK_WINDOW_HANDLE (sink, end, FALSE);
1753   CHECK_D3D_DEVICE (klass, sink, end);
1754   CHECK_D3D_SWAPCHAIN (sink, end);
1755
1756   /* Set the render target to our swap chain */
1757   hr = IDirect3DSwapChain9_GetBackBuffer (sink->d3d.swapchain, 0,
1758       D3DBACKBUFFER_TYPE_MONO, &back_buffer);
1759   ERROR_CHECK_HR (hr) {
1760     CASE_HR_ERR (D3DERR_INVALIDCALL);
1761     CASE_HR_ERR_END (sink, "IDirect3DSwapChain9_GetBackBuffer");
1762     goto end;
1763   }
1764   hr = IDirect3DDevice9_SetRenderTarget (klass->d3d.device.d3d_device, 0,
1765       back_buffer);
1766   ERROR_CHECK_HR (hr) {
1767     CASE_HR_ERR (D3DERR_INVALIDCALL);
1768     CASE_HR_ERR_END (sink, "IDirect3DDevice9_SetRenderTarget");
1769     goto end;
1770   }
1771   hr = IDirect3DSurface9_Release (back_buffer);
1772   ERROR_CHECK_HR (hr) {
1773     CASE_HR_ERR (D3DERR_INVALIDCALL);
1774     CASE_HR_ERR_END (sink, "IDirect3DSurface9_Release");
1775     goto end;
1776   }
1777
1778   /* Clear the target */
1779   hr = IDirect3DDevice9_Clear (klass->d3d.device.d3d_device, 0, NULL,
1780       D3DCLEAR_TARGET, D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0);
1781   ERROR_CHECK_HR (hr) {
1782     CASE_HR_ERR (D3DERR_INVALIDCALL);
1783     CASE_HR_ERR_END (sink, "IDirect3DDevice9_Clear");
1784     goto end;
1785   }
1786
1787   hr = IDirect3DDevice9_BeginScene (klass->d3d.device.d3d_device);
1788   ERROR_CHECK_HR (hr) {
1789     CASE_HR_ERR (D3DERR_INVALIDCALL);
1790     CASE_HR_ERR_END (sink, "IDirect3DDevice9_BeginScene");
1791     goto end;
1792   }
1793
1794   if (!gst_d3d9_overlay_set_render_state (sink)) {
1795     IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1796     goto end;
1797   }
1798
1799   /* Stretch and blit ops, to copy offscreen surface buffer
1800    * to Display back buffer.
1801    */
1802   if (!d3d_stretch_and_copy (sink, back_buffer) ||
1803       !gst_d3d9_overlay_render (sink)) {
1804     IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1805     goto end;
1806   }
1807
1808   hr = IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1809   ERROR_CHECK_HR (hr) {
1810     CASE_HR_ERR (D3DERR_INVALIDCALL);
1811     CASE_HR_ERR_END (sink, "IDirect3DDevice9_EndScene");
1812     goto end;
1813   }
1814
1815   if (d3d_get_render_rects (sink->d3d.render_rect, &dstr, &srcr)) {
1816     pDestRect = &dstr;
1817     pSrcRect = &srcr;
1818   }
1819
1820   /*
1821    * Swap back and front buffers on video card and present to the user
1822    */
1823   hr = IDirect3DSwapChain9_Present (sink->d3d.swapchain, pSrcRect, pDestRect,
1824       NULL, NULL, 0);
1825   if (hr == D3DERR_DEVICELOST) {
1826     d3d_notify_device_lost (sink);
1827     ret = TRUE;
1828     goto end;
1829   }
1830   ERROR_CHECK_HR (hr) {
1831     CASE_HR_ERR (D3DERR_DEVICELOST);
1832     CASE_HR_ERR (D3DERR_DRIVERINTERNALERROR);
1833     CASE_HR_ERR (D3DERR_INVALIDCALL);
1834     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1835     CASE_HR_ERR (E_OUTOFMEMORY);
1836     CASE_HR_DBG_END (sink, "IDirect3DSwapChain9_Present failure");
1837     goto end;
1838   }
1839
1840   ret = TRUE;
1841
1842 end:
1843   UNLOCK_SINK (sink);
1844   UNLOCK_CLASS (sink, klass);
1845   return ret;
1846 }
1847
1848 static gboolean
1849 d3d_stretch_and_copy (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 back_buffer)
1850 {
1851   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1852   GstVideoRectangle *render_rect = NULL;
1853   RECT r, s;
1854   RECT *r_p = NULL;
1855   HRESULT hr;
1856   gboolean ret = FALSE;
1857
1858   LOCK_SINK (sink);
1859
1860   CHECK_WINDOW_HANDLE (sink, end, FALSE);
1861   CHECK_D3D_DEVICE (klass, sink, end);
1862   CHECK_D3D_SURFACE (sink, end);
1863
1864   render_rect = sink->d3d.render_rect;
1865
1866   if (sink->force_aspect_ratio) {
1867     gint window_width;
1868     gint window_height;
1869     GstVideoRectangle src;
1870     GstVideoRectangle dst;
1871     GstVideoRectangle result;
1872
1873     memset (&dst, 0, sizeof (dst));
1874     memset (&src, 0, sizeof (src));
1875
1876     /* Set via GstXOverlay set_render_rect */
1877     if (render_rect) {
1878       memcpy (&dst, render_rect, sizeof (dst));
1879     } else {
1880       d3d_get_hwnd_window_size (sink->d3d.window_handle, &window_width,
1881           &window_height);
1882       dst.w = window_width;
1883       dst.h = window_height;
1884     }
1885
1886     src.w = GST_VIDEO_SINK_WIDTH (sink);
1887     src.h = GST_VIDEO_SINK_HEIGHT (sink);
1888
1889     gst_video_sink_center_rect (src, dst, &result, TRUE);
1890
1891     r.left = result.x;
1892     r.top = result.y;
1893     r.right = result.x + result.w;
1894     r.bottom = result.y + result.h;
1895     r_p = &r;
1896   } else if (render_rect) {
1897     r.left = 0;
1898     r.top = 0;
1899     r.right = render_rect->w;
1900     r.bottom = render_rect->h;
1901     r_p = &r;
1902   }
1903
1904   s.left = sink->crop_rect.x;
1905   s.top = sink->crop_rect.y;
1906   s.right = sink->crop_rect.x + sink->crop_rect.w;
1907   s.bottom = sink->crop_rect.y + sink->crop_rect.h;
1908
1909   /* TODO: StretchRect returns error if the dest rect is outside
1910    * the backbuffer area. So we need to calc how much of the src
1911    * surface is being scaled / copied to the render rect..
1912    */
1913
1914   hr = IDirect3DDevice9_StretchRect (klass->d3d.device.d3d_device, sink->d3d.surface,   /* Source Surface */
1915       &s,                       /* Source Surface Rect (NULL: Whole) */
1916       back_buffer,              /* Dest Surface */
1917       r_p,                      /* Dest Surface Rect (NULL: Whole) */
1918       klass->d3d.device.filter_type);
1919
1920   if (hr == D3D_OK) {
1921     ret = TRUE;
1922   } else {
1923     GST_ERROR_OBJECT (sink, "Failure calling Direct3DDevice9_StretchRect");
1924   }
1925
1926 end:
1927   UNLOCK_SINK (sink);
1928
1929   return ret;
1930 }
1931
1932 GstFlowReturn
1933 d3d_render_buffer (GstD3DVideoSink * sink, GstBuffer * buf)
1934 {
1935   WindowHandleVisibility handle_visibility = WINDOW_VISIBILITY_ERROR;
1936   int clip_ret;
1937   HDC handle_hdc;
1938   RECT handle_rectangle;
1939   RECT clip_rectangle;
1940
1941   GstFlowReturn ret = GST_FLOW_OK;
1942   GstMemory *mem;
1943   LPDIRECT3DSURFACE9 surface = NULL;
1944   GstVideoCropMeta *crop = NULL;
1945
1946   LOCK_SINK (sink);
1947
1948   if (!sink->d3d.window_handle) {
1949     if (sink->stream_stop_on_close) {
1950       /* Handle window deletion by posting an error on the bus */
1951       GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
1952           ("Output window was closed"), (NULL));
1953       ret = GST_FLOW_ERROR;
1954     }
1955     goto end;
1956   }
1957
1958   if (sink->d3d.device_lost) {
1959     GST_LOG_OBJECT (sink, "Device lost, waiting for reset..");
1960     goto end;
1961   }
1962
1963   /* check for window handle visibility, if hidden skip frame rendering  */
1964
1965   handle_hdc = GetDC (sink->d3d.window_handle);
1966   GetClientRect (sink->d3d.window_handle, &handle_rectangle);
1967   clip_ret = GetClipBox (handle_hdc, &clip_rectangle);
1968   ReleaseDC (sink->d3d.window_handle, handle_hdc);
1969
1970   switch (clip_ret) {
1971     case NULLREGION:
1972       handle_visibility = WINDOW_VISIBILITY_HIDDEN;
1973       break;
1974     case SIMPLEREGION:
1975       if (EqualRect (&clip_rectangle, &handle_rectangle))
1976         handle_visibility = WINDOW_VISIBILITY_FULL;
1977       else
1978         handle_visibility = WINDOW_VISIBILITY_PARTIAL;
1979       break;
1980     case COMPLEXREGION:
1981       handle_visibility = WINDOW_VISIBILITY_PARTIAL;
1982       break;
1983     default:
1984       handle_visibility = WINDOW_VISIBILITY_ERROR;
1985       break;
1986   }
1987
1988   if (handle_visibility == WINDOW_VISIBILITY_HIDDEN) {
1989     GST_DEBUG_OBJECT (sink, "Hidden hwnd, skipping frame rendering...");
1990     goto end;
1991   }
1992
1993   GST_INFO_OBJECT (sink, "%s %" GST_TIME_FORMAT,
1994       (sink->d3d.window_handle != NULL) ? "Render" : "No Win",
1995       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1996
1997   crop = gst_buffer_get_video_crop_meta (buf);
1998   if (crop) {
1999     sink->crop_rect.x = crop->x;
2000     sink->crop_rect.y = crop->y;
2001     sink->crop_rect.w = crop->width;
2002     sink->crop_rect.h = crop->height;
2003   } else {
2004     sink->crop_rect.x = 0;
2005     sink->crop_rect.y = 0;
2006     sink->crop_rect.w = sink->info.width;
2007     sink->crop_rect.h = sink->info.height;
2008   }
2009
2010   /* Resize swapchain if needed */
2011   if (!d3d_resize_swap_chain (sink)) {
2012     ret = GST_FLOW_ERROR;
2013     goto end;
2014   }
2015
2016   if (gst_buffer_n_memory (buf) != 1 ||
2017       (mem = gst_buffer_peek_memory (buf, 0)) == 0 ||
2018       !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
2019     GstBuffer *tmp;
2020     GstBufferPoolAcquireParams params = { 0, };
2021
2022     if (!sink->fallback_pool
2023         || !gst_buffer_pool_set_active (sink->fallback_pool, TRUE)) {
2024       ret = GST_FLOW_NOT_NEGOTIATED;
2025       goto end;
2026     }
2027
2028     /* take a buffer from our pool, if there is no buffer in the pool something
2029      * is seriously wrong, waiting for the pool here might deadlock when we try
2030      * to go to PAUSED because we never flush the pool. */
2031     params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
2032     ret = gst_buffer_pool_acquire_buffer (sink->fallback_pool, &tmp, &params);
2033     if (ret != GST_FLOW_OK)
2034       goto end;
2035
2036     if (sink->fallback_buffer) {
2037       gst_buffer_unref (sink->fallback_buffer);
2038       sink->fallback_buffer = NULL;
2039     }
2040
2041     mem = gst_buffer_peek_memory (tmp, 0);
2042     if (!mem || !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
2043       ret = GST_FLOW_ERROR;
2044       gst_buffer_unref (tmp);
2045       goto end;
2046     }
2047     d3d_copy_buffer (sink, buf, tmp);
2048     buf = tmp;
2049
2050     surface = ((GstD3DSurfaceMemory *) mem)->surface;
2051
2052     /* Need to keep an additional ref until the next buffer
2053      * to make sure it isn't reused until then */
2054     sink->fallback_buffer = buf;
2055   } else {
2056     mem = gst_buffer_peek_memory (buf, 0);
2057     surface = ((GstD3DSurfaceMemory *) mem)->surface;
2058
2059     if (sink->fallback_buffer) {
2060       gst_buffer_unref (sink->fallback_buffer);
2061       sink->fallback_buffer = NULL;
2062     }
2063   }
2064
2065   if (sink->d3d.surface)
2066     IDirect3DSurface9_Release (sink->d3d.surface);
2067   IDirect3DSurface9_AddRef (surface);
2068   sink->d3d.surface = surface;
2069
2070   if (!d3d_present_swap_chain (sink)) {
2071     ret = GST_FLOW_ERROR;
2072     goto end;
2073   }
2074
2075 end:
2076   UNLOCK_SINK (sink);
2077   return ret;
2078 }
2079
2080
2081 /* D3D Window Proc Functions */
2082
2083 static LRESULT APIENTRY
2084 d3d_wnd_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2085 {
2086   GstD3DVideoSink *sink =
2087       (GstD3DVideoSink *) GetProp (hWnd, TEXT ("GstD3DVideoSink"));
2088   WNDPROC proc;
2089   LRESULT ret = 0;
2090
2091   /* d3dvideosink object might not available yet.
2092    * The thread for message queue starts earlier than SetProp... */
2093   if (!sink)
2094     return DefWindowProc (hWnd, message, wParam, lParam);
2095
2096   LOCK_SINK (sink);
2097   proc = sink->d3d.orig_wnd_proc;
2098   UNLOCK_SINK (sink);
2099
2100   switch (message) {
2101     case WM_ERASEBKGND:
2102       return TRUE;
2103     case WM_PAINT:{
2104       if (proc)
2105         ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2106       /* Call this afterwards to ensure that our paint happens last */
2107       d3d_present_swap_chain (sink);
2108       goto end;
2109     }
2110     case WM_SIZE:{
2111       if (proc)
2112         ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2113
2114       /* Don't resize if the window is being minimized. Recreating the
2115        * swap chain will fail if the window is minimized
2116        */
2117       if (wParam != SIZE_MINIMIZED)
2118         d3d_resize_swap_chain (sink);
2119       goto end;
2120     }
2121     case WM_KEYDOWN:
2122     case WM_KEYUP:
2123       if (sink->enable_navigation_events) {
2124         gunichar2 wcrep[128];
2125         if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) {
2126           gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
2127           if (utfrep) {
2128             if (message == WM_KEYDOWN)
2129               gst_navigation_send_key_event (GST_NAVIGATION (sink), "key-press",
2130                   utfrep);
2131             else if (message == WM_KEYUP)
2132               gst_navigation_send_key_event (GST_NAVIGATION (sink),
2133                   "key-release", utfrep);
2134             g_free (utfrep);
2135           }
2136         }
2137       }
2138       break;
2139     case WM_LBUTTONDOWN:
2140     case WM_LBUTTONUP:
2141     case WM_RBUTTONDOWN:
2142     case WM_RBUTTONUP:
2143     case WM_MBUTTONDOWN:
2144     case WM_MBUTTONUP:
2145     case WM_MOUSEMOVE:{
2146       gdouble x = 0, y = 0;
2147       if (sink->enable_navigation_events
2148           && d3d_get_render_coordinates (sink, LOWORD (lParam), HIWORD (lParam),
2149               &x, &y)) {
2150         gint button;
2151         const gchar *action = NULL;
2152         switch (message) {
2153           case WM_MOUSEMOVE:
2154             button = 0;
2155             action = "mouse-move";
2156             break;
2157           case WM_LBUTTONDOWN:
2158             button = 1;
2159             action = "mouse-button-press";
2160             break;
2161           case WM_LBUTTONUP:
2162             button = 1;
2163             action = "mouse-button-release";
2164             break;
2165           case WM_RBUTTONDOWN:
2166             button = 2;
2167             action = "mouse-button-press";
2168             break;
2169           case WM_RBUTTONUP:
2170             button = 2;
2171             action = "mouse-button-release";
2172             break;
2173           case WM_MBUTTONDOWN:
2174             button = 3;
2175             action = "mouse-button-press";
2176             break;
2177           case WM_MBUTTONUP:
2178             button = 3;
2179             action = "mouse-button-release";
2180             break;
2181           default:
2182             break;
2183         }
2184         if (action) {
2185           /* GST_DEBUG_OBJECT(sink, "%s: %lfx%lf", action, x, y); */
2186           gst_navigation_send_mouse_event (GST_NAVIGATION (sink), action,
2187               button, x, y);
2188         }
2189       }
2190       break;
2191     }
2192     case WM_CLOSE:
2193       d3d_set_window_handle (sink, 0, FALSE);
2194       break;
2195     default:
2196       break;
2197   }
2198
2199   if (proc)
2200     ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2201   else
2202     ret = DefWindowProc (hWnd, message, wParam, lParam);
2203
2204 end:
2205   return ret;
2206 }
2207
2208 /* Internal Window */
2209
2210 static LRESULT APIENTRY
2211 d3d_wnd_proc_internal (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2212 {
2213   switch (message) {
2214     case WM_DESTROY:
2215       GST_DEBUG ("Internal window: WM_DESTROY");
2216       /* Tell the internal window thread to shut down */
2217       PostQuitMessage (0);
2218       GST_DEBUG ("Posted quit..");
2219       break;
2220   }
2221
2222   return DefWindowProc (hWnd, message, wParam, lParam);
2223 }
2224
2225 static HWND
2226 _d3d_create_internal_window (GstD3DVideoSink * sink)
2227 {
2228   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2229   int width, height;
2230   int offx, offy;
2231   DWORD exstyle, style;
2232   HWND video_window;
2233   RECT rect;
2234   int screenwidth;
2235   int screenheight;
2236
2237   /*
2238    * GST_VIDEO_SINK_WIDTH() is the aspect-ratio-corrected size of the video.
2239    * GetSystemMetrics() returns the width of the dialog's border (doubled
2240    * b/c of left and right borders).
2241    */
2242   width = GST_VIDEO_SINK_WIDTH (sink) + GetSystemMetrics (SM_CXSIZEFRAME) * 2;
2243   height =
2244       GST_VIDEO_SINK_HEIGHT (sink) + GetSystemMetrics (SM_CYCAPTION) +
2245       (GetSystemMetrics (SM_CYSIZEFRAME) * 2);
2246
2247   SystemParametersInfo (SPI_GETWORKAREA, 0, &rect, 0);
2248   screenwidth = rect.right - rect.left;
2249   screenheight = rect.bottom - rect.top;
2250   offx = rect.left;
2251   offy = rect.top;
2252
2253   /* Make it fit into the screen without changing the aspect ratio. */
2254   if (width > screenwidth) {
2255     double ratio = (double) screenwidth / (double) width;
2256     width = screenwidth;
2257     height = (int) (height * ratio);
2258   }
2259
2260   if (height > screenheight) {
2261     double ratio = (double) screenheight / (double) height;
2262     height = screenheight;
2263     width = (int) (width * ratio);
2264   }
2265
2266   style = WS_OVERLAPPEDWINDOW;  /* Normal top-level window */
2267   exstyle = 0;
2268   video_window = CreateWindowEx (exstyle,
2269       klass->d3d.wnd_class.lpszClassName,
2270       TEXT ("GStreamer D3D video sink (internal window)"),
2271       style, offx, offy, width, height,
2272       NULL, NULL, klass->d3d.wnd_class.hInstance, sink);
2273
2274   if (video_window == NULL) {
2275     GST_ERROR_OBJECT (sink, "Failed to create internal window: %lu",
2276         GetLastError ());
2277     return NULL;
2278   }
2279
2280   /* Now show the window, as appropriate */
2281   ShowWindow (video_window, SW_SHOWNORMAL);
2282
2283   /* Trigger the initial paint of the window */
2284   UpdateWindow (video_window);
2285
2286   return video_window;
2287 }
2288
2289 typedef struct
2290 {
2291   GstD3DVideoSink *sink;
2292   gboolean error;
2293   HWND hWnd;
2294   GMutex lock;
2295   GCond cond;
2296 } D3DInternalWindowDat;
2297
2298 static gpointer
2299 d3d_internal_window_thread (D3DInternalWindowDat * dat)
2300 {
2301   GstD3DVideoSink *sink;
2302   HWND hWnd;
2303   MSG msg;
2304
2305   g_return_val_if_fail (dat != NULL, NULL);
2306
2307   sink = dat->sink;
2308   GST_DEBUG_OBJECT (sink, "Entering internal window thread: %p",
2309       g_thread_self ());
2310
2311   /* Create internal window */
2312   g_mutex_lock (&dat->lock);
2313   hWnd = _d3d_create_internal_window (sink);
2314   if (!hWnd) {
2315     GST_ERROR_OBJECT (sink, "Failed to create internal window");
2316     dat->error = TRUE;
2317     g_cond_signal (&dat->cond);
2318     g_mutex_unlock (&dat->lock);
2319     goto end;
2320   }
2321
2322   dat->hWnd = hWnd;
2323   g_cond_signal (&dat->cond);
2324   g_mutex_unlock (&dat->lock);
2325
2326   /*
2327    * Internal window message loop
2328    */
2329
2330   while (GetMessage (&msg, NULL, 0, 0)) {
2331     if (msg.message == WM_QUIT_THREAD)
2332       break;
2333     TranslateMessage (&msg);
2334     DispatchMessage (&msg);
2335   }
2336
2337 end:
2338   GST_DEBUG_OBJECT (sink, "Exiting internal window thread: %p",
2339       g_thread_self ());
2340   return NULL;
2341 }
2342
2343 static HWND
2344 d3d_create_internal_window (GstD3DVideoSink * sink)
2345 {
2346   GThread *thread;
2347   D3DInternalWindowDat dat;
2348   gint64 end_time;
2349   gboolean timeout = FALSE;
2350
2351   dat.sink = sink;
2352   dat.error = FALSE;
2353   dat.hWnd = 0;
2354   g_mutex_init (&dat.lock);
2355   g_cond_init (&dat.cond);
2356
2357   g_mutex_lock (&dat.lock);
2358   thread =
2359       g_thread_new ("d3dvideosink-window-thread",
2360       (GThreadFunc) d3d_internal_window_thread, &dat);
2361   if (!thread) {
2362     g_mutex_unlock (&dat.lock);
2363     GST_ERROR ("Failed to created internal window thread");
2364     goto clear;
2365   }
2366
2367   sink->internal_window_thread = thread;
2368
2369   end_time = g_get_monotonic_time () + 10 * G_TIME_SPAN_SECOND;
2370   /* Wait 10 seconds for window proc loop to start up */
2371   while (!dat.error && !dat.hWnd) {
2372     if (!g_cond_wait_until (&dat.cond, &dat.lock, end_time)) {
2373       timeout = TRUE;
2374       break;
2375     }
2376   }
2377   g_mutex_unlock (&dat.lock);
2378
2379   GST_DEBUG_OBJECT (sink, "Created window: %p (error: %d, timeout: %d)",
2380       dat.hWnd, dat.error, timeout);
2381
2382 clear:
2383   {
2384     g_mutex_clear (&dat.lock);
2385     g_cond_clear (&dat.cond);
2386   }
2387
2388   return dat.hWnd;
2389 }
2390
2391 /* D3D Video Class Methods */
2392
2393 gboolean
2394 d3d_class_init (GstD3DVideoSink * sink)
2395 {
2396   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2397   gulong timeout_interval = 10000;      /* 10 ms interval */
2398   gulong intervals = (10000000 / timeout_interval);     /* 10 secs */
2399   gboolean ret = FALSE;
2400   gulong i;
2401
2402   g_return_val_if_fail (klass != NULL, FALSE);
2403
2404   LOCK_CLASS (sink, klass);
2405
2406   klass->d3d.refs += 1;
2407   GST_DEBUG ("D3D class init [refs:%u]", klass->d3d.refs);
2408   klass->d3d.sink_list = g_list_append (klass->d3d.sink_list, sink);
2409
2410   if (klass->d3d.refs > 1)
2411     goto end;
2412
2413   WM_D3DVIDEO_NOTIFY_DEVICE_LOST =
2414       RegisterWindowMessage ("WM_D3DVIDEO_NOTIFY_DEVICE_LOST");
2415
2416   klass->d3d.d3d = Direct3DCreate9 (D3D_SDK_VERSION);
2417   if (!klass->d3d.d3d) {
2418     GST_ERROR ("Unable to create Direct3D interface");
2419     goto error;
2420   }
2421
2422   /* Register Window Class for internal Windows */
2423   memset (&klass->d3d.wnd_class, 0, sizeof (WNDCLASS));
2424   klass->d3d.wnd_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
2425   klass->d3d.wnd_class.hInstance = GetModuleHandle (NULL);
2426   klass->d3d.wnd_class.lpszClassName = TEXT ("GstD3DVideoSinkInternalWindow");
2427   klass->d3d.wnd_class.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
2428   klass->d3d.wnd_class.hCursor = LoadCursor (NULL, IDC_ARROW);
2429   klass->d3d.wnd_class.hIcon = LoadIcon (NULL, IDI_APPLICATION);
2430   klass->d3d.wnd_class.cbClsExtra = 0;
2431   klass->d3d.wnd_class.cbWndExtra = 0;
2432   klass->d3d.wnd_class.lpfnWndProc = d3d_wnd_proc_internal;
2433
2434   if (RegisterClass (&klass->d3d.wnd_class) == 0) {
2435     GST_ERROR ("Failed to register window class: %lu", GetLastError ());
2436     goto error;
2437   }
2438
2439   klass->d3d.running = FALSE;
2440   klass->d3d.error_exit = FALSE;
2441   UNLOCK_CLASS (sink, klass);
2442   klass->d3d.thread =
2443       g_thread_new ("d3dvideosink-window-thread",
2444       (GThreadFunc) d3d_hidden_window_thread, klass);
2445   LOCK_CLASS (sink, klass);
2446
2447   if (!klass->d3d.thread) {
2448     GST_ERROR ("Failed to created hidden window thread");
2449     goto error;
2450   }
2451
2452   UNLOCK_CLASS (sink, klass);
2453   /* Wait 10 seconds for window proc loop to start up */
2454   for (i = 0; klass->d3d.running == FALSE && i < intervals; i++) {
2455     g_usleep (timeout_interval);
2456   }
2457   LOCK_CLASS (sink, klass);
2458
2459   if (klass->d3d.error_exit)
2460     goto error;
2461
2462   if (!klass->d3d.running) {
2463     GST_ERROR ("Waited %lu ms, window proc loop has not started",
2464         (timeout_interval * intervals) / 1000);
2465     goto error;
2466   }
2467
2468   GST_DEBUG ("Hidden window message loop is running..");
2469
2470 end:
2471   ret = TRUE;
2472 error:
2473   UNLOCK_CLASS (sink, klass);
2474
2475   if (!ret)
2476     d3d_class_destroy (sink);
2477
2478   return ret;
2479 }
2480
2481 void
2482 d3d_class_destroy (GstD3DVideoSink * sink)
2483 {
2484   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2485
2486   g_return_if_fail (klass != NULL);
2487
2488   LOCK_CLASS (sink, klass);
2489
2490   klass->d3d.refs -= 1;
2491
2492   GST_DEBUG ("D3D class destroy [refs:%u]", klass->d3d.refs);
2493
2494   klass->d3d.sink_list = g_list_remove (klass->d3d.sink_list, sink);
2495
2496   if (klass->d3d.refs >= 1)
2497     goto end;
2498
2499   UNLOCK_CLASS (sink, klass);
2500
2501   if (klass->d3d.running) {
2502     GST_DEBUG ("Shutting down window proc thread, waiting to join..");
2503     PostMessage (klass->d3d.hidden_window, WM_QUIT, 0, 0);
2504     g_thread_join (klass->d3d.thread);
2505     GST_DEBUG ("Joined..");
2506   }
2507
2508   LOCK_CLASS (sink, klass);
2509
2510   if (klass->d3d.d3d) {
2511     int ref_count;
2512     ref_count = IDirect3D9_Release (klass->d3d.d3d);
2513     GST_DEBUG ("Direct3D object released. Reference count: %d", ref_count);
2514   }
2515
2516   UnregisterClass (klass->d3d.wnd_class.lpszClassName,
2517       klass->d3d.wnd_class.hInstance);
2518
2519   memset (&klass->d3d, 0, sizeof (GstD3DDataClass));
2520
2521 end:
2522   UNLOCK_CLASS (sink, klass);
2523 }
2524
2525 static gboolean
2526 d3d_class_display_device_create (GstD3DVideoSinkClass * klass, UINT adapter)
2527 {
2528   LPDIRECT3D9 d3d;
2529   GstD3DDisplayDevice *device;
2530   HWND hwnd;
2531   D3DCAPS9 caps;
2532   D3DDISPLAYMODE disp_mode;
2533   DWORD create_mask = 0;
2534   HRESULT hr;
2535   gboolean ret = FALSE;
2536
2537   g_return_val_if_fail (klass != NULL, FALSE);
2538
2539   GST_DEBUG (" ");
2540
2541   LOCK_CLASS (NULL, klass);
2542
2543   d3d = klass->d3d.d3d;
2544   device = &klass->d3d.device;
2545   hwnd = klass->d3d.hidden_window;
2546
2547   memset (&caps, 0, sizeof (caps));
2548   memset (&disp_mode, 0, sizeof (disp_mode));
2549   memset (&device->present_params, 0, sizeof (device->present_params));
2550
2551   device->adapter = adapter;
2552
2553   if (IDirect3D9_GetAdapterDisplayMode (d3d, adapter, &disp_mode) != D3D_OK) {
2554     GST_ERROR ("Unable to request adapter[%u] display mode", adapter);
2555     goto error;
2556   }
2557
2558   if (IDirect3D9_GetDeviceCaps (d3d, adapter, D3DDEVTYPE_HAL, &caps) != D3D_OK) {
2559     GST_ERROR ("Unable to request adapter[%u] device caps", adapter);
2560     goto error;
2561   }
2562
2563   /* Ask DirectX to please not clobber the FPU state when making DirectX
2564    * API calls. This can cause libraries such as cairo to misbehave in
2565    * certain scenarios.
2566    */
2567   create_mask = 0 | D3DCREATE_FPU_PRESERVE;
2568
2569   /* Make sure that device access is threadsafe */
2570   create_mask |= D3DCREATE_MULTITHREADED;
2571
2572   /* Determine vertex processing capabilities. Some cards have issues
2573    * using software vertex processing. Courtesy:
2574    * http://www.chadvernon.com/blog/resources/directx9/improved-direct3d-initialization/
2575    */
2576   if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ==
2577       D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
2578     create_mask |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
2579     /* if ((d3dcaps.DevCaps & D3DDEVCAPS_PUREDEVICE) == D3DDEVCAPS_PUREDEVICE) */
2580     /*  d3dcreate |= D3DCREATE_PUREDEVICE; */
2581   } else {
2582     create_mask |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
2583   }
2584
2585   /* Check the filter type. */
2586   if ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) ==
2587       D3DPTFILTERCAPS_MINFLINEAR
2588       || (caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) ==
2589       D3DPTFILTERCAPS_MAGFLINEAR) {
2590     device->filter_type = D3DTEXF_LINEAR;
2591   } else {
2592     device->filter_type = D3DTEXF_NONE;
2593   }
2594
2595   /* Setup the display mode format. */
2596   device->format = disp_mode.Format;
2597
2598   /* present_params.Flags = D3DPRESENTFLAG_VIDEO; */
2599   device->present_params.Windowed = TRUE;
2600   device->present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
2601   device->present_params.BackBufferCount = 1;
2602   device->present_params.BackBufferFormat = device->format;
2603   device->present_params.BackBufferWidth = 1;
2604   device->present_params.BackBufferHeight = 1;
2605   device->present_params.MultiSampleType = D3DMULTISAMPLE_NONE;
2606   device->present_params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;    /* D3DPRESENT_INTERVAL_IMMEDIATE; */
2607
2608   GST_DEBUG ("Creating Direct3D device for hidden window %p", hwnd);
2609
2610   if ((hr = IDirect3D9_CreateDevice (d3d, adapter, D3DDEVTYPE_HAL, hwnd,
2611               create_mask, &device->present_params,
2612               &device->d3d_device)) != D3D_OK) {
2613     GST_ERROR ("Unable to create Direct3D device. Result: %ld (0x%lx)", hr, hr);
2614     goto error;
2615   }
2616
2617   GST_DEBUG ("Display Device format: %s",
2618       d3d_format_to_string (disp_mode.Format));
2619
2620   ret = TRUE;
2621   goto end;
2622 error:
2623   memset (device, 0, sizeof (GstD3DDisplayDevice));
2624 end:
2625   UNLOCK_CLASS (NULL, klass);
2626
2627   return ret;
2628 }
2629
2630 static void
2631 d3d_class_display_device_destroy (GstD3DVideoSinkClass * klass)
2632 {
2633   g_return_if_fail (klass != NULL);
2634
2635   LOCK_CLASS (NULL, klass);
2636   if (klass->d3d.device.d3d_device) {
2637     int ref_count;
2638     ref_count = IDirect3DDevice9_Release (klass->d3d.device.d3d_device);
2639     GST_DEBUG ("Direct3D device [adapter:%u] released. Reference count: %d",
2640         klass->d3d.device.adapter, ref_count);
2641   }
2642   memset (&klass->d3d.device, 0, sizeof (GstD3DDisplayDevice));
2643   UNLOCK_CLASS (NULL, klass);
2644 }
2645
2646 static void
2647 d3d_class_notify_device_lost (GstD3DVideoSink * sink)
2648 {
2649   GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2650   PostMessage (klass->d3d.hidden_window, WM_D3DVIDEO_NOTIFY_DEVICE_LOST, 0, 0);
2651 }
2652
2653 static void
2654 d3d_class_notify_device_lost_all (GstD3DVideoSinkClass * klass)
2655 {
2656   g_return_if_fail (klass != NULL);
2657
2658   LOCK_CLASS (NULL, klass);
2659   if (!klass->d3d.device_lost) {
2660     GList *lst, *clst;
2661     klass->d3d.device_lost = TRUE;
2662
2663     GST_DEBUG ("Notifying all instances of device loss");
2664
2665     clst = g_list_copy (klass->d3d.sink_list);
2666     UNLOCK_CLASS (NULL, klass);
2667
2668     for (lst = clst; lst != NULL; lst = lst->next) {
2669       GstD3DVideoSink *sink = (GstD3DVideoSink *) lst->data;
2670       if (!sink)
2671         continue;
2672       d3d_notify_device_lost (sink);
2673     }
2674     g_list_free (clst);
2675     LOCK_CLASS (NULL, klass);
2676
2677     /* Set timer to try reset at given interval */
2678     SetTimer (klass->d3d.hidden_window, IDT_DEVICE_RESET_TIMER, 500, NULL);
2679   }
2680   UNLOCK_CLASS (NULL, klass);
2681 }
2682
2683 static void
2684 d3d_class_reset_display_device (GstD3DVideoSinkClass * klass)
2685 {
2686   HRESULT hr;
2687
2688   g_return_if_fail (klass != NULL);
2689
2690   LOCK_CLASS (NULL, klass);
2691   hr = IDirect3DDevice9_Reset (klass->d3d.device.d3d_device,
2692       &klass->d3d.device.present_params);
2693   ERROR_CHECK_HR (hr) {
2694     CASE_HR_ERR (D3DERR_DEVICELOST);
2695     CASE_HR_ERR (D3DERR_DEVICEREMOVED);
2696     CASE_HR_ERR (D3DERR_DRIVERINTERNALERROR);
2697     CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
2698     CASE_HR_DBG_END (NULL, "Attempt device reset.. failed");
2699     goto end;
2700   }
2701
2702   GST_INFO ("Attempt device reset.. success");
2703
2704   klass->d3d.device_lost = FALSE;
2705   KillTimer (klass->d3d.hidden_window, IDT_DEVICE_RESET_TIMER);
2706
2707   g_list_foreach (klass->d3d.sink_list, (GFunc) d3d_notify_device_reset, NULL);
2708 end:;
2709   UNLOCK_CLASS (NULL, klass);
2710 }
2711
2712 /* Hidden Window Loop Thread */
2713
2714 static LRESULT APIENTRY
2715 D3DHiddenWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2716 {
2717   switch (message) {
2718     case WM_TIMER:
2719       switch (wParam) {
2720         case IDT_DEVICE_RESET_TIMER:
2721           d3d_class_reset_display_device ((GstD3DVideoSinkClass *)
2722               GetWindowLongPtr (hWnd, GWLP_USERDATA));
2723           break;
2724         default:;
2725       }
2726       return 0;
2727     case WM_DESTROY:
2728       PostQuitMessage (0);
2729       return 0;
2730     default:
2731       /* non constants */
2732       if (message == WM_D3DVIDEO_NOTIFY_DEVICE_LOST) {
2733         d3d_class_notify_device_lost_all ((GstD3DVideoSinkClass *)
2734             GetWindowLongPtr (hWnd, GWLP_USERDATA));
2735         return 0;
2736       }
2737   }
2738
2739   return DefWindowProc (hWnd, message, wParam, lParam);
2740 }
2741
2742 static gboolean
2743 d3d_hidden_window_thread (GstD3DVideoSinkClass * klass)
2744 {
2745   WNDCLASS WndClass;
2746   gboolean reged = FALSE;
2747   HWND hWnd = 0;
2748   gboolean ret = FALSE;
2749
2750   g_return_val_if_fail (klass != NULL, FALSE);
2751
2752   memset (&WndClass, 0, sizeof (WNDCLASS));
2753   WndClass.hInstance = GetModuleHandle (NULL);
2754   WndClass.lpszClassName = TEXT ("gstd3dvideo-hidden-window-class");
2755   WndClass.lpfnWndProc = D3DHiddenWndProc;
2756
2757   if (!RegisterClass (&WndClass)) {
2758     GST_ERROR ("Unable to register Direct3D hidden window class");
2759     goto error;
2760   }
2761   reged = TRUE;
2762
2763   hWnd = CreateWindowEx (0,
2764       WndClass.lpszClassName,
2765       TEXT ("GStreamer Direct3D hidden window"),
2766       WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, WndClass.hInstance, klass);
2767
2768   if (hWnd == NULL) {
2769     GST_ERROR ("Failed to create Direct3D hidden window");
2770     goto error;
2771   }
2772
2773   GST_DEBUG ("Direct3D hidden window handle: %p", hWnd);
2774
2775   klass->d3d.hidden_window = hWnd;
2776
2777   /* TODO: Multi-monitor setup? */
2778   if (!d3d_class_display_device_create (klass, D3DADAPTER_DEFAULT)) {
2779     GST_ERROR ("Failed to initiazlize adapter: %u", D3DADAPTER_DEFAULT);
2780     goto error;
2781   }
2782
2783   /* Attach data to window */
2784   SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) klass);
2785
2786   GST_DEBUG ("Entering Direct3D hidden window message loop");
2787
2788   klass->d3d.running = TRUE;
2789
2790   /* Hidden Window Message Loop */
2791   while (1) {
2792     MSG msg;
2793     while (GetMessage (&msg, NULL, 0, 0)) {
2794       TranslateMessage (&msg);
2795       DispatchMessage (&msg);
2796     }
2797     if (msg.message == WM_QUIT || msg.message == WM_CLOSE)
2798       break;
2799   }
2800
2801   klass->d3d.running = FALSE;
2802
2803   GST_DEBUG ("Leaving Direct3D hidden window message loop");
2804
2805   ret = TRUE;
2806
2807 error:
2808   if (!ret)
2809     klass->d3d.error_exit = TRUE;
2810   if (hWnd) {
2811     PostMessage (hWnd, WM_DESTROY, 0, 0);
2812     DestroyWindow (hWnd);
2813     klass->d3d.hidden_window = 0;
2814   }
2815   if (reged)
2816     UnregisterClass (WndClass.lpszClassName, WndClass.hInstance);
2817   d3d_class_display_device_destroy (klass);
2818
2819   return ret;
2820 }