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