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