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>
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.
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.
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.
26 #include "d3dvideosink.h"
27 #include "d3dhelpers.h"
28 #include "gstd3d9overlay.h"
34 WINDOW_VISIBILITY_FULL = 1,
35 WINDOW_VISIBILITY_PARTIAL = 2,
36 WINDOW_VISIBILITY_HIDDEN = 3,
37 WINDOW_VISIBILITY_ERROR = 4
38 } WindowHandleVisibility;
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);
55 static void d3d_class_notify_device_lost (GstD3DVideoSink * sink);
57 static void d3d_class_display_device_destroy (GstD3DVideoSinkClass * klass);
58 static gboolean d3d_class_display_device_create (GstD3DVideoSinkClass * klass,
61 static LRESULT APIENTRY d3d_wnd_proc_internal (HWND hWnd, UINT message,
62 WPARAM wParam, LPARAM lParam);
63 static LRESULT APIENTRY d3d_wnd_proc (HWND hWnd, UINT message, WPARAM wParam,
67 GST_DEBUG_CATEGORY_EXTERN (gst_d3dvideosink_debug);
68 #define GST_CAT_DEFAULT gst_d3dvideosink_debug
70 static gint WM_D3DVIDEO_NOTIFY_DEVICE_LOST = 0;
71 #define IDT_DEVICE_RESET_TIMER 0
73 #define WM_QUIT_THREAD WM_USER+0
77 #define ERROR_CHECK_HR(hr) \
79 const gchar * str_err=NULL, *t1=NULL; \
82 #define CASE_HR_ERR(hr_err) \
83 case hr_err: str_err = #hr_err; break;
84 #define CASE_HR_DBG_ERR_END(sink, gst_err_msg, level) \
87 sprintf(tmp, "HR-SEV:%u HR-FAC:%u HR-CODE:%u", (guint)HRESULT_SEVERITY(hr), (guint)HRESULT_FACILITY(hr), (guint)HRESULT_CODE(hr)); \
90 GST_CAT_LEVEL_LOG(GST_CAT_DEFAULT, level, sink, "%s HRESULT: %s", t1?t1:"", str_err);
91 #define CASE_HR_ERR_END(sink, gst_err_msg) \
92 CASE_HR_DBG_ERR_END(sink, gst_err_msg, GST_LEVEL_ERROR)
93 #define CASE_HR_DBG_END(sink, gst_err_msg) \
94 CASE_HR_DBG_ERR_END(sink, gst_err_msg, GST_LEVEL_DEBUG)
96 #define CHECK_REF_COUNT(klass, sink, goto_label) \
97 if(!klass->d3d.refs) { \
98 GST_ERROR_OBJECT(sink, "Direct3D object ref count = 0"); \
101 #define CHECK_D3D_DEVICE(klass, sink, goto_label) \
102 if(!klass->d3d.d3d || !klass->d3d.device.d3d_device) { \
103 GST_ERROR_OBJECT(sink, "Direct3D device or object does not exist"); \
106 #define CHECK_D3D_SWAPCHAIN(sink, goto_label) \
107 if(!sink->d3d.swapchain) { \
108 GST_ERROR_OBJECT(sink, "Direct3D swap chain does not exist"); \
111 #define CHECK_D3D_SURFACE(sink, goto_label) \
112 if(!sink->d3d.surface) { \
113 GST_ERROR_OBJECT(sink, "NULL D3D offscreen surface"); \
116 #define CHECK_WINDOW_HANDLE(sink, goto_label, is_error) \
117 if(!sink->d3d.window_handle) { \
118 GST_CAT_LEVEL_LOG(GST_CAT_DEFAULT, \
119 (is_error?GST_LEVEL_ERROR:GST_LEVEL_DEBUG), \
120 sink, "No window handle is set"); \
125 #define D3DFMT_YV12 MAKEFOURCC ('Y', 'V', '1', '2')
128 #define D3DFMT_NV12 MAKEFOURCC ('N', 'V', '1', '2')
133 #define CASE(x) case x: return #x;
135 d3d_format_to_string (D3DFORMAT format)
137 /* Self defined up above */
138 if (format == D3DFMT_YV12)
139 return "D3DFMT_YV12";
140 else if (format == D3DFMT_NV12)
141 return "D3DFMT_NV12";
145 CASE (D3DFMT_UNKNOWN);
146 CASE (D3DFMT_X8R8G8B8);
148 CASE (D3DFMT_A8R8G8B8);
150 CASE (D3DFMT_R8G8B8);
151 CASE (D3DFMT_R5G6B5);
152 CASE (D3DFMT_X1R5G5B5);
153 CASE (D3DFMT_A1R5G5B5);
154 CASE (D3DFMT_A4R4G4B4);
155 CASE (D3DFMT_R3G3B2);
157 CASE (D3DFMT_A8R3G3B2);
158 CASE (D3DFMT_X4R4G4B4);
159 CASE (D3DFMT_A2B10G10R10);
160 CASE (D3DFMT_A8B8G8R8);
161 CASE (D3DFMT_X8B8G8R8);
162 CASE (D3DFMT_G16R16);
163 CASE (D3DFMT_A2R10G10B10);
164 CASE (D3DFMT_A16B16G16R16);
171 CASE (D3DFMT_L6V5U5);
172 CASE (D3DFMT_X8L8V8U8);
173 CASE (D3DFMT_Q8W8V8U8);
174 CASE (D3DFMT_V16U16);
175 CASE (D3DFMT_A2W10V10U10);
181 CASE (D3DFMT_MULTI2_ARGB8);
182 CASE (D3DFMT_G8R8_G8B8);
183 CASE (D3DFMT_R8G8_B8G8);
184 CASE (D3DFMT_D16_LOCKABLE);
189 CASE (D3DFMT_D24X4S4);
192 CASE (D3DFMT_D32F_LOCKABLE);
193 CASE (D3DFMT_D24FS8);
194 CASE (D3DFMT_VERTEXDATA);
195 CASE (D3DFMT_INDEX16);
196 CASE (D3DFMT_INDEX32);
197 CASE (D3DFMT_Q16W16V16U16);
199 CASE (D3DFMT_G16R16F);
200 CASE (D3DFMT_A16B16G16R16F);
202 CASE (D3DFMT_G32R32F);
203 CASE (D3DFMT_A32B32G32R32F);
204 CASE (D3DFMT_CxV8U8);
205 CASE (D3DFMT_FORCE_DWORD);
217 GstVideoFormat gst_format;
218 D3DFORMAT d3d_format;
219 } gst_d3d_format_map[] = {
221 GST_VIDEO_FORMAT_BGRx, D3DFMT_X8R8G8B8}, {
222 GST_VIDEO_FORMAT_RGBx, D3DFMT_X8B8G8R8}, {
223 GST_VIDEO_FORMAT_BGRA, D3DFMT_A8R8G8B8}, {
224 GST_VIDEO_FORMAT_RGBA, D3DFMT_A8B8G8R8}, {
225 GST_VIDEO_FORMAT_BGR, D3DFMT_R8G8B8}, {
226 GST_VIDEO_FORMAT_RGB16, D3DFMT_R5G6B5}, {
227 GST_VIDEO_FORMAT_RGB15, D3DFMT_X1R5G5B5}, {
228 GST_VIDEO_FORMAT_I420, D3DFMT_YV12}, {
229 GST_VIDEO_FORMAT_YV12, D3DFMT_YV12}, {
230 GST_VIDEO_FORMAT_NV12, D3DFMT_NV12}, {
231 GST_VIDEO_FORMAT_YUY2, D3DFMT_YUY2}, {
232 GST_VIDEO_FORMAT_UYVY, D3DFMT_UYVY}
236 gst_video_format_to_d3d_format (GstVideoFormat format)
240 for (i = 0; i < G_N_ELEMENTS (gst_d3d_format_map); i++)
241 if (gst_d3d_format_map[i].gst_format == format)
242 return gst_d3d_format_map[i].d3d_format;
243 return D3DFMT_UNKNOWN;
247 gst_video_d3d_format_check (GstD3DVideoSink * sink, D3DFORMAT fmt)
249 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
251 gboolean ret = FALSE;
253 hr = IDirect3D9_CheckDeviceFormat (klass->d3d.d3d,
254 klass->d3d.device.adapter,
255 D3DDEVTYPE_HAL, klass->d3d.device.format, 0, D3DRTYPE_SURFACE, fmt);
257 /* test whether device can perform color-conversion
258 * from that format to target format
260 hr = IDirect3D9_CheckDeviceFormatConversion (klass->d3d.d3d,
261 klass->d3d.device.adapter,
262 D3DDEVTYPE_HAL, fmt, klass->d3d.device.format);
266 GST_DEBUG_OBJECT (sink, "Checking: %s - %s", d3d_format_to_string (fmt),
267 ret ? "TRUE" : "FALSE");
273 gst_video_query_d3d_format (GstD3DVideoSink * sink, D3DFORMAT d3dformat)
275 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
277 /* If it's the display adapter format we don't need to probe */
278 if (d3dformat == klass->d3d.device.format)
281 if (gst_video_d3d_format_check (sink, d3dformat))
295 d3d_format_comp_free (D3DFormatComp * comp)
297 g_slice_free (D3DFormatComp, comp);
301 d3d_format_comp_rate (const D3DFormatComp * comp)
304 const GstVideoFormatInfo *info;
306 info = gst_video_format_get_info (comp->fmt);
310 if (GST_VIDEO_FORMAT_INFO_IS_YUV (info))
312 else if (GST_VIDEO_FORMAT_INFO_IS_RGB (info)) {
313 guint i, bit_depth = 0;
314 for (i = 0; i < GST_VIDEO_FORMAT_INFO_N_COMPONENTS (info); i++)
315 bit_depth += GST_VIDEO_FORMAT_INFO_DEPTH (info, i);
324 d3d_format_comp_compare (gconstpointer a, gconstpointer b)
326 gint ptsa = 0, ptsb = 0;
328 ptsa = d3d_format_comp_rate ((const D3DFormatComp *) a);
329 ptsb = d3d_format_comp_rate ((const D3DFormatComp *) b);
333 else if (ptsa == ptsb)
339 #define GST_D3D_SURFACE_MEMORY_NAME "D3DSurface"
345 GstD3DVideoSink *sink;
350 LPDIRECT3DSURFACE9 surface;
352 gint x, y, width, height;
353 } GstD3DSurfaceMemory;
356 gst_d3d_surface_memory_allocator_alloc (GstAllocator * allocator, gsize size,
357 GstAllocationParams * params)
359 g_assert_not_reached ();
364 gst_d3d_surface_memory_allocator_free (GstAllocator * allocator,
367 GstD3DSurfaceMemory *dmem = (GstD3DSurfaceMemory *) mem;
369 /* If this is a sub-memory, do nothing */
374 g_warning ("d3dvideosink: Freeing memory that is still mapped");
376 IDirect3DSurface9_Release (dmem->surface);
377 gst_object_unref (dmem->sink);
378 g_mutex_clear (&dmem->lock);
379 g_slice_free (GstD3DSurfaceMemory, dmem);
383 gst_d3d_surface_memory_map (GstMemory * mem, gsize maxsize, GstMapFlags flags)
385 GstD3DSurfaceMemory *parent;
388 /* find the real parent */
389 if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
390 parent = (GstD3DSurfaceMemory *) mem;
392 g_mutex_lock (&parent->lock);
393 if (!parent->map_count
394 && IDirect3DSurface9_LockRect (parent->surface, &parent->lr, NULL,
400 ret = parent->lr.pBits;
404 g_mutex_unlock (&parent->lock);
410 gst_d3d_surface_memory_unmap (GstMemory * mem)
412 GstD3DSurfaceMemory *parent;
414 /* find the real parent */
415 if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
416 parent = (GstD3DSurfaceMemory *) mem;
418 g_mutex_lock (&parent->lock);
420 if (parent->map_count == 0) {
421 IDirect3DSurface9_UnlockRect (parent->surface);
422 memset (&parent->lr, 0, sizeof (parent->lr));
425 g_mutex_unlock (&parent->lock);
429 gst_d3d_surface_memory_share (GstMemory * mem, gssize offset, gssize size)
431 GstD3DSurfaceMemory *sub;
432 GstD3DSurfaceMemory *parent;
434 /* find the real parent */
435 if ((parent = (GstD3DSurfaceMemory *) mem->parent) == NULL)
436 parent = (GstD3DSurfaceMemory *) mem;
439 size = mem->size - offset;
441 sub = g_slice_new0 (GstD3DSurfaceMemory);
442 /* the shared memory is always readonly */
443 gst_memory_init (GST_MEMORY_CAST (sub), GST_MINI_OBJECT_FLAGS (parent) |
444 GST_MINI_OBJECT_FLAG_LOCK_READONLY, mem->allocator,
445 GST_MEMORY_CAST (parent), mem->maxsize, mem->align, mem->offset + offset,
448 return GST_MEMORY_CAST (sub);
454 } GstD3DSurfaceMemoryAllocator;
458 GstAllocatorClass parent_class;
459 } GstD3DSurfaceMemoryAllocatorClass;
461 GType gst_d3d_surface_memory_allocator_get_type (void);
462 G_DEFINE_TYPE (GstD3DSurfaceMemoryAllocator, gst_d3d_surface_memory_allocator,
465 #define GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR (gst_d3d_surface_memory_allocator_get_type())
466 #define GST_IS_D3D_SURFACE_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR))
469 gst_d3d_surface_memory_allocator_class_init (GstD3DSurfaceMemoryAllocatorClass *
472 GstAllocatorClass *allocator_class;
474 allocator_class = (GstAllocatorClass *) klass;
476 allocator_class->alloc = gst_d3d_surface_memory_allocator_alloc;
477 allocator_class->free = gst_d3d_surface_memory_allocator_free;
481 gst_d3d_surface_memory_allocator_init (GstD3DSurfaceMemoryAllocator * allocator)
483 GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator);
485 alloc->mem_type = GST_D3D_SURFACE_MEMORY_NAME;
486 alloc->mem_map = gst_d3d_surface_memory_map;
487 alloc->mem_unmap = gst_d3d_surface_memory_unmap;
488 alloc->mem_share = gst_d3d_surface_memory_share;
491 GST_OBJECT_FLAG_SET (allocator, GST_ALLOCATOR_FLAG_CUSTOM_ALLOC);
494 G_DEFINE_TYPE (GstD3DSurfaceBufferPool, gst_d3dsurface_buffer_pool,
495 GST_TYPE_VIDEO_BUFFER_POOL);
498 gst_d3dsurface_buffer_pool_new (GstD3DVideoSink * sink)
500 GstD3DSurfaceBufferPool *pool;
502 pool = g_object_new (GST_TYPE_D3DSURFACE_BUFFER_POOL, NULL);
503 gst_object_ref_sink (pool);
504 pool->sink = gst_object_ref (sink);
506 GST_LOG_OBJECT (pool, "new buffer pool %p", pool);
508 return GST_BUFFER_POOL_CAST (pool);
512 gst_d3dsurface_buffer_pool_finalize (GObject * object)
514 GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (object);
516 GST_LOG_OBJECT (pool, "finalize buffer pool %p", pool);
518 gst_object_unref (pool->sink);
520 gst_object_unref (pool->allocator);
522 G_OBJECT_CLASS (gst_d3dsurface_buffer_pool_parent_class)->finalize (object);
525 static const gchar **
526 gst_d3dsurface_buffer_pool_get_options (GstBufferPool * pool)
528 static const gchar *options[] = { GST_BUFFER_POOL_OPTION_VIDEO_META, NULL };
533 /* Calculate actual required buffer size from D3DLOCKED_RECT structure.
534 * Note that D3D could require larger Pitch value than minimum required one in theory.
536 * https://docs.microsoft.com/en-us/windows/desktop/direct3d9/width-vs--pitch */
538 d3d_calculate_buffer_size (GstVideoInfo * info, D3DLOCKED_RECT * lr,
539 gsize * offset, gint * stride, gsize * size)
541 switch (GST_VIDEO_INFO_FORMAT (info)) {
542 case GST_VIDEO_FORMAT_BGR:
543 case GST_VIDEO_FORMAT_BGRx:
544 case GST_VIDEO_FORMAT_RGBx:
545 case GST_VIDEO_FORMAT_BGRA:
546 case GST_VIDEO_FORMAT_RGBA:
547 case GST_VIDEO_FORMAT_RGB16:
548 case GST_VIDEO_FORMAT_RGB15:
549 case GST_VIDEO_FORMAT_YUY2:
550 case GST_VIDEO_FORMAT_UYVY:
552 stride[0] = lr->Pitch;
553 *size = lr->Pitch * GST_VIDEO_INFO_HEIGHT (info);
555 case GST_VIDEO_FORMAT_I420:
556 case GST_VIDEO_FORMAT_YV12:
558 stride[0] = lr->Pitch;
559 if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_YV12) {
561 offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
562 stride[1] = lr->Pitch / 2;
564 offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
565 stride[2] = lr->Pitch / 2;
566 *size = offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (info, 2);
569 offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
570 stride[2] = lr->Pitch / 2;
572 offset[2] + stride[2] * GST_VIDEO_INFO_COMP_HEIGHT (info, 2);
573 stride[1] = lr->Pitch / 2;
574 *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
577 case GST_VIDEO_FORMAT_NV12:
579 stride[0] = lr->Pitch;
580 offset[1] = offset[0] + stride[0] * GST_VIDEO_INFO_COMP_HEIGHT (info, 0);
581 stride[1] = lr->Pitch;
582 *size = offset[1] + stride[1] * GST_VIDEO_INFO_COMP_HEIGHT (info, 1);
588 GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT
589 " (%s %dx%d, Pitch %d)", *size,
590 gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info)),
591 GST_VIDEO_INFO_WIDTH (info), GST_VIDEO_INFO_HEIGHT (info), lr->Pitch);
597 gst_d3dsurface_buffer_pool_set_config (GstBufferPool * bpool,
598 GstStructure * config)
600 GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool);
601 GstD3DVideoSink *sink = pool->sink;
602 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
605 LPDIRECT3DSURFACE9 surface;
607 gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
608 gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
613 if (!gst_buffer_pool_config_get_params (config, &caps, NULL, NULL, NULL)
615 GST_ERROR_OBJECT (pool, "Buffer pool configuration without caps");
619 /* now parse the caps from the config */
620 if (!gst_video_info_from_caps (&info, caps)) {
621 GST_ERROR_OBJECT (pool, "Failed to parse caps %" GST_PTR_FORMAT, caps);
625 d3dformat = gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&info));
626 if (d3dformat == D3DFMT_UNKNOWN) {
627 GST_ERROR_OBJECT (pool, "Unsupported video format in caps %" GST_PTR_FORMAT,
632 GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
635 /* Create a surface to get exact buffer size */
636 LOCK_CLASS (sink, klass);
637 CHECK_REF_COUNT (klass, sink, error);
638 CHECK_D3D_DEVICE (klass, sink, error);
639 hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d.
640 device.d3d_device, GST_VIDEO_INFO_WIDTH (&info),
641 GST_VIDEO_INFO_HEIGHT (&info), d3dformat, D3DPOOL_DEFAULT, &surface,
643 UNLOCK_CLASS (sink, klass);
645 GST_ERROR_OBJECT (sink, "Failed to create D3D surface");
649 IDirect3DSurface9_LockRect (surface, &lr, NULL, 0);
651 GST_ERROR_OBJECT (sink, "Failed to lock D3D surface");
652 IDirect3DSurface9_Release (surface);
656 if (!d3d_calculate_buffer_size (&info, &lr, offset, stride, &size)) {
657 GST_ERROR_OBJECT (sink, "Failed to get buffer size");
658 IDirect3DSurface9_UnlockRect (surface);
659 IDirect3DSurface9_Release (surface);
663 IDirect3DSurface9_UnlockRect (surface);
664 IDirect3DSurface9_Release (surface);
668 pool->add_metavideo =
669 gst_buffer_pool_config_has_option (config,
670 GST_BUFFER_POOL_OPTION_VIDEO_META);
672 if (pool->add_metavideo) {
674 g_object_new (GST_TYPE_D3D_SURFACE_MEMORY_ALLOCATOR, NULL);
675 gst_object_ref_sink (pool->allocator);
678 gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
680 return GST_BUFFER_POOL_CLASS
681 (gst_d3dsurface_buffer_pool_parent_class)->set_config (bpool, config);
684 UNLOCK_CLASS (sink, klass);
689 gst_d3dsurface_buffer_pool_alloc_buffer (GstBufferPool * bpool,
690 GstBuffer ** buffer, GstBufferPoolAcquireParams * params)
692 GstD3DSurfaceBufferPool *pool = GST_D3DSURFACE_BUFFER_POOL_CAST (bpool);
693 GstD3DVideoSink *sink = pool->sink;
694 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
695 GstD3DSurfaceMemory *mem;
696 LPDIRECT3DSURFACE9 surface;
698 gint stride[GST_VIDEO_MAX_PLANES] = { 0, };
699 gsize offset[GST_VIDEO_MAX_PLANES] = { 0, };
705 if (!pool->add_metavideo) {
706 GST_DEBUG_OBJECT (pool, "No video meta allowed, fallback alloc");
711 gst_video_format_to_d3d_format (GST_VIDEO_INFO_FORMAT (&pool->info));
712 LOCK_CLASS (sink, klass);
713 CHECK_REF_COUNT (klass, sink, error);
714 CHECK_D3D_DEVICE (klass, sink, error);
715 hr = IDirect3DDevice9_CreateOffscreenPlainSurface (klass->d3d.
716 device.d3d_device, GST_VIDEO_INFO_WIDTH (&pool->info),
717 GST_VIDEO_INFO_HEIGHT (&pool->info), d3dformat, D3DPOOL_DEFAULT, &surface,
719 UNLOCK_CLASS (sink, klass);
721 GST_ERROR_OBJECT (sink, "Failed to create D3D surface");
725 IDirect3DSurface9_LockRect (surface, &lr, NULL, 0);
727 GST_ERROR_OBJECT (sink, "Failed to lock D3D surface");
728 IDirect3DSurface9_Release (surface);
732 if (!d3d_calculate_buffer_size (&pool->info, &lr, offset, stride, &size)) {
733 GST_ERROR_OBJECT (sink, "Failed to get buffer size");
734 IDirect3DSurface9_UnlockRect (surface);
735 IDirect3DSurface9_Release (surface);
736 return GST_FLOW_ERROR;
739 IDirect3DSurface9_UnlockRect (surface);
741 *buffer = gst_buffer_new ();
743 gst_buffer_add_video_meta_full (*buffer, GST_VIDEO_FRAME_FLAG_NONE,
744 GST_VIDEO_INFO_FORMAT (&pool->info), GST_VIDEO_INFO_WIDTH (&pool->info),
745 GST_VIDEO_INFO_HEIGHT (&pool->info),
746 GST_VIDEO_INFO_N_PLANES (&pool->info), offset, stride);
748 mem = g_slice_new0 (GstD3DSurfaceMemory);
749 gst_memory_init (GST_MEMORY_CAST (mem), 0, pool->allocator, NULL, size, 0, 0,
752 mem->surface = surface;
753 mem->sink = gst_object_ref (sink);
755 mem->width = GST_VIDEO_INFO_WIDTH (&pool->info);
756 mem->height = GST_VIDEO_INFO_HEIGHT (&pool->info);
757 g_mutex_init (&mem->lock);
759 gst_buffer_append_memory (*buffer, GST_MEMORY_CAST (mem));
766 GST_BUFFER_POOL_CLASS
767 (gst_d3dsurface_buffer_pool_parent_class)->alloc_buffer (bpool, buffer,
771 UNLOCK_CLASS (sink, klass);
772 return GST_FLOW_ERROR;
776 gst_d3dsurface_buffer_pool_class_init (GstD3DSurfaceBufferPoolClass * klass)
778 GObjectClass *gobject_class = (GObjectClass *) klass;
779 GstBufferPoolClass *gstbufferpool_class = (GstBufferPoolClass *) klass;
781 gobject_class->finalize = gst_d3dsurface_buffer_pool_finalize;
783 gstbufferpool_class->get_options = gst_d3dsurface_buffer_pool_get_options;
784 gstbufferpool_class->set_config = gst_d3dsurface_buffer_pool_set_config;
785 gstbufferpool_class->alloc_buffer = gst_d3dsurface_buffer_pool_alloc_buffer;
789 gst_d3dsurface_buffer_pool_init (GstD3DSurfaceBufferPool * pool)
794 d3d_supported_caps (GstD3DVideoSink * sink)
796 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
798 GList *fmts = NULL, *l;
799 GstCaps *caps = NULL;
800 GstVideoFormat gst_format;
801 D3DFORMAT d3d_format;
807 if (sink->supported_caps) {
808 caps = gst_caps_ref (sink->supported_caps);
812 LOCK_CLASS (sink, klass);
813 if (klass->d3d.refs == 0) {
814 UNLOCK_CLASS (sink, klass);
817 UNLOCK_CLASS (sink, klass);
819 for (i = 0; i < G_N_ELEMENTS (gst_d3d_format_map); i++) {
822 gst_format = gst_d3d_format_map[i].gst_format;
823 d3d_format = gst_d3d_format_map[i].d3d_format;
824 if (!gst_video_query_d3d_format (sink, d3d_format))
827 comp = g_slice_new0 (D3DFormatComp);
828 comp->fmt = (GstVideoFormat) gst_format;
829 comp->d3d_fmt = d3d_format;
830 comp->display = (d3d_format == klass->d3d.device.format);
831 fmts = g_list_insert_sorted (fmts, comp, d3d_format_comp_compare);
834 GST_DEBUG_OBJECT (sink, "Supported Caps:");
836 g_value_init (&va, GST_TYPE_LIST);
837 g_value_init (&v, G_TYPE_STRING);
839 for (l = fmts; l; l = g_list_next (l)) {
840 D3DFormatComp *comp = (D3DFormatComp *) l->data;
842 GST_DEBUG_OBJECT (sink, "%s -> %s %s",
843 gst_video_format_to_string (comp->fmt),
844 d3d_format_to_string (comp->d3d_fmt), comp->display ? "[display]" : "");
845 g_value_set_string (&v, gst_video_format_to_string (comp->fmt));
846 gst_value_list_append_value (&va, &v);
850 gst_caps_make_writable (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
853 gst_caps_set_value (caps, "format", &va);
856 g_list_free_full (fmts, (GDestroyNotify) d3d_format_comp_free);
858 sink->supported_caps = gst_caps_ref (caps);
860 #ifndef GST_DISABLE_GST_DEBUG
862 GST_DEBUG_OBJECT (sink, "Supported caps: %" GST_PTR_FORMAT, caps);
873 d3d_set_render_format (GstD3DVideoSink * sink)
876 gboolean ret = FALSE;
880 fmt = gst_video_format_to_d3d_format (sink->format);
881 if (fmt == D3DFMT_UNKNOWN) {
882 GST_ERROR_OBJECT (sink, "Unsupported video format %s",
883 gst_video_format_to_string (sink->format));
887 if (!gst_video_query_d3d_format (sink, fmt)) {
888 GST_ERROR_OBJECT (sink, "Failed to query a D3D render format for %s",
889 gst_video_format_to_string (sink->format));
893 GST_DEBUG_OBJECT (sink, "Selected %s -> %s",
894 gst_video_format_to_string (sink->format), d3d_format_to_string (fmt));
896 sink->d3d.format = fmt;
907 d3d_get_hwnd_window_size (HWND hwnd, gint * width, gint * height)
911 g_return_val_if_fail (width != NULL, FALSE);
912 g_return_val_if_fail (height != NULL, FALSE);
920 GetClientRect (hwnd, &sz);
922 *width = MAX (1, ABS (sz.right - sz.left));
923 *height = MAX (1, ABS (sz.bottom - sz.top));
929 d3d_get_render_rects (GstVideoRectangle * rr, RECT * dst, RECT * src)
938 dst->right = rr->x + rr->w;
939 dst->bottom = rr->y + rr->h;
954 d3d_get_render_coordinates (GstD3DVideoSink * sink, gint in_x, gint in_y,
955 gdouble * out_x, gdouble * out_y)
957 GstVideoRectangle r_area;
959 gboolean ret = FALSE;
961 g_return_val_if_fail (out_x != NULL, FALSE);
962 g_return_val_if_fail (out_y != NULL, FALSE);
965 CHECK_WINDOW_HANDLE (sink, end, FALSE);
967 /* Get renderable area of the window */
968 if (sink->d3d.render_rect) {
969 memcpy (&r_area, sink->d3d.render_rect, sizeof (r_area));
971 memset (&r_area, 0, sizeof (r_area));
972 d3d_get_hwnd_window_size (sink->d3d.window_handle, &r_area.w, &r_area.h);
975 /* If window coords outside render area.. return */
976 if (in_x < r_area.x || in_x > r_area.x + r_area.w ||
977 in_y < r_area.y || in_y > r_area.y + r_area.h)
980 /* Convert window coordinates to source frame pixel coordinates */
981 if (sink->force_aspect_ratio) {
982 GstVideoRectangle tmp = { 0, 0, 0, 0 };
983 GstVideoRectangle dst = { 0, 0, 0, 0 };
985 tmp.w = GST_VIDEO_SINK_WIDTH (sink);
986 tmp.h = GST_VIDEO_SINK_HEIGHT (sink);
987 gst_video_sink_center_rect (tmp, r_area, &dst, TRUE);
989 r_area.x = r_area.x + dst.x;
990 r_area.y = r_area.y + dst.y;
994 /* If window coords outside render area.. return */
995 if (in_x < r_area.x || in_x > (r_area.x + r_area.w) ||
996 in_y < r_area.y || in_y > (r_area.y + r_area.h))
1000 tmp = in_x - r_area.x;
1001 if (r_area.w == GST_VIDEO_SINK_WIDTH (sink))
1003 else if (r_area.w > GST_VIDEO_SINK_WIDTH (sink))
1005 ((gdouble) tmp / ((gdouble) r_area.w /
1006 (gdouble) GST_VIDEO_SINK_WIDTH (sink)));
1009 ((gdouble) GST_VIDEO_SINK_WIDTH (sink) / (gdouble) r_area.w) *
1012 tmp = in_y - r_area.y;
1013 if (r_area.h == GST_VIDEO_SINK_HEIGHT (sink))
1015 else if (r_area.h > GST_VIDEO_SINK_HEIGHT (sink))
1017 ((gdouble) tmp / ((gdouble) r_area.h /
1018 (gdouble) GST_VIDEO_SINK_HEIGHT (sink)));
1021 ((gdouble) GST_VIDEO_SINK_HEIGHT (sink) / (gdouble) r_area.h) *
1030 /* Windows for rendering (User Set or Internal) */
1033 d3d_window_wndproc_unset (GstD3DVideoSink * sink)
1035 WNDPROC cur_wnd_proc = NULL;
1039 GST_DEBUG_OBJECT (sink, " ");
1041 if (sink->d3d.window_handle == NULL) {
1042 GST_WARNING_OBJECT (sink, "D3D window_handle is NULL");
1047 (WNDPROC) GetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC);
1049 if (cur_wnd_proc != d3d_wnd_proc) {
1050 GST_WARNING_OBJECT (sink, "D3D window proc is not set on current window");
1054 if (sink->d3d.orig_wnd_proc == NULL) {
1055 GST_WARNING_OBJECT (sink, "D3D orig window proc is NULL, can not restore");
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");
1067 sink->d3d.orig_wnd_proc = NULL;
1068 sink->d3d.window_handle = NULL;
1074 d3d_window_wndproc_set (GstD3DVideoSink * sink)
1076 WNDPROC cur_wnd_proc;
1077 gboolean ret = FALSE;
1082 (WNDPROC) GetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC);
1084 if (cur_wnd_proc != NULL && cur_wnd_proc == d3d_wnd_proc) {
1085 GST_DEBUG_OBJECT (sink,
1086 "D3D window proc func is already set on the current window");
1091 /* Store the original window proc function */
1092 sink->d3d.orig_wnd_proc =
1093 (WNDPROC) SetWindowLongPtr (sink->d3d.window_handle, GWLP_WNDPROC,
1094 (LONG_PTR) d3d_wnd_proc);
1096 /* Note: If the window belongs to another process this will fail */
1097 if (sink->d3d.orig_wnd_proc == NULL) {
1098 GST_ERROR_OBJECT (sink,
1099 "Failed to set WndProc function on window. Error: %d",
1100 (gint) GetLastError ());
1104 /* Make sink accessible to d3d_wnd_proc */
1105 SetProp (sink->d3d.window_handle, TEXT ("GstD3DVideoSink"), sink);
1115 d3d_prepare_render_window (GstD3DVideoSink * sink)
1119 if (sink->d3d.window_handle == NULL) {
1120 GST_DEBUG_OBJECT (sink, "No window handle has been set.");
1124 if (sink->d3d.device_lost) {
1125 GST_DEBUG_OBJECT (sink, "Device is lost, waiting for reset.");
1129 if (d3d_init_swap_chain (sink, sink->d3d.window_handle)) {
1130 d3d_window_wndproc_set (sink);
1131 sink->d3d.renderable = TRUE;
1132 GST_DEBUG_OBJECT (sink, "Prepared window for render [HWND:%p]",
1133 sink->d3d.window_handle);
1135 GST_ERROR_OBJECT (sink, "Failed preparing window for render [HWND:%p]",
1136 sink->d3d.window_handle);
1145 d3d_set_window_handle (GstD3DVideoSink * sink, guintptr window_id,
1146 gboolean is_internal)
1150 if (sink->d3d.window_handle == (HWND) window_id) {
1151 GST_WARNING_OBJECT (sink, "Window HWND already set to: %" G_GUINTPTR_FORMAT,
1156 /* Unset current window */
1157 if (sink->d3d.window_handle != NULL) {
1158 PostMessage (sink->d3d.window_handle, WM_QUIT_THREAD, 0, 0);
1159 GST_DEBUG_OBJECT (sink, "Unsetting window [HWND:%p]",
1160 sink->d3d.window_handle);
1161 d3d_window_wndproc_unset (sink);
1162 d3d_release_swap_chain (sink);
1163 sink->d3d.window_handle = NULL;
1164 sink->d3d.window_is_internal = FALSE;
1165 sink->d3d.renderable = FALSE;
1170 sink->d3d.window_handle = (HWND) window_id;
1171 sink->d3d.window_is_internal = is_internal;
1173 sink->d3d.external_window_handle = sink->d3d.window_handle;
1174 /* If caps have been set.. prepare window */
1175 if (sink->format != 0)
1176 d3d_prepare_render_window (sink);
1184 d3d_set_render_rectangle (GstD3DVideoSink * sink)
1187 /* Setting the pointer lets us know render rect is set */
1188 sink->d3d.render_rect = &sink->render_rect;
1189 d3d_resize_swap_chain (sink);
1190 d3d_present_swap_chain (sink);
1195 d3d_expose_window (GstD3DVideoSink * sink)
1197 GST_DEBUG_OBJECT (sink, "EXPOSE");
1198 d3d_present_swap_chain (sink);
1202 d3d_prepare_window (GstD3DVideoSink * sink)
1205 gboolean ret = FALSE;
1209 /* if we already had an external window, then use it again */
1210 if (sink->d3d.external_window_handle)
1211 sink->d3d.window_handle = sink->d3d.external_window_handle;
1213 /* Give the app a last chance to set a window id */
1214 if (!sink->d3d.window_handle)
1215 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
1217 /* If the user did not set a window id .. check if we should create one */
1218 if (!sink->d3d.window_handle) {
1219 if (sink->create_internal_window) {
1220 if ((hWnd = d3d_create_internal_window (sink))) {
1221 GST_DEBUG_OBJECT (sink,
1222 "No window id was set.. creating internal window");
1223 d3d_set_window_handle (sink, (guintptr) hWnd, TRUE);
1225 GST_ERROR_OBJECT (sink, "Failed to create internal window");
1229 GST_DEBUG_OBJECT (sink, "No window id is set..");
1233 d3d_prepare_render_window (sink);
1245 d3d_stop (GstD3DVideoSink * sink)
1248 gst_buffer_pool_set_active (sink->pool, FALSE);
1249 if (sink->fallback_pool)
1250 gst_buffer_pool_set_active (sink->fallback_pool, FALSE);
1251 gst_object_replace ((GstObject **) & sink->pool, NULL);
1252 gst_object_replace ((GstObject **) & sink->fallback_pool, NULL);
1253 gst_buffer_replace (&sink->fallback_buffer, NULL);
1255 /* Release D3D resources */
1256 d3d_set_window_handle (sink, 0, FALSE);
1258 if (sink->internal_window_thread) {
1259 g_thread_join (sink->internal_window_thread);
1260 sink->internal_window_thread = NULL;
1266 /* D3D Lost and Reset Device */
1269 d3d_notify_device_lost (GstD3DVideoSink * sink)
1271 gboolean notify = FALSE;
1273 g_return_if_fail (GST_IS_D3DVIDEOSINK (sink));
1277 if (!sink->d3d.device_lost) {
1278 GST_WARNING_OBJECT (sink, "D3D Device has been lost. Clean up resources.");
1280 /* Stream will continue with GST_FLOW_OK, until device has been reset */
1281 sink->d3d.device_lost = TRUE;
1283 /* First we clean up all resources in this d3dvideo instance */
1284 d3d_release_swap_chain (sink);
1286 /* Notify our hidden thread */
1293 d3d_class_notify_device_lost (sink);
1297 d3d_notify_device_reset (GstD3DVideoSink * sink)
1301 if (sink->d3d.device_lost) {
1302 GST_DEBUG_OBJECT (sink,
1303 "D3D Device has been reset. Re-init swap chain if still streaming");
1304 /* If we're still streaming.. reset swap chain */
1305 if (sink->d3d.window_handle != NULL)
1306 d3d_init_swap_chain (sink, sink->d3d.window_handle);
1307 sink->d3d.device_lost = FALSE;
1316 d3d_init_swap_chain (GstD3DVideoSink * sink, HWND hWnd)
1318 D3DPRESENT_PARAMETERS present_params;
1319 LPDIRECT3DSWAPCHAIN9 d3d_swapchain = NULL;
1320 D3DTEXTUREFILTERTYPE d3d_filtertype;
1322 GstD3DVideoSinkClass *klass;
1323 gboolean ret = FALSE;
1325 g_return_val_if_fail (sink != NULL, FALSE);
1326 klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1327 g_return_val_if_fail (klass != NULL, FALSE);
1330 LOCK_CLASS (sink, klass);
1332 /* We need a display device */
1333 CHECK_D3D_DEVICE (klass, sink, error);
1335 GST_DEBUG ("Initializing Direct3D swap chain");
1337 GST_DEBUG ("Direct3D back buffer size: %dx%d", GST_VIDEO_SINK_WIDTH (sink),
1338 GST_VIDEO_SINK_HEIGHT (sink));
1340 /* When windowed, width and height determined by HWND */
1341 ZeroMemory (&present_params, sizeof (present_params));
1342 present_params.Windowed = TRUE;
1343 present_params.SwapEffect = D3DSWAPEFFECT_DISCARD; /* D3DSWAPEFFECT_COPY */
1344 present_params.hDeviceWindow = hWnd;
1345 present_params.BackBufferFormat = klass->d3d.device.format;
1347 hr = IDirect3DDevice9_CreateAdditionalSwapChain (klass->d3d.device.d3d_device,
1348 &present_params, &d3d_swapchain);
1349 ERROR_CHECK_HR (hr) {
1350 CASE_HR_ERR (D3DERR_NOTAVAILABLE);
1351 CASE_HR_ERR (D3DERR_DEVICELOST);
1352 CASE_HR_ERR (D3DERR_INVALIDCALL);
1353 CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1354 CASE_HR_ERR (E_OUTOFMEMORY);
1355 CASE_HR_ERR_END (sink, "Error creating D3D swapchian");
1359 /* Determine texture filtering support. If it's supported for this format,
1360 * use the filter type determined when we created the dev and checked the
1363 hr = IDirect3D9_CheckDeviceFormat (klass->d3d.d3d,
1364 klass->d3d.device.adapter,
1366 klass->d3d.device.format,
1367 D3DUSAGE_QUERY_FILTER, D3DRTYPE_TEXTURE, sink->d3d.format);
1369 d3d_filtertype = klass->d3d.device.filter_type;
1371 d3d_filtertype = D3DTEXF_NONE;
1373 GST_DEBUG ("Direct3D stretch rect texture filter: %d", d3d_filtertype);
1375 sink->d3d.filtertype = d3d_filtertype;
1377 if (sink->d3d.swapchain != NULL)
1378 IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1380 sink->d3d.swapchain = d3d_swapchain;
1387 IDirect3DSwapChain9_Release (d3d_swapchain);
1390 UNLOCK_CLASS (sink, klass);
1397 d3d_release_swap_chain (GstD3DVideoSink * sink)
1399 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1401 gboolean ret = FALSE;
1405 GST_DEBUG_OBJECT (sink, "Releasing Direct3D swap chain");
1407 CHECK_D3D_DEVICE (klass, sink, end);
1409 if (!sink->d3d.swapchain) {
1414 gst_buffer_replace (&sink->fallback_buffer, NULL);
1415 if (sink->fallback_pool)
1416 gst_buffer_pool_set_active (sink->fallback_pool, FALSE);
1418 if (sink->d3d.swapchain) {
1419 ref_count = IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1420 sink->d3d.swapchain = NULL;
1421 GST_DEBUG_OBJECT (sink, "D3D swapchain released. Ref count: %d", ref_count);
1424 if (sink->d3d.surface) {
1425 ref_count = IDirect3DSurface9_Release (sink->d3d.surface);
1426 sink->d3d.surface = NULL;
1427 GST_DEBUG_OBJECT (sink, "D3D surface released. Ref count: %d", ref_count);
1439 d3d_resize_swap_chain (GstD3DVideoSink * sink)
1441 GstD3DVideoSinkClass *klass;
1442 D3DPRESENT_PARAMETERS d3d_pp;
1443 LPDIRECT3DSWAPCHAIN9 swapchain = NULL;
1444 gint w = 0, h = 0, ref_count = 0;
1445 gboolean ret = FALSE;
1447 gboolean need_new = FALSE;
1450 RECT clip_rectangle;
1452 g_return_val_if_fail (sink != NULL, FALSE);
1453 klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1454 g_return_val_if_fail (klass != NULL, FALSE);
1458 if (!sink->d3d.renderable || sink->d3d.device_lost) {
1463 LOCK_CLASS (sink, klass);
1465 CHECK_WINDOW_HANDLE (sink, end, FALSE);
1466 CHECK_D3D_DEVICE (klass, sink, end);
1467 CHECK_D3D_SWAPCHAIN (sink, end);
1469 handle_hdc = GetDC (sink->d3d.window_handle);
1470 clip_ret = GetClipBox (handle_hdc, &clip_rectangle);
1471 ReleaseDC (sink->d3d.window_handle, handle_hdc);
1472 if (clip_ret == NULLREGION) {
1473 GST_DEBUG_OBJECT (sink, "Window is hidden, not resizing swapchain");
1474 UNLOCK_CLASS (sink, klass);
1479 d3d_get_hwnd_window_size (sink->d3d.window_handle, &w, &h);
1480 ZeroMemory (&d3d_pp, sizeof (d3d_pp));
1482 /* Get the parameters used to create this swap chain */
1483 hr = IDirect3DSwapChain9_GetPresentParameters (sink->d3d.swapchain, &d3d_pp);
1485 GST_ERROR_OBJECT (sink,
1486 "Unable to determine Direct3D present parameters for swap chain");
1490 /* Reisze needed? */
1491 if (d3d_pp.BackBufferWidth != w || d3d_pp.BackBufferHeight != h)
1494 /* Render rect set or unset? */
1495 if ((d3d_pp.SwapEffect != D3DSWAPEFFECT_COPY && sink->d3d.render_rect) ||
1496 (d3d_pp.SwapEffect != D3DSWAPEFFECT_DISCARD
1497 && sink->d3d.render_rect == NULL)) {
1499 (sink->d3d.render_rect ==
1500 NULL) ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
1501 GST_DEBUG_OBJECT (sink, "Setting SwapEffect: %s",
1502 sink->d3d.render_rect ? "COPY" : "DISCARD");
1511 GST_DEBUG_OBJECT (sink, "Resizing swapchain %dx%d to %dx%d",
1512 d3d_pp.BackBufferWidth, d3d_pp.BackBufferHeight, w, h);
1515 /* As long as present params windowed == TRUE, width or height
1516 * of 0 will force use of HWND's size.
1518 d3d_pp.BackBufferWidth = 0;
1519 d3d_pp.BackBufferHeight = 0;
1521 /* Release current swapchain */
1522 if (sink->d3d.swapchain != NULL) {
1523 ref_count = IDirect3DSwapChain9_Release (sink->d3d.swapchain);
1524 if (ref_count > 0) {
1525 GST_WARNING_OBJECT (sink, "Release swapchain refcount: %d", ref_count);
1527 sink->d3d.swapchain = NULL;
1530 hr = IDirect3DDevice9_CreateAdditionalSwapChain (klass->d3d.device.d3d_device,
1531 &d3d_pp, &swapchain);
1532 ERROR_CHECK_HR (hr) {
1533 CASE_HR_ERR (D3DERR_NOTAVAILABLE);
1534 CASE_HR_ERR (D3DERR_DEVICELOST);
1535 CASE_HR_ERR (D3DERR_INVALIDCALL);
1536 CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1537 CASE_HR_ERR (E_OUTOFMEMORY);
1538 CASE_HR_ERR_END (sink, "Error creating swapchian");
1542 sink->d3d.swapchain = swapchain;
1543 sink->d3d.overlay_needs_resize = TRUE;
1547 UNLOCK_CLASS (sink, klass);
1554 d3d_copy_buffer (GstD3DVideoSink * sink, GstBuffer * from, GstBuffer * to)
1556 gboolean ret = FALSE;
1557 GstVideoFrame from_frame, to_frame;
1559 memset (&from_frame, 0, sizeof (from_frame));
1560 memset (&to_frame, 0, sizeof (to_frame));
1564 if (!sink->d3d.renderable || sink->d3d.device_lost)
1567 if (!gst_video_frame_map (&from_frame, &sink->info, from, GST_MAP_READ) ||
1568 !gst_video_frame_map (&to_frame, &sink->info, to, GST_MAP_WRITE)) {
1569 GST_ERROR_OBJECT (sink, "NULL GstBuffer");
1573 switch (sink->format) {
1574 case GST_VIDEO_FORMAT_YUY2:
1575 case GST_VIDEO_FORMAT_UYVY:{
1578 gint dststride, srcstride;
1581 src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1582 dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1583 srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1584 dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1585 h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1586 w = GST_ROUND_UP_4 (GST_VIDEO_FRAME_WIDTH (&from_frame) * 2);
1588 for (i = 0; i < h; i++) {
1589 memcpy (dst, src, w);
1596 case GST_VIDEO_FORMAT_I420:
1597 case GST_VIDEO_FORMAT_YV12:{
1600 gint srcstride, dststride;
1603 for (i = 0; i < 3; i++) {
1604 src = GST_VIDEO_FRAME_COMP_DATA (&from_frame, i);
1605 dst = GST_VIDEO_FRAME_COMP_DATA (&to_frame, i);
1606 srcstride = GST_VIDEO_FRAME_COMP_STRIDE (&from_frame, i);
1607 dststride = GST_VIDEO_FRAME_COMP_STRIDE (&to_frame, i);
1608 h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
1609 w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
1611 for (j = 0; j < h_; j++) {
1612 memcpy (dst, src, w_);
1620 case GST_VIDEO_FORMAT_NV12:{
1623 gint srcstride, dststride;
1626 for (i = 0; i < 2; i++) {
1627 src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, i);
1628 dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, i);
1629 srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, i);
1630 dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, i);
1631 h_ = GST_VIDEO_FRAME_COMP_HEIGHT (&from_frame, i);
1632 w_ = GST_VIDEO_FRAME_COMP_WIDTH (&from_frame, i);
1634 for (j = 0; j < h_; j++) {
1635 memcpy (dst, src, w_ * 2);
1643 case GST_VIDEO_FORMAT_BGRA:
1644 case GST_VIDEO_FORMAT_RGBA:
1645 case GST_VIDEO_FORMAT_BGRx:
1646 case GST_VIDEO_FORMAT_RGBx:{
1649 gint srcstride, dststride;
1652 src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1653 dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1654 srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1655 dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1656 h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1657 w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 4;
1659 for (i = 0; i < h; i++) {
1660 memcpy (dst, src, w);
1667 case GST_VIDEO_FORMAT_BGR:{
1670 gint srcstride, dststride;
1673 src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1674 dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1675 srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1676 dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1677 h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1678 w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 3;
1680 for (i = 0; i < h; i++) {
1681 memcpy (dst, src, w);
1688 case GST_VIDEO_FORMAT_RGB16:
1689 case GST_VIDEO_FORMAT_RGB15:{
1692 gint srcstride, dststride;
1695 src = GST_VIDEO_FRAME_PLANE_DATA (&from_frame, 0);
1696 dst = GST_VIDEO_FRAME_PLANE_DATA (&to_frame, 0);
1697 srcstride = GST_VIDEO_FRAME_PLANE_STRIDE (&from_frame, 0);
1698 dststride = GST_VIDEO_FRAME_PLANE_STRIDE (&to_frame, 0);
1699 h = GST_VIDEO_FRAME_HEIGHT (&from_frame);
1700 w = GST_VIDEO_FRAME_WIDTH (&from_frame) * 2;
1702 for (i = 0; i < h; i++) {
1703 memcpy (dst, src, w);
1711 goto unhandled_format;
1717 if (from_frame.buffer)
1718 gst_video_frame_unmap (&from_frame);
1719 if (to_frame.buffer)
1720 gst_video_frame_unmap (&to_frame);
1726 GST_ERROR_OBJECT (sink,
1727 "Unhandled format '%s' -> '%s' (should not get here)",
1728 gst_video_format_to_string (sink->format),
1729 d3d_format_to_string (sink->d3d.format));
1735 d3d_present_swap_chain (GstD3DVideoSink * sink)
1737 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1738 LPDIRECT3DSURFACE9 back_buffer = NULL;
1739 gboolean ret = FALSE;
1741 RECT dstr, srcr, *pDestRect = NULL, *pSrcRect = NULL;
1745 if (!sink->d3d.renderable || sink->d3d.device_lost) {
1750 LOCK_CLASS (sink, klass);
1752 CHECK_WINDOW_HANDLE (sink, end, FALSE);
1753 CHECK_D3D_DEVICE (klass, sink, end);
1754 CHECK_D3D_SWAPCHAIN (sink, end);
1756 /* Set the render target to our swap chain */
1757 hr = IDirect3DSwapChain9_GetBackBuffer (sink->d3d.swapchain, 0,
1758 D3DBACKBUFFER_TYPE_MONO, &back_buffer);
1759 ERROR_CHECK_HR (hr) {
1760 CASE_HR_ERR (D3DERR_INVALIDCALL);
1761 CASE_HR_ERR_END (sink, "IDirect3DSwapChain9_GetBackBuffer");
1764 hr = IDirect3DDevice9_SetRenderTarget (klass->d3d.device.d3d_device, 0,
1766 ERROR_CHECK_HR (hr) {
1767 CASE_HR_ERR (D3DERR_INVALIDCALL);
1768 CASE_HR_ERR_END (sink, "IDirect3DDevice9_SetRenderTarget");
1771 hr = IDirect3DSurface9_Release (back_buffer);
1772 ERROR_CHECK_HR (hr) {
1773 CASE_HR_ERR (D3DERR_INVALIDCALL);
1774 CASE_HR_ERR_END (sink, "IDirect3DSurface9_Release");
1778 /* Clear the target */
1779 hr = IDirect3DDevice9_Clear (klass->d3d.device.d3d_device, 0, NULL,
1780 D3DCLEAR_TARGET, D3DCOLOR_XRGB (0, 0, 0), 1.0f, 0);
1781 ERROR_CHECK_HR (hr) {
1782 CASE_HR_ERR (D3DERR_INVALIDCALL);
1783 CASE_HR_ERR_END (sink, "IDirect3DDevice9_Clear");
1787 hr = IDirect3DDevice9_BeginScene (klass->d3d.device.d3d_device);
1788 ERROR_CHECK_HR (hr) {
1789 CASE_HR_ERR (D3DERR_INVALIDCALL);
1790 CASE_HR_ERR_END (sink, "IDirect3DDevice9_BeginScene");
1794 if (!gst_d3d9_overlay_set_render_state (sink)) {
1795 IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1799 /* Stretch and blit ops, to copy offscreen surface buffer
1800 * to Display back buffer.
1802 if (!d3d_stretch_and_copy (sink, back_buffer) ||
1803 !gst_d3d9_overlay_render (sink)) {
1804 IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1808 hr = IDirect3DDevice9_EndScene (klass->d3d.device.d3d_device);
1809 ERROR_CHECK_HR (hr) {
1810 CASE_HR_ERR (D3DERR_INVALIDCALL);
1811 CASE_HR_ERR_END (sink, "IDirect3DDevice9_EndScene");
1815 if (d3d_get_render_rects (sink->d3d.render_rect, &dstr, &srcr)) {
1821 * Swap back and front buffers on video card and present to the user
1823 hr = IDirect3DSwapChain9_Present (sink->d3d.swapchain, pSrcRect, pDestRect,
1825 if (hr == D3DERR_DEVICELOST) {
1826 d3d_notify_device_lost (sink);
1830 ERROR_CHECK_HR (hr) {
1831 CASE_HR_ERR (D3DERR_DEVICELOST);
1832 CASE_HR_ERR (D3DERR_DRIVERINTERNALERROR);
1833 CASE_HR_ERR (D3DERR_INVALIDCALL);
1834 CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
1835 CASE_HR_ERR (E_OUTOFMEMORY);
1836 CASE_HR_DBG_END (sink, "IDirect3DSwapChain9_Present failure");
1844 UNLOCK_CLASS (sink, klass);
1849 d3d_stretch_and_copy (GstD3DVideoSink * sink, LPDIRECT3DSURFACE9 back_buffer)
1851 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
1852 GstVideoRectangle *render_rect = NULL;
1856 gboolean ret = FALSE;
1860 CHECK_WINDOW_HANDLE (sink, end, FALSE);
1861 CHECK_D3D_DEVICE (klass, sink, end);
1862 CHECK_D3D_SURFACE (sink, end);
1864 render_rect = sink->d3d.render_rect;
1866 if (sink->force_aspect_ratio) {
1869 GstVideoRectangle src;
1870 GstVideoRectangle dst;
1871 GstVideoRectangle result;
1873 memset (&dst, 0, sizeof (dst));
1874 memset (&src, 0, sizeof (src));
1876 /* Set via GstXOverlay set_render_rect */
1878 memcpy (&dst, render_rect, sizeof (dst));
1880 d3d_get_hwnd_window_size (sink->d3d.window_handle, &window_width,
1882 dst.w = window_width;
1883 dst.h = window_height;
1886 src.w = GST_VIDEO_SINK_WIDTH (sink);
1887 src.h = GST_VIDEO_SINK_HEIGHT (sink);
1889 gst_video_sink_center_rect (src, dst, &result, TRUE);
1893 r.right = result.x + result.w;
1894 r.bottom = result.y + result.h;
1896 } else if (render_rect) {
1899 r.right = render_rect->w;
1900 r.bottom = render_rect->h;
1904 s.left = sink->crop_rect.x;
1905 s.top = sink->crop_rect.y;
1906 s.right = sink->crop_rect.x + sink->crop_rect.w;
1907 s.bottom = sink->crop_rect.y + sink->crop_rect.h;
1909 /* TODO: StretchRect returns error if the dest rect is outside
1910 * the backbuffer area. So we need to calc how much of the src
1911 * surface is being scaled / copied to the render rect..
1914 hr = IDirect3DDevice9_StretchRect (klass->d3d.device.d3d_device, sink->d3d.surface, /* Source Surface */
1915 &s, /* Source Surface Rect (NULL: Whole) */
1916 back_buffer, /* Dest Surface */
1917 r_p, /* Dest Surface Rect (NULL: Whole) */
1918 klass->d3d.device.filter_type);
1923 GST_ERROR_OBJECT (sink, "Failure calling Direct3DDevice9_StretchRect");
1933 d3d_render_buffer (GstD3DVideoSink * sink, GstBuffer * buf)
1935 WindowHandleVisibility handle_visibility = WINDOW_VISIBILITY_ERROR;
1938 RECT handle_rectangle;
1939 RECT clip_rectangle;
1941 GstFlowReturn ret = GST_FLOW_OK;
1943 LPDIRECT3DSURFACE9 surface = NULL;
1944 GstVideoCropMeta *crop = NULL;
1948 if (!sink->d3d.window_handle) {
1949 if (sink->stream_stop_on_close) {
1950 /* Handle window deletion by posting an error on the bus */
1951 GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND,
1952 ("Output window was closed"), (NULL));
1953 ret = GST_FLOW_ERROR;
1958 if (sink->d3d.device_lost) {
1959 GST_LOG_OBJECT (sink, "Device lost, waiting for reset..");
1963 /* check for window handle visibility, if hidden skip frame rendering */
1965 handle_hdc = GetDC (sink->d3d.window_handle);
1966 GetClientRect (sink->d3d.window_handle, &handle_rectangle);
1967 clip_ret = GetClipBox (handle_hdc, &clip_rectangle);
1968 ReleaseDC (sink->d3d.window_handle, handle_hdc);
1972 handle_visibility = WINDOW_VISIBILITY_HIDDEN;
1975 if (EqualRect (&clip_rectangle, &handle_rectangle))
1976 handle_visibility = WINDOW_VISIBILITY_FULL;
1978 handle_visibility = WINDOW_VISIBILITY_PARTIAL;
1981 handle_visibility = WINDOW_VISIBILITY_PARTIAL;
1984 handle_visibility = WINDOW_VISIBILITY_ERROR;
1988 if (handle_visibility == WINDOW_VISIBILITY_HIDDEN) {
1989 GST_DEBUG_OBJECT (sink, "Hidden hwnd, skipping frame rendering...");
1993 GST_INFO_OBJECT (sink, "%s %" GST_TIME_FORMAT,
1994 (sink->d3d.window_handle != NULL) ? "Render" : "No Win",
1995 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1997 crop = gst_buffer_get_video_crop_meta (buf);
1999 sink->crop_rect.x = crop->x;
2000 sink->crop_rect.y = crop->y;
2001 sink->crop_rect.w = crop->width;
2002 sink->crop_rect.h = crop->height;
2004 sink->crop_rect.x = 0;
2005 sink->crop_rect.y = 0;
2006 sink->crop_rect.w = sink->info.width;
2007 sink->crop_rect.h = sink->info.height;
2010 /* Resize swapchain if needed */
2011 if (!d3d_resize_swap_chain (sink)) {
2012 ret = GST_FLOW_ERROR;
2016 if (gst_buffer_n_memory (buf) != 1 ||
2017 (mem = gst_buffer_peek_memory (buf, 0)) == 0 ||
2018 !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
2020 GstBufferPoolAcquireParams params = { 0, };
2022 if (!sink->fallback_pool
2023 || !gst_buffer_pool_set_active (sink->fallback_pool, TRUE)) {
2024 ret = GST_FLOW_NOT_NEGOTIATED;
2028 /* take a buffer from our pool, if there is no buffer in the pool something
2029 * is seriously wrong, waiting for the pool here might deadlock when we try
2030 * to go to PAUSED because we never flush the pool. */
2031 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
2032 ret = gst_buffer_pool_acquire_buffer (sink->fallback_pool, &tmp, ¶ms);
2033 if (ret != GST_FLOW_OK)
2036 if (sink->fallback_buffer) {
2037 gst_buffer_unref (sink->fallback_buffer);
2038 sink->fallback_buffer = NULL;
2041 mem = gst_buffer_peek_memory (tmp, 0);
2042 if (!mem || !gst_memory_is_type (mem, GST_D3D_SURFACE_MEMORY_NAME)) {
2043 ret = GST_FLOW_ERROR;
2044 gst_buffer_unref (tmp);
2047 d3d_copy_buffer (sink, buf, tmp);
2050 surface = ((GstD3DSurfaceMemory *) mem)->surface;
2052 /* Need to keep an additional ref until the next buffer
2053 * to make sure it isn't reused until then */
2054 sink->fallback_buffer = buf;
2056 mem = gst_buffer_peek_memory (buf, 0);
2057 surface = ((GstD3DSurfaceMemory *) mem)->surface;
2059 if (sink->fallback_buffer) {
2060 gst_buffer_unref (sink->fallback_buffer);
2061 sink->fallback_buffer = NULL;
2065 if (sink->d3d.surface)
2066 IDirect3DSurface9_Release (sink->d3d.surface);
2067 IDirect3DSurface9_AddRef (surface);
2068 sink->d3d.surface = surface;
2070 if (!d3d_present_swap_chain (sink)) {
2071 ret = GST_FLOW_ERROR;
2081 /* D3D Window Proc Functions */
2083 static LRESULT APIENTRY
2084 d3d_wnd_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2086 GstD3DVideoSink *sink =
2087 (GstD3DVideoSink *) GetProp (hWnd, TEXT ("GstD3DVideoSink"));
2091 /* d3dvideosink object might not available yet.
2092 * The thread for message queue starts earlier than SetProp... */
2094 return DefWindowProc (hWnd, message, wParam, lParam);
2097 proc = sink->d3d.orig_wnd_proc;
2105 ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2106 /* Call this afterwards to ensure that our paint happens last */
2107 d3d_present_swap_chain (sink);
2112 ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2114 /* Don't resize if the window is being minimized. Recreating the
2115 * swap chain will fail if the window is minimized
2117 if (wParam != SIZE_MINIMIZED)
2118 d3d_resize_swap_chain (sink);
2123 if (sink->enable_navigation_events) {
2124 gunichar2 wcrep[128];
2125 if (GetKeyNameTextW (lParam, (LPWSTR) wcrep, 128)) {
2126 gchar *utfrep = g_utf16_to_utf8 (wcrep, 128, NULL, NULL, NULL);
2128 if (message == WM_KEYDOWN)
2129 gst_navigation_send_key_event (GST_NAVIGATION (sink), "key-press",
2131 else if (message == WM_KEYUP)
2132 gst_navigation_send_key_event (GST_NAVIGATION (sink),
2133 "key-release", utfrep);
2139 case WM_LBUTTONDOWN:
2141 case WM_RBUTTONDOWN:
2143 case WM_MBUTTONDOWN:
2146 gdouble x = 0, y = 0;
2147 if (sink->enable_navigation_events
2148 && d3d_get_render_coordinates (sink, LOWORD (lParam), HIWORD (lParam),
2151 const gchar *action = NULL;
2155 action = "mouse-move";
2157 case WM_LBUTTONDOWN:
2159 action = "mouse-button-press";
2163 action = "mouse-button-release";
2165 case WM_RBUTTONDOWN:
2167 action = "mouse-button-press";
2171 action = "mouse-button-release";
2173 case WM_MBUTTONDOWN:
2175 action = "mouse-button-press";
2179 action = "mouse-button-release";
2185 /* GST_DEBUG_OBJECT(sink, "%s: %lfx%lf", action, x, y); */
2186 gst_navigation_send_mouse_event (GST_NAVIGATION (sink), action,
2193 d3d_set_window_handle (sink, 0, FALSE);
2200 ret = CallWindowProc (proc, hWnd, message, wParam, lParam);
2202 ret = DefWindowProc (hWnd, message, wParam, lParam);
2208 /* Internal Window */
2210 static LRESULT APIENTRY
2211 d3d_wnd_proc_internal (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2215 GST_DEBUG ("Internal window: WM_DESTROY");
2216 /* Tell the internal window thread to shut down */
2217 PostQuitMessage (0);
2218 GST_DEBUG ("Posted quit..");
2222 return DefWindowProc (hWnd, message, wParam, lParam);
2226 _d3d_create_internal_window (GstD3DVideoSink * sink)
2228 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2231 DWORD exstyle, style;
2238 * GST_VIDEO_SINK_WIDTH() is the aspect-ratio-corrected size of the video.
2239 * GetSystemMetrics() returns the width of the dialog's border (doubled
2240 * b/c of left and right borders).
2242 width = GST_VIDEO_SINK_WIDTH (sink) + GetSystemMetrics (SM_CXSIZEFRAME) * 2;
2244 GST_VIDEO_SINK_HEIGHT (sink) + GetSystemMetrics (SM_CYCAPTION) +
2245 (GetSystemMetrics (SM_CYSIZEFRAME) * 2);
2247 SystemParametersInfo (SPI_GETWORKAREA, 0, &rect, 0);
2248 screenwidth = rect.right - rect.left;
2249 screenheight = rect.bottom - rect.top;
2253 /* Make it fit into the screen without changing the aspect ratio. */
2254 if (width > screenwidth) {
2255 double ratio = (double) screenwidth / (double) width;
2256 width = screenwidth;
2257 height = (int) (height * ratio);
2260 if (height > screenheight) {
2261 double ratio = (double) screenheight / (double) height;
2262 height = screenheight;
2263 width = (int) (width * ratio);
2266 style = WS_OVERLAPPEDWINDOW; /* Normal top-level window */
2268 video_window = CreateWindowEx (exstyle,
2269 klass->d3d.wnd_class.lpszClassName,
2270 TEXT ("GStreamer D3D video sink (internal window)"),
2271 style, offx, offy, width, height,
2272 NULL, NULL, klass->d3d.wnd_class.hInstance, sink);
2274 if (video_window == NULL) {
2275 GST_ERROR_OBJECT (sink, "Failed to create internal window: %lu",
2280 /* Now show the window, as appropriate */
2281 ShowWindow (video_window, SW_SHOWNORMAL);
2283 /* Trigger the initial paint of the window */
2284 UpdateWindow (video_window);
2286 return video_window;
2291 GstD3DVideoSink *sink;
2296 } D3DInternalWindowDat;
2299 d3d_internal_window_thread (D3DInternalWindowDat * dat)
2301 GstD3DVideoSink *sink;
2305 g_return_val_if_fail (dat != NULL, NULL);
2308 GST_DEBUG_OBJECT (sink, "Entering internal window thread: %p",
2311 /* Create internal window */
2312 g_mutex_lock (&dat->lock);
2313 hWnd = _d3d_create_internal_window (sink);
2315 GST_ERROR_OBJECT (sink, "Failed to create internal window");
2317 g_cond_signal (&dat->cond);
2318 g_mutex_unlock (&dat->lock);
2323 g_cond_signal (&dat->cond);
2324 g_mutex_unlock (&dat->lock);
2327 * Internal window message loop
2330 while (GetMessage (&msg, NULL, 0, 0)) {
2331 if (msg.message == WM_QUIT_THREAD)
2333 TranslateMessage (&msg);
2334 DispatchMessage (&msg);
2338 GST_DEBUG_OBJECT (sink, "Exiting internal window thread: %p",
2344 d3d_create_internal_window (GstD3DVideoSink * sink)
2347 D3DInternalWindowDat dat;
2349 gboolean timeout = FALSE;
2354 g_mutex_init (&dat.lock);
2355 g_cond_init (&dat.cond);
2357 g_mutex_lock (&dat.lock);
2359 g_thread_new ("d3dvideosink-window-thread",
2360 (GThreadFunc) d3d_internal_window_thread, &dat);
2362 g_mutex_unlock (&dat.lock);
2363 GST_ERROR ("Failed to created internal window thread");
2367 sink->internal_window_thread = thread;
2369 end_time = g_get_monotonic_time () + 10 * G_TIME_SPAN_SECOND;
2370 /* Wait 10 seconds for window proc loop to start up */
2371 while (!dat.error && !dat.hWnd) {
2372 if (!g_cond_wait_until (&dat.cond, &dat.lock, end_time)) {
2377 g_mutex_unlock (&dat.lock);
2379 GST_DEBUG_OBJECT (sink, "Created window: %p (error: %d, timeout: %d)",
2380 dat.hWnd, dat.error, timeout);
2384 g_mutex_clear (&dat.lock);
2385 g_cond_clear (&dat.cond);
2391 /* D3D Video Class Methods */
2394 d3d_class_init (GstD3DVideoSink * sink)
2396 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2397 gulong timeout_interval = 10000; /* 10 ms interval */
2398 gulong intervals = (10000000 / timeout_interval); /* 10 secs */
2399 gboolean ret = FALSE;
2402 g_return_val_if_fail (klass != NULL, FALSE);
2404 LOCK_CLASS (sink, klass);
2406 klass->d3d.refs += 1;
2407 GST_DEBUG ("D3D class init [refs:%u]", klass->d3d.refs);
2408 klass->d3d.sink_list = g_list_append (klass->d3d.sink_list, sink);
2410 if (klass->d3d.refs > 1)
2413 WM_D3DVIDEO_NOTIFY_DEVICE_LOST =
2414 RegisterWindowMessage ("WM_D3DVIDEO_NOTIFY_DEVICE_LOST");
2416 klass->d3d.d3d = Direct3DCreate9 (D3D_SDK_VERSION);
2417 if (!klass->d3d.d3d) {
2418 GST_ERROR ("Unable to create Direct3D interface");
2422 /* Register Window Class for internal Windows */
2423 memset (&klass->d3d.wnd_class, 0, sizeof (WNDCLASS));
2424 klass->d3d.wnd_class.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
2425 klass->d3d.wnd_class.hInstance = GetModuleHandle (NULL);
2426 klass->d3d.wnd_class.lpszClassName = TEXT ("GstD3DVideoSinkInternalWindow");
2427 klass->d3d.wnd_class.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
2428 klass->d3d.wnd_class.hCursor = LoadCursor (NULL, IDC_ARROW);
2429 klass->d3d.wnd_class.hIcon = LoadIcon (NULL, IDI_APPLICATION);
2430 klass->d3d.wnd_class.cbClsExtra = 0;
2431 klass->d3d.wnd_class.cbWndExtra = 0;
2432 klass->d3d.wnd_class.lpfnWndProc = d3d_wnd_proc_internal;
2434 if (RegisterClass (&klass->d3d.wnd_class) == 0) {
2435 GST_ERROR ("Failed to register window class: %lu", GetLastError ());
2439 klass->d3d.running = FALSE;
2440 klass->d3d.error_exit = FALSE;
2441 UNLOCK_CLASS (sink, klass);
2443 g_thread_new ("d3dvideosink-window-thread",
2444 (GThreadFunc) d3d_hidden_window_thread, klass);
2445 LOCK_CLASS (sink, klass);
2447 if (!klass->d3d.thread) {
2448 GST_ERROR ("Failed to created hidden window thread");
2452 UNLOCK_CLASS (sink, klass);
2453 /* Wait 10 seconds for window proc loop to start up */
2454 for (i = 0; klass->d3d.running == FALSE && i < intervals; i++) {
2455 g_usleep (timeout_interval);
2457 LOCK_CLASS (sink, klass);
2459 if (klass->d3d.error_exit)
2462 if (!klass->d3d.running) {
2463 GST_ERROR ("Waited %lu ms, window proc loop has not started",
2464 (timeout_interval * intervals) / 1000);
2468 GST_DEBUG ("Hidden window message loop is running..");
2473 UNLOCK_CLASS (sink, klass);
2476 d3d_class_destroy (sink);
2482 d3d_class_destroy (GstD3DVideoSink * sink)
2484 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2486 g_return_if_fail (klass != NULL);
2488 LOCK_CLASS (sink, klass);
2490 klass->d3d.refs -= 1;
2492 GST_DEBUG ("D3D class destroy [refs:%u]", klass->d3d.refs);
2494 klass->d3d.sink_list = g_list_remove (klass->d3d.sink_list, sink);
2496 if (klass->d3d.refs >= 1)
2499 UNLOCK_CLASS (sink, klass);
2501 if (klass->d3d.running) {
2502 GST_DEBUG ("Shutting down window proc thread, waiting to join..");
2503 PostMessage (klass->d3d.hidden_window, WM_QUIT, 0, 0);
2504 g_thread_join (klass->d3d.thread);
2505 GST_DEBUG ("Joined..");
2508 LOCK_CLASS (sink, klass);
2510 if (klass->d3d.d3d) {
2512 ref_count = IDirect3D9_Release (klass->d3d.d3d);
2513 GST_DEBUG ("Direct3D object released. Reference count: %d", ref_count);
2516 UnregisterClass (klass->d3d.wnd_class.lpszClassName,
2517 klass->d3d.wnd_class.hInstance);
2519 memset (&klass->d3d, 0, sizeof (GstD3DDataClass));
2522 UNLOCK_CLASS (sink, klass);
2526 d3d_class_display_device_create (GstD3DVideoSinkClass * klass, UINT adapter)
2529 GstD3DDisplayDevice *device;
2532 D3DDISPLAYMODE disp_mode;
2533 DWORD create_mask = 0;
2535 gboolean ret = FALSE;
2537 g_return_val_if_fail (klass != NULL, FALSE);
2541 LOCK_CLASS (NULL, klass);
2543 d3d = klass->d3d.d3d;
2544 device = &klass->d3d.device;
2545 hwnd = klass->d3d.hidden_window;
2547 memset (&caps, 0, sizeof (caps));
2548 memset (&disp_mode, 0, sizeof (disp_mode));
2549 memset (&device->present_params, 0, sizeof (device->present_params));
2551 device->adapter = adapter;
2553 if (IDirect3D9_GetAdapterDisplayMode (d3d, adapter, &disp_mode) != D3D_OK) {
2554 GST_ERROR ("Unable to request adapter[%u] display mode", adapter);
2558 if (IDirect3D9_GetDeviceCaps (d3d, adapter, D3DDEVTYPE_HAL, &caps) != D3D_OK) {
2559 GST_ERROR ("Unable to request adapter[%u] device caps", adapter);
2563 /* Ask DirectX to please not clobber the FPU state when making DirectX
2564 * API calls. This can cause libraries such as cairo to misbehave in
2565 * certain scenarios.
2567 create_mask = 0 | D3DCREATE_FPU_PRESERVE;
2569 /* Make sure that device access is threadsafe */
2570 create_mask |= D3DCREATE_MULTITHREADED;
2572 /* Determine vertex processing capabilities. Some cards have issues
2573 * using software vertex processing. Courtesy:
2574 * http://www.chadvernon.com/blog/resources/directx9/improved-direct3d-initialization/
2576 if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) ==
2577 D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
2578 create_mask |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
2579 /* if ((d3dcaps.DevCaps & D3DDEVCAPS_PUREDEVICE) == D3DDEVCAPS_PUREDEVICE) */
2580 /* d3dcreate |= D3DCREATE_PUREDEVICE; */
2582 create_mask |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
2585 /* Check the filter type. */
2586 if ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) ==
2587 D3DPTFILTERCAPS_MINFLINEAR
2588 || (caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) ==
2589 D3DPTFILTERCAPS_MAGFLINEAR) {
2590 device->filter_type = D3DTEXF_LINEAR;
2592 device->filter_type = D3DTEXF_NONE;
2595 /* Setup the display mode format. */
2596 device->format = disp_mode.Format;
2598 /* present_params.Flags = D3DPRESENTFLAG_VIDEO; */
2599 device->present_params.Windowed = TRUE;
2600 device->present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
2601 device->present_params.BackBufferCount = 1;
2602 device->present_params.BackBufferFormat = device->format;
2603 device->present_params.BackBufferWidth = 1;
2604 device->present_params.BackBufferHeight = 1;
2605 device->present_params.MultiSampleType = D3DMULTISAMPLE_NONE;
2606 device->present_params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; /* D3DPRESENT_INTERVAL_IMMEDIATE; */
2608 GST_DEBUG ("Creating Direct3D device for hidden window %p", hwnd);
2610 if ((hr = IDirect3D9_CreateDevice (d3d, adapter, D3DDEVTYPE_HAL, hwnd,
2611 create_mask, &device->present_params,
2612 &device->d3d_device)) != D3D_OK) {
2613 GST_ERROR ("Unable to create Direct3D device. Result: %ld (0x%lx)", hr, hr);
2617 GST_DEBUG ("Display Device format: %s",
2618 d3d_format_to_string (disp_mode.Format));
2623 memset (device, 0, sizeof (GstD3DDisplayDevice));
2625 UNLOCK_CLASS (NULL, klass);
2631 d3d_class_display_device_destroy (GstD3DVideoSinkClass * klass)
2633 g_return_if_fail (klass != NULL);
2635 LOCK_CLASS (NULL, klass);
2636 if (klass->d3d.device.d3d_device) {
2638 ref_count = IDirect3DDevice9_Release (klass->d3d.device.d3d_device);
2639 GST_DEBUG ("Direct3D device [adapter:%u] released. Reference count: %d",
2640 klass->d3d.device.adapter, ref_count);
2642 memset (&klass->d3d.device, 0, sizeof (GstD3DDisplayDevice));
2643 UNLOCK_CLASS (NULL, klass);
2647 d3d_class_notify_device_lost (GstD3DVideoSink * sink)
2649 GstD3DVideoSinkClass *klass = GST_D3DVIDEOSINK_GET_CLASS (sink);
2650 PostMessage (klass->d3d.hidden_window, WM_D3DVIDEO_NOTIFY_DEVICE_LOST, 0, 0);
2654 d3d_class_notify_device_lost_all (GstD3DVideoSinkClass * klass)
2656 g_return_if_fail (klass != NULL);
2658 LOCK_CLASS (NULL, klass);
2659 if (!klass->d3d.device_lost) {
2661 klass->d3d.device_lost = TRUE;
2663 GST_DEBUG ("Notifying all instances of device loss");
2665 clst = g_list_copy (klass->d3d.sink_list);
2666 UNLOCK_CLASS (NULL, klass);
2668 for (lst = clst; lst != NULL; lst = lst->next) {
2669 GstD3DVideoSink *sink = (GstD3DVideoSink *) lst->data;
2672 d3d_notify_device_lost (sink);
2675 LOCK_CLASS (NULL, klass);
2677 /* Set timer to try reset at given interval */
2678 SetTimer (klass->d3d.hidden_window, IDT_DEVICE_RESET_TIMER, 500, NULL);
2680 UNLOCK_CLASS (NULL, klass);
2684 d3d_class_reset_display_device (GstD3DVideoSinkClass * klass)
2688 g_return_if_fail (klass != NULL);
2690 LOCK_CLASS (NULL, klass);
2691 hr = IDirect3DDevice9_Reset (klass->d3d.device.d3d_device,
2692 &klass->d3d.device.present_params);
2693 ERROR_CHECK_HR (hr) {
2694 CASE_HR_ERR (D3DERR_DEVICELOST);
2695 CASE_HR_ERR (D3DERR_DEVICEREMOVED);
2696 CASE_HR_ERR (D3DERR_DRIVERINTERNALERROR);
2697 CASE_HR_ERR (D3DERR_OUTOFVIDEOMEMORY);
2698 CASE_HR_DBG_END (NULL, "Attempt device reset.. failed");
2702 GST_INFO ("Attempt device reset.. success");
2704 klass->d3d.device_lost = FALSE;
2705 KillTimer (klass->d3d.hidden_window, IDT_DEVICE_RESET_TIMER);
2707 g_list_foreach (klass->d3d.sink_list, (GFunc) d3d_notify_device_reset, NULL);
2709 UNLOCK_CLASS (NULL, klass);
2712 /* Hidden Window Loop Thread */
2714 static LRESULT APIENTRY
2715 D3DHiddenWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
2720 case IDT_DEVICE_RESET_TIMER:
2721 d3d_class_reset_display_device ((GstD3DVideoSinkClass *)
2722 GetWindowLongPtr (hWnd, GWLP_USERDATA));
2728 PostQuitMessage (0);
2732 if (message == WM_D3DVIDEO_NOTIFY_DEVICE_LOST) {
2733 d3d_class_notify_device_lost_all ((GstD3DVideoSinkClass *)
2734 GetWindowLongPtr (hWnd, GWLP_USERDATA));
2739 return DefWindowProc (hWnd, message, wParam, lParam);
2743 d3d_hidden_window_thread (GstD3DVideoSinkClass * klass)
2746 gboolean reged = FALSE;
2748 gboolean ret = FALSE;
2750 g_return_val_if_fail (klass != NULL, FALSE);
2752 memset (&WndClass, 0, sizeof (WNDCLASS));
2753 WndClass.hInstance = GetModuleHandle (NULL);
2754 WndClass.lpszClassName = TEXT ("gstd3dvideo-hidden-window-class");
2755 WndClass.lpfnWndProc = D3DHiddenWndProc;
2757 if (!RegisterClass (&WndClass)) {
2758 GST_ERROR ("Unable to register Direct3D hidden window class");
2763 hWnd = CreateWindowEx (0,
2764 WndClass.lpszClassName,
2765 TEXT ("GStreamer Direct3D hidden window"),
2766 WS_POPUP, 0, 0, 1, 1, HWND_MESSAGE, NULL, WndClass.hInstance, klass);
2769 GST_ERROR ("Failed to create Direct3D hidden window");
2773 GST_DEBUG ("Direct3D hidden window handle: %p", hWnd);
2775 klass->d3d.hidden_window = hWnd;
2777 /* TODO: Multi-monitor setup? */
2778 if (!d3d_class_display_device_create (klass, D3DADAPTER_DEFAULT)) {
2779 GST_ERROR ("Failed to initiazlize adapter: %u", D3DADAPTER_DEFAULT);
2783 /* Attach data to window */
2784 SetWindowLongPtr (hWnd, GWLP_USERDATA, (LONG_PTR) klass);
2786 GST_DEBUG ("Entering Direct3D hidden window message loop");
2788 klass->d3d.running = TRUE;
2790 /* Hidden Window Message Loop */
2793 while (GetMessage (&msg, NULL, 0, 0)) {
2794 TranslateMessage (&msg);
2795 DispatchMessage (&msg);
2797 if (msg.message == WM_QUIT || msg.message == WM_CLOSE)
2801 klass->d3d.running = FALSE;
2803 GST_DEBUG ("Leaving Direct3D hidden window message loop");
2809 klass->d3d.error_exit = TRUE;
2811 PostMessage (hWnd, WM_DESTROY, 0, 0);
2812 DestroyWindow (hWnd);
2813 klass->d3d.hidden_window = 0;
2816 UnregisterClass (WndClass.lpszClassName, WndClass.hInstance);
2817 d3d_class_display_device_destroy (klass);