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