diff --git a/branches/2.0/doubango/plugins/pluginDirectShow/dllmain_dshow.cxx b/branches/2.0/doubango/plugins/pluginDirectShow/dllmain_dshow.cxx index b2b176b9..2b3fcba6 100644 --- a/branches/2.0/doubango/plugins/pluginDirectShow/dllmain_dshow.cxx +++ b/branches/2.0/doubango/plugins/pluginDirectShow/dllmain_dshow.cxx @@ -23,6 +23,8 @@ #include "tsk_plugin.h" #include "tsk_debug.h" +#include "internals/DSUtils.h" + #include #if !defined(ENABLE_SCREENCAST) @@ -87,7 +89,7 @@ tsk_plugin_def_type_t __plugin_get_def_type_at(int index) { switch(index){ case PLUGIN_INDEX_VIDEO_CONSUMER: - return tsk_plugin_def_type_consumer; + return IsD3D9Supported() ? tsk_plugin_def_type_consumer : tsk_plugin_def_type_none; case PLUGIN_INDEX_VIDEO_PRODUCER: #if ENABLE_SCREENCAST case PLUGIN_INDEX_SCREENCAST_PRODUCER: @@ -105,6 +107,9 @@ tsk_plugin_def_media_type_t __plugin_get_def_media_type_at(int index) { switch(index){ case PLUGIN_INDEX_VIDEO_CONSUMER: + { + return IsD3D9Supported() ? tsk_plugin_def_media_type_video : tsk_plugin_def_media_type_none; + } case PLUGIN_INDEX_VIDEO_PRODUCER: { return tsk_plugin_def_media_type_video; @@ -128,7 +133,7 @@ tsk_plugin_def_ptr_const_t __plugin_get_def_at(int index) switch(index){ case PLUGIN_INDEX_VIDEO_CONSUMER: { - return plugin_video_dshow_consumer_plugin_def_t; + return IsD3D9Supported() ? plugin_video_dshow_consumer_plugin_def_t : tsk_null; } case PLUGIN_INDEX_VIDEO_PRODUCER: { diff --git a/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.cxx b/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.cxx index bd23a900..bf8938a8 100644 --- a/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.cxx +++ b/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.cxx @@ -19,6 +19,7 @@ #include #include +#include #include "tsk_debug.h" @@ -46,6 +47,74 @@ bool IsMainThread() return false; } +bool IsD3D9Supported() +{ + static bool g_bChecked = false; + static bool g_bSupported = false; + + if (g_bChecked) { + return g_bSupported; + } + g_bChecked = true; + HRESULT hr = S_OK; + IDirect3D9* pD3D = NULL; + D3DDISPLAYMODE mode = { 0 }; + D3DPRESENT_PARAMETERS pp = {0}; + IDirect3DDevice9* pDevice = NULL; + + if (!(pD3D = Direct3DCreate9(D3D_SDK_VERSION))) { + hr = E_OUTOFMEMORY; + goto bail; + } + + hr = pD3D->GetAdapterDisplayMode( + D3DADAPTER_DEFAULT, + &mode + ); + if (FAILED(hr)) { + goto bail; + } + + hr = pD3D->CheckDeviceType( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mode.Format, + D3DFMT_X8R8G8B8, + TRUE // windowed + ); + if (FAILED(hr)) { + goto bail; + } + pp.BackBufferFormat = D3DFMT_X8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + pp.Windowed = TRUE; + pp.hDeviceWindow = GetDesktopWindow(); + hr = pD3D->CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + pp.hDeviceWindow, + D3DCREATE_HARDWARE_VERTEXPROCESSING, + &pp, + &pDevice + ); + if (FAILED(hr)) { + goto bail; + } + + // Everythings is OK + g_bSupported = TRUE; + TSK_DEBUG_INFO("D3D9 supported"); + +bail: + if (!g_bSupported) { + TSK_DEBUG_WARN("D3D9 not supported"); + } + SAFE_RELEASE(pDevice); + SAFE_RELEASE(pD3D); + return g_bSupported; +} + IPin *GetPin(IBaseFilter *filter, PIN_DIRECTION direction) { IEnumPins *enumPins = NULL; diff --git a/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.h b/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.h index 6374d383..527196bd 100644 --- a/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.h +++ b/branches/2.0/doubango/plugins/pluginDirectShow/internals/DSUtils.h @@ -64,6 +64,8 @@ HWND GetMainWindow(); bool IsMainThread(); +bool IsD3D9Supported(); + IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION dir); HRESULT ConnectFilters(IGraphBuilder *graphBuilder, IBaseFilter *source, IBaseFilter *destination, AM_MEDIA_TYPE *mediaType = NULL); diff --git a/branches/2.0/doubango/plugins/pluginWinMF/dllmain_mf.cxx b/branches/2.0/doubango/plugins/pluginWinMF/dllmain_mf.cxx index ad2d42be..320268e3 100644 --- a/branches/2.0/doubango/plugins/pluginWinMF/dllmain_mf.cxx +++ b/branches/2.0/doubango/plugins/pluginWinMF/dllmain_mf.cxx @@ -132,9 +132,12 @@ tsk_plugin_def_type_t __plugin_get_def_type_at(int index) #endif #if PLUGIN_MF_ENABLE_VIDEO_IO case PLUGIN_INDEX_VIDEO_CONSUMER: + { + return MFUtils::IsD3D9Supported() ? tsk_plugin_def_type_consumer : tsk_plugin_def_type_none; + } case PLUGIN_INDEX_VIDEO_PRODUCER: { - return (index == PLUGIN_INDEX_VIDEO_CONSUMER) ? tsk_plugin_def_type_consumer : tsk_plugin_def_type_producer; + return tsk_plugin_def_type_producer; } #endif #if PLUGIN_MF_ENABLE_VIDEO_CONVERTER @@ -168,6 +171,9 @@ tsk_plugin_def_media_type_t __plugin_get_def_media_type_at(int index) #endif #if PLUGIN_MF_ENABLE_VIDEO_IO case PLUGIN_INDEX_VIDEO_CONSUMER: + { + return MFUtils::IsD3D9Supported() ? tsk_plugin_def_media_type_video : tsk_plugin_def_media_type_none; + } case PLUGIN_INDEX_VIDEO_PRODUCER: { return tsk_plugin_def_media_type_video; @@ -202,7 +208,7 @@ tsk_plugin_def_ptr_const_t __plugin_get_def_at(int index) } case PLUGIN_INDEX_VIDEO_CONSUMER: { - return plugin_win_mf_consumer_video_plugin_def_t; + return MFUtils::IsD3D9Supported() ? plugin_win_mf_consumer_video_plugin_def_t : tsk_null; } #endif #if PLUGIN_MF_ENABLE_AUDIO_IO diff --git a/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.cxx b/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.cxx index d69bfe63..eda2f409 100644 --- a/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.cxx +++ b/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.cxx @@ -25,12 +25,14 @@ #include #include +#include #include #ifdef _MSC_VER #pragma comment(lib, "strmiids.lib") #pragma comment(lib, "wmcodecdspuuid.lib") +#pragma comment(lib, "d3d9") #endif #if !defined(PLUGIN_MF_DISABLE_CODECS) @@ -60,6 +62,9 @@ DWORD MFUtils::g_dwMinorVersion = -1; BOOL MFUtils::g_bLowLatencyH264Checked = FALSE; BOOL MFUtils::g_bLowLatencyH264Supported = FALSE; +BOOL MFUtils::g_bD3D9Checked = FALSE; +BOOL MFUtils::g_bD3D9Supported = FALSE; + const TOPOID MFUtils::g_ullTopoIdSinkMain = 111; const TOPOID MFUtils::g_ullTopoIdSinkPreview = 222; const TOPOID MFUtils::g_ullTopoIdSource = 333; @@ -134,6 +139,77 @@ HRESULT MFUtils::Shutdown() return S_OK; } +BOOL MFUtils::IsD3D9Supported() +{ + if (MFUtils::g_bD3D9Checked) + { + return MFUtils::g_bD3D9Supported; + } + MFUtils::g_bD3D9Checked = TRUE; + HRESULT hr = S_OK; + IDirect3D9* pD3D = NULL; + D3DDISPLAYMODE mode = { 0 }; + D3DPRESENT_PARAMETERS pp = {0}; + IDirect3DDevice9* pDevice = NULL; + + CHECK_HR(hr = MFUtils::Startup()); + + if (!(pD3D = Direct3DCreate9(D3D_SDK_VERSION))) + { + CHECK_HR(hr = E_OUTOFMEMORY); + } + + hr = pD3D->GetAdapterDisplayMode( + D3DADAPTER_DEFAULT, + &mode + ); + if (FAILED(hr)) + { + goto bail; + } + + hr = pD3D->CheckDeviceType( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mode.Format, + D3DFMT_X8R8G8B8, + TRUE // windowed + ); + if (FAILED(hr)) + { + goto bail; + } + pp.BackBufferFormat = D3DFMT_X8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + pp.Windowed = TRUE; + pp.hDeviceWindow = GetDesktopWindow(); + hr = pD3D->CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + pp.hDeviceWindow, + D3DCREATE_HARDWARE_VERTEXPROCESSING, + &pp, + &pDevice + ); + if (FAILED(hr)) + { + goto bail; + } + + // Everythings is OK + MFUtils::g_bD3D9Supported = TRUE; + TSK_DEBUG_INFO("D3D9 supported"); + +bail: + if (!MFUtils::g_bD3D9Supported) { + TSK_DEBUG_WARN("D3D9 not supported"); + } + SafeRelease(&pDevice); + SafeRelease(&pD3D); + return MFUtils::g_bD3D9Supported; +} + BOOL MFUtils::IsLowLatencyH264Supported() { if(MFUtils::g_bLowLatencyH264Checked) diff --git a/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.h b/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.h index 0bfab294..644bfb93 100644 --- a/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.h +++ b/branches/2.0/doubango/plugins/pluginWinMF/internals/mf_utils.h @@ -55,6 +55,7 @@ public: static HRESULT Startup(); static HRESULT Shutdown(); +static BOOL IsD3D9Supported(); static BOOL IsLowLatencyH264Supported(); static HRESULT IsAsyncMFT( @@ -244,6 +245,9 @@ private: static BOOL g_bLowLatencyH264Checked; static BOOL g_bLowLatencyH264Supported; + static BOOL g_bD3D9Checked; + static BOOL g_bD3D9Supported; + public: static const TOPOID g_ullTopoIdSinkMain; static const TOPOID g_ullTopoIdSinkPreview; diff --git a/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_consumer_video.cxx b/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_consumer_video.cxx index 5e3df029..9faa9914 100644 --- a/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_consumer_video.cxx +++ b/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_consumer_video.cxx @@ -137,9 +137,7 @@ static int plugin_win_mf_consumer_video_set(tmedia_consumer_t *self, const tmedi hr = ResetDevice(pSelf); } tsk_safeobj_unlock(pSelf); // unblock consumer thread - } - - + } } } else if(param->value_type == tmedia_pvt_int32) @@ -1536,6 +1534,8 @@ static tsk_object_t* plugin_win_mf_consumer_video_ctor(tsk_object_t * self, va_l TMEDIA_CONSUMER(pSelf)->video.display.width = 0; // use codec value TMEDIA_CONSUMER(pSelf)->video.display.height = 0; // use codec value TMEDIA_CONSUMER(pSelf)->video.display.auto_resize = tsk_true; + + TSK_DEBUG_INFO("Create WinMF video consumer"); } return self; } diff --git a/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_converter_video.cxx b/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_converter_video.cxx index 1234098e..02c109a6 100644 --- a/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_converter_video.cxx +++ b/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_converter_video.cxx @@ -386,6 +386,7 @@ static inline const GUID& _plugin_win_mf_converter_video_ms_get_pixfmt(tmedia_ch { switch(chroma){ case tmedia_chroma_rgb24: + case tmedia_chroma_bgr24: return MFVideoFormat_RGB24; case tmedia_chroma_rgb565le: return MFVideoFormat_RGB565; diff --git a/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_producer_video.cxx b/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_producer_video.cxx index 7099d2be..adc04121 100644 --- a/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_producer_video.cxx +++ b/branches/2.0/doubango/plugins/pluginWinMF/plugin_win_mf_producer_video.cxx @@ -625,6 +625,8 @@ static tsk_object_t* plugin_win_mf_producer_video_ctor(tsk_object_t * self, va_l TMEDIA_PRODUCER(pSelf)->video.fps = 15; TMEDIA_PRODUCER(pSelf)->video.width = 352; TMEDIA_PRODUCER(pSelf)->video.height = 288; + + TSK_DEBUG_INFO("Create WinMF video producer"); } return self; } diff --git a/branches/2.0/doubango/tinyDAV/include/tinydav/video/gdi/tdav_consumer_video_gdi.h b/branches/2.0/doubango/tinyDAV/include/tinydav/video/gdi/tdav_consumer_video_gdi.h index d50413da..9ffbf487 100644 --- a/branches/2.0/doubango/tinyDAV/include/tinydav/video/gdi/tdav_consumer_video_gdi.h +++ b/branches/2.0/doubango/tinyDAV/include/tinydav/video/gdi/tdav_consumer_video_gdi.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2014 Doubango Telecom +/* Copyright (C) 2014 Mamadou DIOP * * This file is part of Open Source Doubango Framework. * @@ -26,7 +26,7 @@ TDAV_BEGIN_DECLS -TINYDAV_GEXTERN const tmedia_consumer_plugin_def_t *tdav_consumer_gdi_plugin_def_t; +TINYDAV_GEXTERN const tmedia_consumer_plugin_def_t *tdav_consumer_video_gdi_plugin_def_t; TDAV_END_DECLS diff --git a/branches/2.0/doubango/tinyDAV/src/tdav.c b/branches/2.0/doubango/tinyDAV/src/tdav.c index a63b7456..88d62438 100644 --- a/branches/2.0/doubango/tinyDAV/src/tdav.c +++ b/branches/2.0/doubango/tinyDAV/src/tdav.c @@ -105,6 +105,7 @@ static const tsk_size_t __codec_plugins_all_count = sizeof(__codec_plugins_all)/ #include "tinydav/audio/wasapi/tdav_consumer_wasapi.h" #include "tinydav/video/winm/tdav_consumer_winm.h" #include "tinydav/video/mf/tdav_consumer_video_mf.h" +#include "tinydav/video/gdi/tdav_consumer_video_gdi.h" #include "tinydav/t140/tdav_consumer_t140.h" // Producers @@ -351,6 +352,9 @@ int tdav_init() #elif HAVE_WASAPI tmedia_consumer_plugin_register(tdav_consumer_wasapi_plugin_def_t); #endif +#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT // Windows GDI + tmedia_consumer_plugin_register(tdav_consumer_video_gdi_plugin_def_t); +#endif #if HAVE_WINM // Windows Media (WP8) tmedia_consumer_plugin_register(tdav_consumer_winm_plugin_def_t); #endif @@ -588,6 +592,9 @@ int tdav_deinit() #if HAVE_WASAPI tmedia_consumer_plugin_unregister(tdav_consumer_wasapi_plugin_def_t); #endif +#if TDAV_UNDER_WINDOWS && !TDAV_UNDER_WINDOWS_RT // Windows GDI + tmedia_consumer_plugin_unregister(tdav_consumer_video_gdi_plugin_def_t); +#endif #if HAVE_WINM // Windows Media (WP8) tmedia_consumer_plugin_unregister(tdav_consumer_winm_plugin_def_t); #endif diff --git a/branches/2.0/doubango/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c b/branches/2.0/doubango/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c index e69de29b..07291458 100644 --- a/branches/2.0/doubango/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c +++ b/branches/2.0/doubango/tinyDAV/src/video/gdi/tdav_consumer_video_gdi.c @@ -0,0 +1,525 @@ +/* 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 + +#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); + // FIXME: full screen not supported yet + 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 = TMEDIA_CONSUMER(p_gdi)->video.in.width; + p_gdi->bitmapInfo.bmiHeader.biHeight = TMEDIA_CONSUMER(p_gdi)->video.in.height; // do not MULT(-1), already flipped + p_gdi->bitmapInfo.bmiHeader.biSizeImage = TMEDIA_CONSUMER(p_gdi)->video.in.width * 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; + + 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; + } + + if (p_gdi->bitmapInfo.bmiHeader.biSizeImage != size) { + 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 = TMEDIA_CONSUMER(p_gdi)->video.in.width; + p_gdi->bitmapInfo.bmiHeader.biHeight = TMEDIA_CONSUMER(p_gdi)->video.in.height; // do not MULT(-1), already flipped + p_gdi->bitmapInfo.bmiHeader.biSizeImage = 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*)GetPropA(hWnd, "Self")); + if (p_gdi) { + + } + break; + } + + case WM_PAINT: + { + struct tdav_consumer_video_gdi_s* p_gdi = ((struct tdav_consumer_video_gdi_s*)GetPropA(hWnd, "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) { + SetMapMode(all_dc[i], MM_ISOTROPIC); + SetWindowExtEx(all_dc[i], width, height, NULL); + SetViewportExtEx(all_dc[i], rc.right, rc.bottom, NULL); + } + + 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; + DPtoLP(ps.hdc, &logical_area, 1); + + 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*)GetPropA(hWnd, "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)) { + *p_wndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG)WndProc); + if (!*p_wndProc) { + TSK_DEBUG_ERROR("HookWindowLongPtr() failed with errcode=%d", GetLastError()); + CHECK_HR(hr = E_FAIL); + } + *p_bWindowHooked = TRUE; + SetPropA(*p_Window, "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) { + SetWindowLongPtr(*p_Window, GWL_WNDPROC, (LONG)*p_wndProc); + *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) { + ShowWindow(hWnd, SW_SHOWDEFAULT); + 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); + + SetPropA(p_gdi->hWindowFullScreen, "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; + } + 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 DirectShow 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 */ + diff --git a/branches/2.0/doubango/tinyDAV/src/video/tdav_converter_video.cxx b/branches/2.0/doubango/tinyDAV/src/video/tdav_converter_video.cxx index 2b809c39..44205975 100644 --- a/branches/2.0/doubango/tinyDAV/src/video/tdav_converter_video.cxx +++ b/branches/2.0/doubango/tinyDAV/src/video/tdav_converter_video.cxx @@ -132,8 +132,6 @@ static inline enum FourCC _tdav_converter_video_libyuv_get_pixfmt(tmedia_chroma_ } } - - static int tdav_converter_video_libyuv_init(tmedia_converter_video_t* self, tsk_size_t srcWidth, tsk_size_t srcHeight, tmedia_chroma_t srcChroma, tsk_size_t dstWidth, tsk_size_t dstHeight, tmedia_chroma_t dstChroma) { TSK_DEBUG_INFO("Initializing new LibYUV Video Converter src=(%dx%d@%d) dst=(%dx%d@%d)", srcWidth, srcHeight, srcChroma, dstWidth, dstHeight, dstChroma); diff --git a/branches/2.0/doubango/tinyDAV/tinyDAV.vcproj b/branches/2.0/doubango/tinyDAV/tinyDAV.vcproj index 837827a3..2e39d47f 100644 --- a/branches/2.0/doubango/tinyDAV/tinyDAV.vcproj +++ b/branches/2.0/doubango/tinyDAV/tinyDAV.vcproj @@ -1,7 +1,7 @@ + + @@ -918,6 +922,10 @@ + + diff --git a/branches/2.0/doubango/tinyNET/src/tnet_utils.c b/branches/2.0/doubango/tinyNET/src/tnet_utils.c index d3b9e643..2f53642c 100644 --- a/branches/2.0/doubango/tinyNET/src/tnet_utils.c +++ b/branches/2.0/doubango/tinyNET/src/tnet_utils.c @@ -829,7 +829,7 @@ int tnet_getbestsource(const char* destination, tnet_port_t port, tnet_socket_ty } if ((rlen = write(s, rtm, l)) < 0) { - printf("writing to routing socket"); + TSK_DEBUG_INFO("writing to routing socket"); // TODO } do {