summaryrefslogtreecommitdiffstats
path: root/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c')
-rw-r--r--tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c544
1 files changed, 544 insertions, 0 deletions
diff --git a/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c b/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c
new file mode 100644
index 0000000..8a81b66
--- /dev/null
+++ b/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c
@@ -0,0 +1,544 @@
+/* Copyright (C) 2014 Mamadou DIOP
+*
+* This file is part of Open Source Doubango Framework.
+*
+* DOUBANGO is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* DOUBANGO is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with DOUBANGO.
+*/
+#include "tinydav/video/gdi/tdav_consumer_video_gdi.h"
+
+#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT
+
+#include <windows.h>
+
+#include "tsk_memory.h"
+#include "tsk_string.h"
+#include "tsk_safeobj.h"
+#include "tsk_debug.h"
+
+#define CHECK_HR(x) { HRESULT __hr__ = (x); if (FAILED(__hr__)) { TSK_DEBUG_ERROR("Operation Failed (%08x)", __hr__); goto bail; } }
+
+static HRESULT HookWindow(struct tdav_consumer_video_gdi_s *p_gdi, HWND hWnd, BOOL bFullScreenWindow);
+static HRESULT UnhookWindow(struct tdav_consumer_video_gdi_s *p_gdi, BOOL bFullScreenWindow);
+static HRESULT SetFullscreen(struct tdav_consumer_video_gdi_s *p_gdi, BOOL bFullScreen);
+static HWND CreateFullScreenWindow(struct tdav_consumer_video_gdi_s *p_gdi);
+static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+typedef struct tdav_consumer_video_gdi_s
+{
+ TMEDIA_DECLARE_CONSUMER;
+
+ BOOL bStarted, bPrepared, bPaused, bFullScreen, bWindowHooked, bWindowHookedFullScreen;
+ HWND hWindow;
+ WNDPROC wndProc;
+ HWND hWindowFullScreen;
+ WNDPROC wndProcFullScreen;
+ BITMAPINFO bitmapInfo;
+ void* pBuffer;
+
+ TSK_DECLARE_SAFEOBJ;
+}
+tdav_consumer_video_gdi_t;
+
+
+
+/* ============ Media Consumer Interface ================= */
+static int tdav_consumer_video_gdi_set(tmedia_consumer_t *self, const tmedia_param_t* param)
+{
+ int ret = 0;
+ tdav_consumer_video_gdi_t* p_gdi = (tdav_consumer_video_gdi_t*)self;
+ HRESULT hr = S_OK;
+
+ if (!self || !param) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ CHECK_HR(hr = E_POINTER);
+ }
+
+ if (param->value_type == tmedia_pvt_int64) {
+ if (tsk_striequals(param->key, "remote-hwnd")) {
+ HWND hWnd = ((HWND)*((int64_t*)param->value));
+ if (hWnd != p_gdi->hWindow) {
+ tsk_safeobj_lock(p_gdi); // block consumer thread
+ UnhookWindow(p_gdi, FALSE);
+ p_gdi->hWindow = hWnd;
+ tsk_safeobj_unlock(p_gdi); // unblock consumer thread
+ }
+ }
+ }
+ else if(param->value_type == tmedia_pvt_int32) {
+ if(tsk_striequals(param->key, "fullscreen")) {
+ BOOL bFullScreen = !!*((int32_t*)param->value);
+ TSK_DEBUG_INFO("[GDI video consumer] Full Screen = %d", bFullScreen);
+ CHECK_HR(hr = SetFullscreen(p_gdi, bFullScreen));
+ }
+ }
+
+ CHECK_HR(hr);
+
+bail:
+ return SUCCEEDED(hr) ? 0 : -1;
+}
+
+
+static int tdav_consumer_video_gdi_prepare(tmedia_consumer_t* self, const tmedia_codec_t* codec)
+{
+ tdav_consumer_video_gdi_t* p_gdi = (tdav_consumer_video_gdi_t*)self;
+
+ if (!p_gdi || !codec && codec->plugin) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ TMEDIA_CONSUMER(p_gdi)->video.fps = TMEDIA_CODEC_VIDEO(codec)->in.fps;
+ TMEDIA_CONSUMER(p_gdi)->video.in.width = TMEDIA_CODEC_VIDEO(codec)->in.width;
+ TMEDIA_CONSUMER(p_gdi)->video.in.height = TMEDIA_CODEC_VIDEO(codec)->in.height;
+
+ if (!TMEDIA_CONSUMER(p_gdi)->video.display.width) {
+ TMEDIA_CONSUMER(p_gdi)->video.display.width = TMEDIA_CONSUMER(p_gdi)->video.in.width;
+ }
+ if (!TMEDIA_CONSUMER(p_gdi)->video.display.height) {
+ TMEDIA_CONSUMER(p_gdi)->video.display.height = TMEDIA_CONSUMER(p_gdi)->video.in.height;
+ }
+
+ ZeroMemory(&p_gdi->bitmapInfo, sizeof(p_gdi->bitmapInfo));
+ p_gdi->bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ p_gdi->bitmapInfo.bmiHeader.biPlanes = 1;
+ p_gdi->bitmapInfo.bmiHeader.biBitCount = 24; // RGB24
+ p_gdi->bitmapInfo.bmiHeader.biCompression = BI_RGB;
+ p_gdi->bitmapInfo.bmiHeader.biWidth = (LONG)TMEDIA_CONSUMER(p_gdi)->video.in.width;
+ p_gdi->bitmapInfo.bmiHeader.biHeight = (LONG)(TMEDIA_CONSUMER(p_gdi)->video.in.height * -1);
+ p_gdi->bitmapInfo.bmiHeader.biSizeImage = (DWORD)(TMEDIA_CONSUMER(p_gdi)->video.in.width * abs((int)TMEDIA_CONSUMER(p_gdi)->video.in.height) *
+ (p_gdi->bitmapInfo.bmiHeader.biBitCount >> 3));
+
+ return 0;
+}
+
+static int tdav_consumer_video_gdi_start(tmedia_consumer_t* self)
+{
+ tdav_consumer_video_gdi_t* p_gdi = (tdav_consumer_video_gdi_t*)self;
+
+ if (!p_gdi) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_safeobj_lock(p_gdi);
+
+ p_gdi->bPaused = FALSE;
+ p_gdi->bStarted = TRUE;
+
+ tsk_safeobj_unlock(p_gdi);
+
+ return 0;
+}
+
+static int tdav_consumer_video_gdi_consume(tmedia_consumer_t* self, const void* buffer, tsk_size_t size, const tsk_object_t* proto_hdr)
+{
+ tdav_consumer_video_gdi_t* p_gdi = (tdav_consumer_video_gdi_t*)self;
+ int ret = 0;
+ HWND* p_Window;
+ BOOL *p_bWindowHooked, bImputSizeChanged;
+
+ if (!p_gdi) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_safeobj_lock(p_gdi);
+
+ if (!p_gdi->bStarted || p_gdi->bPaused) {
+ TSK_DEBUG_INFO("GDI consumer stopped or paused");
+ goto bail;
+ }
+
+ bImputSizeChanged = (size != p_gdi->bitmapInfo.bmiHeader.biSizeImage)
+ || (TMEDIA_CONSUMER(p_gdi)->video.in.width != p_gdi->bitmapInfo.bmiHeader.biWidth)
+ || (TMEDIA_CONSUMER(p_gdi)->video.in.height != TSK_ABS(p_gdi->bitmapInfo.bmiHeader.biHeight));
+
+ if (bImputSizeChanged) {
+ tsk_size_t xNewSize = TMEDIA_CONSUMER(p_gdi)->video.in.width * TMEDIA_CONSUMER(p_gdi)->video.in.height * (p_gdi->bitmapInfo.bmiHeader.biBitCount >> 3);
+ TSK_DEBUG_INFO("GDI input size changed: %u->%u", p_gdi->bitmapInfo.bmiHeader.biSizeImage, size);
+ if (xNewSize != size) {
+ TSK_DEBUG_ERROR("GDI consumer: chroma issue?");
+ ret = -1;
+ goto bail;
+ }
+ p_gdi->bitmapInfo.bmiHeader.biWidth = (LONG)TMEDIA_CONSUMER(p_gdi)->video.in.width;
+ p_gdi->bitmapInfo.bmiHeader.biHeight = (LONG)TMEDIA_CONSUMER(p_gdi)->video.in.height * -1;
+ p_gdi->bitmapInfo.bmiHeader.biSizeImage = (DWORD)xNewSize;
+ p_gdi->pBuffer = tsk_realloc(p_gdi->pBuffer, p_gdi->bitmapInfo.bmiHeader.biSizeImage);
+ }
+
+ p_Window = p_gdi->bFullScreen ? &p_gdi->hWindowFullScreen : &p_gdi->hWindow;
+ p_bWindowHooked = p_gdi->bFullScreen ? &p_gdi->bWindowHookedFullScreen : &p_gdi->bWindowHooked;
+
+ if (*p_Window) {
+ if (!*p_bWindowHooked) {
+ // Do not hook "hWnd" as it could be the fullscreen handle which is always hooked.
+ CHECK_HR(HookWindow(p_gdi, *p_Window, p_gdi->bFullScreen));
+ }
+ if (!p_gdi->pBuffer) {
+ p_gdi->pBuffer = tsk_realloc(p_gdi->pBuffer, p_gdi->bitmapInfo.bmiHeader.biSizeImage);
+ }
+ if (p_gdi->pBuffer) {
+ memcpy(p_gdi->pBuffer, buffer, size);
+ InvalidateRect(*p_Window, NULL, TRUE);
+ }
+ }
+
+bail:
+ tsk_safeobj_unlock(p_gdi);
+ return ret;
+}
+
+static int tdav_consumer_video_gdi_pause(tmedia_consumer_t* self)
+{
+ tdav_consumer_video_gdi_t* p_gdi = (tdav_consumer_video_gdi_t*)self;
+
+ if (!p_gdi) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_safeobj_lock(p_gdi);
+
+ p_gdi->bPaused = TRUE;
+
+ tsk_safeobj_unlock(p_gdi);
+
+ return 0;
+}
+
+static int tdav_consumer_video_gdi_stop(tmedia_consumer_t* self)
+{
+ tdav_consumer_video_gdi_t* p_gdi = (tdav_consumer_video_gdi_t*)self;
+
+ if (!p_gdi) {
+ TSK_DEBUG_ERROR("Invalid parameter");
+ return -1;
+ }
+
+ tsk_safeobj_lock(p_gdi);
+
+ p_gdi->bStarted = FALSE;
+ p_gdi->bPaused = FALSE;
+ SetFullscreen(p_gdi, FALSE);
+ UnhookWindow(p_gdi, TRUE);
+ UnhookWindow(p_gdi, FALSE);
+
+ tsk_safeobj_unlock(p_gdi);
+
+ return 0;
+}
+
+
+static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_CREATE:
+ case WM_SIZE:
+ case WM_MOVE:
+ {
+ struct tdav_consumer_video_gdi_s* p_gdi = ((struct tdav_consumer_video_gdi_s*)GetProp(hWnd, TEXT("Self")));
+ if (p_gdi) {
+
+ }
+ break;
+ }
+
+ case WM_PAINT:
+ {
+ struct tdav_consumer_video_gdi_s* p_gdi = ((struct tdav_consumer_video_gdi_s*)GetProp(hWnd, TEXT("Self")));
+ if (p_gdi) {
+ tsk_safeobj_lock(p_gdi);
+
+ if (p_gdi->bStarted && !p_gdi->bPaused && p_gdi->pBuffer) {
+ PAINTSTRUCT ps;
+ HDC hdc;
+ RECT rc, logical_rect;
+ int height, width, i, x, y;
+ HDC dc_mem, all_dc[2];
+ HBITMAP bmp_mem;
+ HGDIOBJ bmp_old;
+ POINT logical_area;
+ HBRUSH brush;
+
+ if (!(hdc = BeginPaint(hWnd, &ps))) {
+ goto paint_done;
+ }
+
+ if (!GetClientRect(hWnd, &rc)) {
+ EndPaint(hWnd, &ps);
+ goto paint_done;
+ }
+
+ height = abs(p_gdi->bitmapInfo.bmiHeader.biHeight);
+ width = p_gdi->bitmapInfo.bmiHeader.biWidth;
+
+ dc_mem = CreateCompatibleDC(ps.hdc);
+ SetStretchBltMode(dc_mem, HALFTONE);
+
+ // Set the map mode so that the ratio will be maintained for us.
+ all_dc[0] = ps.hdc, all_dc[1] = dc_mem;
+ for (i = 0; i < sizeof(all_dc)/sizeof(all_dc[0]); ++i) {
+#if !TDAV_UNDER_WINDOWS_CE
+ SetMapMode(all_dc[i], MM_ISOTROPIC);
+ SetWindowExtEx(all_dc[i], width, height, NULL);
+ SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL);
+#endif
+ }
+
+ bmp_mem = CreateCompatibleBitmap(ps.hdc, rc.right, rc.bottom);
+ bmp_old = SelectObject(dc_mem, bmp_mem);
+
+ logical_area.x = rc.right, logical_area.y = rc.bottom;
+#if !TDAV_UNDER_WINDOWS_CE
+ DPtoLP(ps.hdc, &logical_area, 1);
+#endif
+
+ brush = CreateSolidBrush(RGB(0, 0, 0));
+ logical_rect.left = 0, logical_rect.top = 0, logical_rect.right = logical_area.x, logical_rect.bottom = logical_area.y;
+ FillRect(dc_mem, &logical_rect, brush);
+ DeleteObject(brush);
+
+ x = (logical_area.x / 2) - (width / 2);
+ y = (logical_area.y / 2) - (height / 2);
+
+ StretchDIBits(dc_mem, x, y, width, height,
+ 0, 0, width, height, p_gdi->pBuffer, &p_gdi->bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
+
+ BitBlt(ps.hdc, 0, 0, logical_area.x, logical_area.y,
+ dc_mem, 0, 0, SRCCOPY);
+
+ // Cleanup.
+ SelectObject(dc_mem, bmp_old);
+ DeleteObject(bmp_mem);
+ DeleteDC(dc_mem);
+
+ EndPaint(hWnd, &ps);
+ }
+paint_done:
+ tsk_safeobj_unlock(p_gdi);
+ }
+ break;
+ }
+
+ case WM_ERASEBKGND:
+ {
+ return TRUE; // avoid background erasing.
+ }
+
+ case WM_CHAR:
+ case WM_KEYUP:
+ {
+ struct tdav_consumer_video_gdi_s* p_gdi = ((struct tdav_consumer_video_gdi_s*)GetProp(hWnd, TEXT("Self")));
+ if (p_gdi) {
+ SetFullscreen(p_gdi, FALSE);
+ }
+
+ break;
+ }
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+static HRESULT HookWindow(struct tdav_consumer_video_gdi_s *p_gdi, HWND hWnd, BOOL bFullScreenWindow)
+{
+ HRESULT hr = S_OK;
+ HWND* p_Window = bFullScreenWindow ? &p_gdi->hWindowFullScreen : &p_gdi->hWindow;
+ WNDPROC* p_wndProc = bFullScreenWindow ? &p_gdi->wndProcFullScreen : &p_gdi->wndProc;
+ BOOL* p_bWindowHooked = bFullScreenWindow ? &p_gdi->bWindowHookedFullScreen : &p_gdi->bWindowHooked;
+
+ tsk_safeobj_lock(p_gdi);
+
+ CHECK_HR(hr = UnhookWindow(p_gdi, bFullScreenWindow));
+
+ if ((*p_Window = hWnd)) {
+#if TDAV_UNDER_WINDOWS_CE
+ *p_wndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc);
+#else
+ *p_wndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc);
+#endif
+ if (!*p_wndProc) {
+ TSK_DEBUG_ERROR("HookWindowLongPtr() failed with errcode=%d", GetLastError());
+ CHECK_HR(hr = E_FAIL);
+ }
+ *p_bWindowHooked = TRUE;
+ SetProp(*p_Window, TEXT("Self"), p_gdi);
+ }
+bail:
+ tsk_safeobj_unlock(p_gdi);
+ return S_OK;
+}
+
+static HRESULT UnhookWindow(struct tdav_consumer_video_gdi_s *p_gdi, BOOL bFullScreenWindow)
+{
+ HWND* p_Window = bFullScreenWindow ? &p_gdi->hWindowFullScreen : &p_gdi->hWindow;
+ WNDPROC* p_wndProc = bFullScreenWindow ? &p_gdi->wndProcFullScreen : &p_gdi->wndProc;
+ BOOL* p_bWindowHooked = bFullScreenWindow ? &p_gdi->bWindowHookedFullScreen : &p_gdi->bWindowHooked;
+
+ tsk_safeobj_lock(p_gdi);
+ if (*p_Window && *p_wndProc) {
+#if TDAV_UNDER_WINDOWS_CE
+ SetWindowLong(*p_Window, GWL_WNDPROC, (LONG)*p_wndProc);
+#else
+ SetWindowLongPtr(*p_Window, GWLP_WNDPROC, (LONG_PTR)*p_wndProc);
+#endif
+ *p_wndProc = NULL;
+ }
+ if (*p_Window) {
+ if (p_gdi->pBuffer) {
+ memset(p_gdi->pBuffer, 0, p_gdi->bitmapInfo.bmiHeader.biSizeImage);
+ }
+ InvalidateRect(*p_Window, NULL, FALSE);
+ }
+ *p_bWindowHooked = FALSE;
+ tsk_safeobj_unlock(p_gdi);
+ return S_OK;
+}
+
+static HRESULT SetFullscreen(struct tdav_consumer_video_gdi_s *p_gdi, BOOL bFullScreen)
+{
+ HRESULT hr = S_OK;
+ if (!p_gdi) {
+ CHECK_HR(hr = E_POINTER);
+ }
+
+ if (p_gdi->bFullScreen != bFullScreen) {
+ tsk_safeobj_lock(p_gdi);
+ if (bFullScreen) {
+ HWND hWnd = CreateFullScreenWindow(p_gdi);
+ if (hWnd) {
+#if TDAV_UNDER_WINDOWS_CE
+ ShowWindow(hWnd, SW_SHOWNORMAL);
+#else
+ ShowWindow(hWnd, SW_SHOWDEFAULT);
+#endif
+ UpdateWindow(hWnd);
+ HookWindow(p_gdi, hWnd, TRUE);
+ }
+ }
+ else if(p_gdi->hWindowFullScreen) {
+ ShowWindow(p_gdi->hWindowFullScreen, SW_HIDE);
+ UnhookWindow(p_gdi, TRUE);
+ }
+ p_gdi->bFullScreen = bFullScreen;
+ tsk_safeobj_unlock(p_gdi);
+
+ CHECK_HR(hr);
+ }
+
+bail:
+ return hr;
+}
+
+static HWND CreateFullScreenWindow(struct tdav_consumer_video_gdi_s *p_gdi)
+{
+ HRESULT hr = S_OK;
+
+ if(!p_gdi) {
+ return NULL;
+ }
+
+ if (!p_gdi->hWindowFullScreen) {
+ WNDCLASS wc = {0};
+
+ wc.lpfnWndProc = WndProc;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.lpszClassName = L"WindowClass";
+ RegisterClass(&wc);
+ p_gdi->hWindowFullScreen = CreateWindowEx(
+ 0,
+ wc.lpszClassName,
+ L"Doubango's Video Consumer Fullscreen",
+ WS_EX_TOPMOST | WS_POPUP,
+ 0, 0,
+ GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
+ NULL,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL);
+
+ SetProp(p_gdi->hWindowFullScreen, TEXT("Self"), p_gdi);
+ }
+ return p_gdi->hWindowFullScreen;
+}
+
+//
+// GDI video consumer object definition
+//
+/* constructor */
+static tsk_object_t* tdav_consumer_video_gdi_ctor(tsk_object_t * self, va_list * app)
+{
+ tdav_consumer_video_gdi_t *p_gdi = (tdav_consumer_video_gdi_t *)self;
+ if (p_gdi) {
+ /* init base */
+ tmedia_consumer_init(TMEDIA_CONSUMER(p_gdi));
+ TMEDIA_CONSUMER(p_gdi)->video.display.chroma = tmedia_chroma_bgr24;
+
+ /* init self */
+ TMEDIA_CONSUMER(p_gdi)->video.fps = 15;
+ TMEDIA_CONSUMER(p_gdi)->video.display.width = 352;
+ TMEDIA_CONSUMER(p_gdi)->video.display.height = 288;
+ TMEDIA_CONSUMER(p_gdi)->video.display.auto_resize = tsk_true;
+ tsk_safeobj_init(p_gdi);
+ }
+ return self;
+}
+/* destructor */
+static tsk_object_t* tdav_consumer_video_gdi_dtor(tsk_object_t * self)
+{
+ tdav_consumer_video_gdi_t *p_gdi = (tdav_consumer_video_gdi_t *)self;
+ if (p_gdi) {
+ /* stop */
+ tdav_consumer_video_gdi_stop((tmedia_consumer_t*)self);
+
+ /* deinit base */
+ tmedia_consumer_deinit(TMEDIA_CONSUMER(p_gdi));
+ /* deinit self */
+ TSK_FREE(p_gdi->pBuffer);
+ tsk_safeobj_deinit(p_gdi);
+ }
+
+ return self;
+}
+/* object definition */
+static const tsk_object_def_t tdav_consumer_video_gdi_def_s =
+{
+ sizeof(tdav_consumer_video_gdi_t),
+ tdav_consumer_video_gdi_ctor,
+ tdav_consumer_video_gdi_dtor,
+ tsk_null,
+};
+/* plugin definition*/
+static const tmedia_consumer_plugin_def_t tdav_consumer_video_gdi_plugin_def_s =
+{
+ &tdav_consumer_video_gdi_def_s,
+
+ tmedia_video,
+ "Microsoft GDI consumer (using custom source)",
+
+ tdav_consumer_video_gdi_set,
+ tdav_consumer_video_gdi_prepare,
+ tdav_consumer_video_gdi_start,
+ tdav_consumer_video_gdi_consume,
+ tdav_consumer_video_gdi_pause,
+ tdav_consumer_video_gdi_stop
+};
+const tmedia_consumer_plugin_def_t *tdav_consumer_video_gdi_plugin_def_t = &tdav_consumer_video_gdi_plugin_def_s;
+
+#endif /* TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT */
+
OpenPOWER on IntegriCloud