Play FLAC via DirectShow - c++

I would like write an simple (only one exe) music player.
The player is work if I want play mp3, but didn't work if I want use MadFLAC Filter.
I can't connect of the MadFLAC pins. Always drop 0x80040207 error.
(I think, the MadFLAC filter is loaded correctly, I can add to the GraphBuilder without error and see in the EnumFilters.)
I do not insist on using MadFLAC. All other options is interest me with which I can play a FLAC file via DirectShow without install codec pack to client pc.
Here is my simplified code:
#include <windows.h>
#include <strmif.h>
#include <control.h>
#include <uuids.h>
#pragma comment(lib, "strmiids.lib")
IPin* GetPin(IBaseFilter *bFilter, PIN_DIRECTION pindir)
{
IEnumPins *EnumPin;
bFilter->EnumPins(&EnumPin);
unsigned long int num;
IPin *TempPin = NULL;
do {
EnumPin->Next(1, &TempPin, &num);
if (num != 0)
{
PIN_INFO PinInfo;
TempPin->QueryPinInfo(&PinInfo);
if (PinInfo.dir == pindir) break;
}
} while (num != 0);
return TempPin;
}
typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid, REFIID riid, void **ppv);
HRESULT CreateFilterFromFile(HINSTANCE hLibInst, GUID TGUID, void **Filter) {
IClassFactory * ClassFactory;
HRESULT Result = S_FALSE;
FARPROC func;
func = GetProcAddress(hLibInst, "DllGetClassObject");
if (func != NULL)
{
IClassFactory *classFactory;
DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)func;
Result = dllGetClassObject(TGUID, IID_IClassFactory, (void**)&classFactory);
if (SUCCEEDED(Result))
{
Result = classFactory->CreateInstance(NULL, IID_IBaseFilter, Filter);
classFactory->Release();
}
}
return Result;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
void Player(bool isFlac) {
IGraphBuilder * player;
IMediaControl * mcontrol;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&player);
player->QueryInterface(IID_IMediaControl, (void **)&mcontrol);
IBaseFilter *ARS = NULL;
HRESULT hr = CoCreateInstance(CLSID_AsyncReader, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&ARS);
IBaseFilter * AudioDecoderFilter = NULL;
if (isFlac) {
hr = player->AddSourceFilter(L"D:\\test.flac", L"Async Reader Source", &ARS);
GUID CLSID_MadFlacAudioDecoder;
CLSIDFromString(L"{6B257121-CBB6-46B3-ABFA-B14DFA98C4A6}", &CLSID_MadFlacAudioDecoder);
HINSTANCE FHMPCAudioFilterInst = CoLoadLibrary(L"d:\\_MadFlac\\madFlac.ax", true);
if (FHMPCAudioFilterInst != 0) {
hr = CreateFilterFromFile(FHMPCAudioFilterInst, CLSID_MadFlacAudioDecoder, (void **)&AudioDecoderFilter);
if (AudioDecoderFilter != NULL) {
player->AddFilter(AudioDecoderFilter, L"MadFLAC Audio decoder (internal)");
}
}
}
else {
hr = player->AddSourceFilter(L"D:\\test.mp3", L"Async Reader Source", &ARS);
hr = CoCreateInstance(CLSID_MPEG1Splitter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&AudioDecoderFilter);
hr = player->AddFilter(AudioDecoderFilter, L"MPEG1Splitter");
}
IBaseFilter *PCM = NULL;
hr = CoCreateInstance(CLSID_ACMWrapper, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&PCM);
hr = player->AddFilter(PCM, L"PCM");
IBaseFilter *DSE = NULL;
hr = CoCreateInstance(CLSID_DSoundRender, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&DSE);
hr = player->AddFilter(DSE, L"Direct Renderer");
//ARS -> AdioDecoder
hr = GetPin(ARS, PINDIR_OUTPUT)->Connect(GetPin(AudioDecoderFilter, PINDIR_INPUT), NULL);
//If I use MadFLAC filter this hresult is 0x80040207
if (FAILED(hr)) MessageBoxA(NULL, "There is no common media type between these pins.", "Error", NULL);
//AudioDecoderFilter -> PCM
hr = GetPin(AudioDecoderFilter, PINDIR_OUTPUT)->Connect(GetPin(PCM, PINDIR_INPUT), NULL);
//PCM -> DSE
hr = GetPin(PCM, PINDIR_OUTPUT)->Connect(GetPin(DSE, PINDIR_INPUT), NULL);
//ListGraph(player);
mcontrol->Run();
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "Teszt";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; }
hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "Teszt", "AppName", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT, 460, 240, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; }
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
Player(false);
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
I use VS2015, native C++.
I would have tried to use LibFLAC as well, but I was already stuck there that I couldn’t Build because it threw hundreds of bugs, which I eventually reduced to less than 20 but I had already given up there after several hours of suffering.
https://github.com/xiph/flac
https://ftp.osuosl.org/pub/xiph/releases/oggdsf/

Windows is not shipped with FLAC decoder for DirectShow. Hence, ability to play FLAC audio via DirectShow requires a third party decoder. If you don't want to reply on a codec pack, you need to either bundle certain FLAC decoder with DirectShow interface or implement a decoder in code. https://xiph.org/dshow looks like the best third party decoder option to me, implementing a DirectShow filter over FLAC library requires some development effort, which is probably an overkill for the task in question.

Related

Basic Win32 WebView2 example not working in pure C

A new problem blows my mind lately: a very simple code using the WebView2 library from Microsoft works if compiled as C++ but not as C. What could be causing the problem? I tried all sorts of fixes, like using an older version of the WebView2 library, using Edge Canary or beta or a different version of WebView2 Runtime, it simply refuses to work.
Here is the sample code in C:
#include <initguid.h>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <Shlobj_core.h>
#include "WebView2.h"
#define APPLICATION_NAME TEXT("WebView2")
#define error_printf printf
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* envHandler;
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* completedHandler;
HWND hWnd = NULL;
ICoreWebView2Controller* webviewController = NULL;
ICoreWebView2* webviewWindow = NULL;
BOOL bEnvCreated = FALSE;
ULONG HandlerRefCount = 0;
ULONG HandlerAddRef(IUnknown* This)
{
return ++HandlerRefCount;
}
ULONG HandlerRelease(IUnknown* This)
{
--HandlerRefCount;
if (HandlerRefCount == 0)
{
if (completedHandler)
{
free(completedHandler->lpVtbl);
free(completedHandler);
}
if (envHandler)
{
free(envHandler->lpVtbl);
free(envHandler);
}
}
return HandlerRefCount;
}
HRESULT HandlerQueryInterface(
IUnknown* This,
IID* riid,
void** ppvObject
)
{
*ppvObject = This;
HandlerAddRef(This);
return S_OK;
}
HRESULT HandlerInvoke(
IUnknown* This,
HRESULT errorCode,
void* arg
)
{
if (!bEnvCreated)
{
bEnvCreated = TRUE;
char ch;
completedHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler));
if (!completedHandler)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandler",
GetLastError()
);
ch = _getch();
return GetLastError();
}
completedHandler->lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl));
if (!completedHandler->lpVtbl)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl",
GetLastError()
);
ch = _getch();
return GetLastError();
}
completedHandler->lpVtbl->AddRef = HandlerAddRef;
completedHandler->lpVtbl->Release = HandlerRelease;
completedHandler->lpVtbl->QueryInterface = HandlerQueryInterface;
completedHandler->lpVtbl->Invoke = HandlerInvoke;
ICoreWebView2Environment* env = arg;
env->lpVtbl->CreateCoreWebView2Controller(
env,
hWnd,
completedHandler
);
}
else
{
ICoreWebView2Controller* controller = arg;
if (controller != NULL) {
webviewController = controller;
webviewController->lpVtbl->get_CoreWebView2(
webviewController,
&webviewWindow
);
}
ICoreWebView2Settings* Settings;
webviewWindow->lpVtbl->get_Settings(
webviewWindow,
&Settings
);
Settings->lpVtbl->put_IsScriptEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_AreDefaultScriptDialogsEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_IsWebMessageEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_AreDevToolsEnabled(
Settings,
FALSE
);
Settings->lpVtbl->put_AreDefaultContextMenusEnabled(
Settings,
TRUE
);
Settings->lpVtbl->put_IsStatusBarEnabled(
Settings,
TRUE
);
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->lpVtbl->put_Bounds(
webviewController,
bounds
);
webviewWindow->lpVtbl->Navigate(
webviewWindow,
L"https://google.com/"
);
}
return S_OK;
}
LRESULT CALLBACK WindowProc(
_In_ HWND hWnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
/*case WM_NCCALCSIZE:
{
return 0;
}*/
case WM_DPICHANGED:
{
RECT* const newWindowSize = (RECT*)(lParam);
SetWindowPos(
hWnd,
NULL,
newWindowSize->left,
newWindowSize->top,
newWindowSize->right - newWindowSize->left,
newWindowSize->bottom - newWindowSize->top,
SWP_NOZORDER | SWP_NOACTIVATE);
return TRUE;
}
case WM_SIZE:
{
if (webviewController != NULL) {
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->lpVtbl->put_Bounds(
webviewController,
bounds
);
};
break;
}
default:
{
return DefWindowProc(
hWnd,
uMsg,
wParam,
lParam
);
}
}
return 0;
}
int WINAPI wWinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nShowCmd
)
{
int ch;
FILE* conout;
AllocConsole();
freopen_s(
&conout,
"CONOUT$",
"w",
stdout
);
HRESULT hr;
if (!SetProcessDpiAwarenessContext(
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
))
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"SetProcessDpiAwarenessContext",
GetLastError()
);
ch = _getch();
return GetLastError();
}
hr = CoInitialize(NULL);
if (FAILED(hr))
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"CoInitialize",
hr
);
ch = _getch();
return hr;
}
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = APPLICATION_NAME;
hWnd = CreateWindowEx(
0,
(LPCWSTR)(
MAKEINTATOM(
RegisterClass(&wndClass)
)
),
APPLICATION_NAME,
WS_OVERLAPPEDWINDOW,
100, 100, 800, 800,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"CreateWindowEx",
GetLastError()
);
ch = _getch();
return GetLastError();
}
ShowWindow(hWnd, nShowCmd);
envHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler));
if (!envHandler)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler",
GetLastError()
);
ch = _getch();
return GetLastError();
}
envHandler->lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl));
if (!envHandler->lpVtbl)
{
error_printf(
"%s:%d: %s (0x%x).\n",
__FILE__,
__LINE__,
"Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl",
GetLastError()
);
ch = _getch();
return GetLastError();
}
envHandler->lpVtbl->AddRef = HandlerAddRef;
envHandler->lpVtbl->Release = HandlerRelease;
envHandler->lpVtbl->QueryInterface = HandlerQueryInterface;
envHandler->lpVtbl->Invoke = HandlerInvoke;
UpdateWindow(hWnd);
CreateCoreWebView2EnvironmentWithOptions(
NULL,
NULL,
NULL,
envHandler
);
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(
&msg,
NULL,
0,
0)) != 0)
{
// An error occured
if (bRet == -1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
As you can see, I simply used lpVtbl where appropiate and provided proper callbacks. The code is a bit more compact in C++:
#include <Windows.h>
#include <stdio.h>
#include <wrl.h>
#include <wil/com.h>
#include "WebView2.h"
#define APPLICATION_NAME TEXT("WebView2")
static wil::com_ptr<ICoreWebView2Controller> webviewController;
static wil::com_ptr<ICoreWebView2> webviewWindow;
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
switch (uMsg)
{
case WM_DPICHANGED:
{
RECT* const newWindowSize = (RECT*)(lParam);
SetWindowPos(hwnd,
NULL,
newWindowSize->left,
newWindowSize->top,
newWindowSize->right - newWindowSize->left,
newWindowSize->bottom - newWindowSize->top,
SWP_NOZORDER | SWP_NOACTIVATE);
return TRUE;
}
case WM_SIZE:
{
if (webviewController != NULL) {
RECT bounds;
GetClientRect(hwnd, &bounds);
webviewController->put_Bounds(bounds);
};
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(
hwnd,
uMsg,
wParam,
lParam
);
}
}
return 0;
}
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PWSTR pCmdLine,
int nCmdShow
)
{
SetProcessDpiAwarenessContext(
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
);
WNDCLASS wndClass = { 0 };
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = APPLICATION_NAME;
RegisterClass(&wndClass);
HWND hWnd = CreateWindowEx(
0,
APPLICATION_NAME,
APPLICATION_NAME,
WS_OVERLAPPEDWINDOW,
100, 100, 800, 800,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(
hWnd,
nCmdShow
);
UpdateWindow(hWnd);
CreateCoreWebView2EnvironmentWithOptions(NULL, NULL, NULL,
Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT {
// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
env->CreateCoreWebView2Controller(hWnd, Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
if (controller != nullptr) {
webviewController = controller;
webviewController->get_CoreWebView2(&webviewWindow);
}
// Add a few settings for the webview
// The demo step is redundant since the values are the default settings
ICoreWebView2Settings* Settings;
webviewWindow->get_Settings(&Settings);
Settings->put_IsScriptEnabled(TRUE);
Settings->put_AreDefaultScriptDialogsEnabled(TRUE);
Settings->put_IsWebMessageEnabled(TRUE);
Settings->put_AreDevToolsEnabled(FALSE);
//Settings->put_AreDefaultContextMenusEnabled(FALSE);
Settings->put_IsStatusBarEnabled(FALSE);
// Resize WebView to fit the bounds of the parent window
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->put_Bounds(bounds);
webviewController->put_ZoomFactor(0.8);
// Schedule an async task to navigate to Bing
webviewWindow->Navigate(HOME_PAGE);
// Step 4 - Navigation events
// Step 5 - Scripting
// Step 6 - Communication between host and web content
return S_OK;
}).Get());
return S_OK;
}).Get());
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(
&msg,
NULL,
0,
0)) != 0)
{
// An error occured
if (bRet == -1)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
Since I have now stumbled upon this, I am really curious what may be causing this. Why does it matter in the end? Thanks for any pointers.
It does not work means the web page is not displayed. The window is blank. The WebView2 actually is not displayed on the window.
Edit:
What are wil::com_ptrs actually? If I change this:
static wil::com_ptr<ICoreWebView2Controller> webviewController;
static wil::com_ptr<ICoreWebView2> webviewWindow;
To this:
static ICoreWebView2Controller* webviewController;
static ICoreWebView2* webviewWindow;
In C++, I break it. Why? Just why? (I replaced the callbacks Microsoft::WRL::Callback with standalone classes and, naturally, it still worked, but getting rid of the COM pointers and using regular pointers breaks it. Why...?
The solution is simple, if one actually takes a minute to look a bit on the correctness and logic of the code, rather than suggesting generics and commenting just so that the patch cables have some bytes to carry around. This section has to be modified a bit: since I use controller after the function returns, and without using smart pointers, I have to increase its reference count so that the library knows I use it and does not free it after the Invoke function body is executed. Of course, when you do the assignment in the version with smart pointers, this is automatically done in the background. Unfortunately, I overlooked this, not being that aware this happens. Things like these is the reason I write these ports in C, so that I take an in-depth look and better understand how stuff works from top to bottom. 'It can't be done' without an actual reason is not a valid answer for me.
Anyway, here is the fixed version:
if (controller != NULL) {
webviewController = controller;
webviewController->lpVtbl->get_CoreWebView2(
webviewController,
&webviewWindow
);
webviewController->lpVtbl->AddRef(webviewController); // <-- here, increase the reference count for the webviewController
}
That's it. many thanks to the guy who helped me out: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1124

How can I draw correctly in a window with Direct2D?

I was making a program that draws two lines and a circle in the window, for this I used Direct2D.
I was doing a program that draws two lines and a circle in the window. I noticed a problem and that is that when drawing a line of coordinates (100,200), (200,200), that line is not drawn in the pixels (100,200) and ( 200,200). I checked this by reading the coordinates of the mouse in the window and seeing if the line actually has the coordinates that I mentioned.
Do a little more research, and according to Microsoft, direct2d works with dpi and not physical pixels.
So is there a way to work directly with pixels like in Processing or Win32 where the position of the controls is expressed in physical pixels of the window.
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d3d11_1.h>
#include <dxgi1_6.h>
#include <string>
#pragma comment(lib,"d3d11")
#pragma comment(lib,"d2d1")
#pragma comment(lib,"dxgi")
#pragma warning(disable : 4996)
using namespace D2D1;
using namespace std;
template<typename Interface> void SafeRelease(Interface**);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInst
, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInst);
UNREFERENCED_PARAMETER(lpCmdLine);
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINLOGO));
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"Demo_Wnd_Class";
wc.lpszMenuName = NULL;
wc.style = 0;
RegisterClass(&wc);
DWORD dwTopLevelWndStyle = WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME | WS_MAXIMIZEBOX);
RECT clientRc = { 0,0,640,480 };
AdjustWindowRect(&clientRc, dwTopLevelWndStyle, FALSE);
HWND hWnd = CreateWindow(L"Demo_Wnd_Class", L"Direct2DDemo", dwTopLevelWndStyle, CW_USEDEFAULT, CW_USEDEFAULT
, clientRc.right - clientRc.left, clientRc.bottom - clientRc.top, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, SW_SHOWDEFAULT);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
ID3D11Device* d3dDevice;
IDXGIDevice* dxgiDevice = NULL;
D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1,
};
HRESULT hr = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featureLevels
, 7, D3D11_SDK_VERSION, &d3dDevice, NULL, NULL);
if (SUCCEEDED(hr)) {
OutputDebugString(L"D3D11 Device created successful\n");
hr = d3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice);
}
ID2D1Device6* d2dDevice = NULL;
ID2D1DeviceContext6* d2dDeviceContext = NULL;
IDXGIFactory2* dxgiFactory = NULL;
IDXGISwapChain1* dxgiSwapChain = NULL;
IDXGISurface *dxgiBackBufferSurface = NULL; // Back buffer
ID2D1Bitmap1* bmpTarget = NULL;
ID2D1SolidColorBrush* shapeBr = NULL;
DXGI_SWAP_CHAIN_DESC1 dscd;
dscd.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
dscd.BufferCount = 2;
dscd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
dscd.Flags = 0;
dscd.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
dscd.Height = 480;
dscd.SampleDesc.Count = 1;
dscd.SampleDesc.Quality = 0;
dscd.Scaling = DXGI_SCALING_NONE;
dscd.Stereo = FALSE;
dscd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
dscd.Width = 640;
if (SUCCEEDED(hr)) {
OutputDebugString(L"DXGI Device created successful\n");
hr = D2D1CreateDevice(dxgiDevice, CreationProperties(D2D1_THREADING_MODE_SINGLE_THREADED, D2D1_DEBUG_LEVEL_NONE, D2D1_DEVICE_CONTEXT_OPTIONS_NONE), (ID2D1Device**)&d2dDevice);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Device created successful\n");
hr = d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, (ID2D1DeviceContext**)&d2dDeviceContext);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Device Context created successful\n");
hr = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG, __uuidof(IDXGIFactory2), (void**)&dxgiFactory);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"DXGI Factory created successful\n");
hr = dxgiFactory->CreateSwapChainForHwnd(d3dDevice, hWnd, &dscd, NULL, NULL, &dxgiSwapChain);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"DXGI Swap Chain created successful\n");
hr = dxgiSwapChain->GetBuffer(0, __uuidof(IDXGISurface), (void**)&dxgiBackBufferSurface);
}
if (SUCCEEDED(hr)) {
hr = d2dDeviceContext->CreateBitmapFromDxgiSurface(dxgiBackBufferSurface, BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),0,0), &bmpTarget);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Bitmap created successful\n");
d2dDeviceContext->SetTarget(bmpTarget);
hr = d2dDeviceContext->CreateSolidColorBrush(ColorF(ColorF::White), &shapeBr);
}
if (SUCCEEDED(hr)) {
OutputDebugString(L"D2D Solid Color Brush created successful\n");
d2dDeviceContext->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
d2dDeviceContext->SetTransform(Matrix3x2F::Identity());
d2dDeviceContext->SetAntialiasMode(D2D1_ANTIALIAS_MODE::D2D1_ANTIALIAS_MODE_ALIASED);
}
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
d2dDeviceContext->BeginDraw();
d2dDeviceContext->Clear(ColorF(ColorF::ForestGreen));
d2dDeviceContext->DrawLine(Point2F(400, 0), Point2F(400, 400), shapeBr);
d2dDeviceContext->DrawLine(Point2F(200.0f, 320.0f), Point2F(400.0f, 320.0f), shapeBr);
d2dDeviceContext->DrawEllipse(D2D1::Ellipse(Point2F(300.0f, 300.0f), 10.0f, 10.0f), shapeBr);
POINT cursorPos;
GetCursorPos(&cursorPos);
ScreenToClient(hWnd,&cursorPos);
wstring dbgCursorPos = wstring(L"(MouseX: " + to_wstring(cursorPos.x) + L" MouseY: " + to_wstring(cursorPos.y) + L")\n");
OutputDebugString(dbgCursorPos.c_str());
d2dDeviceContext->EndDraw();
dxgiSwapChain->Present(1, 0);
}
}
SafeRelease(&d3dDevice);
SafeRelease(&dxgiBackBufferSurface);
SafeRelease(&dxgiDevice);
SafeRelease(&dxgiFactory);
SafeRelease(&dxgiSwapChain);
SafeRelease(&d2dDevice);
SafeRelease(&d2dDeviceContext);
SafeRelease(&bmpTarget);
SafeRelease(&shapeBr);
return 0;
}
template<typename Interface> void SafeRelease(Interface** ppInterface) {
if (*ppInterface) {
(*ppInterface)->Release();
(*ppInterface) = NULL;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
if (Msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}

How do I make TTS stop speaking IMMEDIATELY?

I am using ISpVoice to speak an input string. Now, even though I use SPF_ASYNC and SPF_PURGEBEFORESPEAK tags in the Speak method, the tts doesn't stop whenever Pause is called instead it continues until the tts finishes a word.
Here's how I do it:
void speakSentence()
{
pVoice->Pause();
pVoice->Speak(L"This is a sentence.", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
pVoice->Resume();
}
Whenever I try to call this function at the middle of the word "sentence", the tts doesn't pause and instead continues to say the word until the end.
From microsoft documentation:
ISpVoice::Pause pauses the voice at the nearest alert boundary and closes the output device, allowing access to pending speak requests from other voices.
I tried changing the alert boundary by:
pVoice->SetAlertBoundary(SPEI_PHONEME);
and it doesn't work.
There is NVDA Screen Reader that solved this problem but I don't know how they did it.
Is there anyway to solve my problem?
EDIT:
Here's my full code. I am creating a small screen reader program that uses both UIAutomation and MSAA.
The program may somewhat unstable when comparing UI objects but most times it works.
screeenreader.h:
#ifndef _SCREENREADER_H_
#define _SCREENREADER_H_
#define WIN32_LEAN_AND_MEAN
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <memory>
#include "speechsynthesis.h"
#include "uiautomator.h"
class ScreenReader
{
public:
explicit ScreenReader(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdShow);
virtual ~ScreenReader();
LRESULT CALLBACK MessageHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int Exec();
private:
void InitializeWindows();
void InitRawInputDevices();
bool IsMouseMove();
private:
LPCWSTR m_applicationName;
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
PSTR m_pScmdline;
int m_iCmdShow;
HWND m_hWnd;
SpeechSynthesis *m_pSpeech;
UIAutomator *m_pAutomator;
RAWINPUTDEVICE rid[2];
LONG m_prevMouseX;
LONG m_prevMouseY;
BSTR currItem;
};
static ScreenReader *application;
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#endif
screenreader.cpp: In this part I called ISpVoice at the messageloop section. At ScreenReader::MessageHandler() function at IsMouseMove condition.
#include "screenreader.h"
ScreenReader::ScreenReader(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdShow)
{
CoInitialize(NULL);
m_pSpeech = new SpeechSynthesis;
m_pAutomator = new UIAutomator;
// Get current Cursor position.
POINT pt;
GetCursorPos(&pt);
m_prevMouseX = pt.x;
m_prevMouseY = pt.y;
// Notify user the program is loading.
m_pSpeech->Speak(L"Loading Rescan. Please wait.", SPF_DEFAULT, NULL);
m_hInstance = hInstance;
m_hPrevInstance = hPrevInstance;
m_pScmdline = pScmdline;
m_iCmdShow = iCmdShow;
application = this;
InitializeWindows();
InitRawInputDevices();
}
ScreenReader::~ScreenReader()
{
if (m_pSpeech != nullptr)
{
delete m_pSpeech;
m_pSpeech = nullptr;
}
if (m_pAutomator != nullptr)
{
delete m_pAutomator;
m_pAutomator = nullptr;
}
if (currItem != NULL)
{
SysFreeString(currItem);
currItem = NULL;
}
CoUninitialize();
}
LRESULT CALLBACK ScreenReader::MessageHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INPUT:
{
UINT dwSize;
GetRawInputData(
(HRAWINPUT)lParam,
RID_INPUT,
NULL,
&dwSize,
sizeof(RAWINPUTHEADER)
);
std::unique_ptr<BYTE[]> lpb(new BYTE[dwSize]);
if (!lpb)
return 0;
if (GetRawInputData(
(HRAWINPUT)lParam,
RID_INPUT,
lpb.get(),
&dwSize,
sizeof(RAWINPUTHEADER)
) != dwSize)
OutputDebugString(L"GetRawInputData does not return correct size!\n");
RAWINPUT *raw = (RAWINPUT*)lpb.get();
if (raw->header.dwType == RIM_TYPEKEYBOARD)
{
UINT mess = raw->data.keyboard.Message;
UINT vKey = raw->data.keyboard.VKey;
if (mess == WM_KEYDOWN)
{
}
}
else if (raw->header.dwType == RIM_TYPEMOUSE)
{
if (IsMouseMove())
{
BSTR item;
HRESULT hr = m_pAutomator->GetUIAutomationItemNameAtMousePoint(&item);
if (item == NULL)
return 0;
if (currItem == NULL)
currItem = SysAllocString(item);
if (wcscmp(currItem, item) != 0)
{
m_pSpeech->Stop();
m_pSpeech->Speak(item);
if (currItem != NULL)
SysFreeString(currItem);
currItem = SysAllocString(item);
}
SysFreeString(item);
}
}
}
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
int ScreenReader::Exec()
{
MSG msg;
ShowWindow(m_hWnd, m_iCmdShow);
// Tell the user that the program is ready.
m_pSpeech->Speak(L"Rescan ready.", SPF_PURGEBEFORESPEAK);
// The message loop
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void ScreenReader::InitializeWindows()
{
// Create Window class.
WNDCLASSEX wc;
m_applicationName = L"Rescan Screen Reader";
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = m_hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = m_applicationName;
wc.hIconSm = wc.hIcon;
// Register the window class.
RegisterClassEx(&wc);
m_hWnd = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
m_applicationName,
L"Rescan Screen Reader",
WS_CAPTION | WS_MINIMIZEBOX | WS_OVERLAPPED | WS_SYSMENU,
(GetSystemMetrics(SM_CXSCREEN) - 500) / 2,
(GetSystemMetrics(SM_CYSCREEN) - 300) / 2,
500,
300,
NULL,
NULL,
m_hInstance,
NULL
);
}
void ScreenReader::InitRawInputDevices()
{
// Initialize Keyboard
rid[0].usUsagePage = 0x01;
rid[0].usUsage = 0x06;
rid[0].dwFlags = RIDEV_INPUTSINK;
rid[0].hwndTarget = m_hWnd;
// Initialize Mouse
rid[1].usUsagePage = 0x01;
rid[1].usUsage = 0x02;
rid[1].dwFlags = RIDEV_INPUTSINK;
rid[1].hwndTarget = m_hWnd;
// Register RIDs
RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE));
}
bool ScreenReader::IsMouseMove()
{
POINT pt;
GetCursorPos(&pt);
bool result = !(m_prevMouseX == pt.x && m_prevMouseY == pt.y);
m_prevMouseX = pt.x;
m_prevMouseY = pt.y;
return result;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_QUIT:
PostQuitMessage(0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return application->MessageHandler(hWnd, message, wParam, lParam);
}
}
I wrapped ISpVoice into the SpeechSynthesis class.
speechsynthesis.h:
#ifndef _SPEECHSYNTHESIS_H_
#define _SPEECHSYNTHESIS_H_
#pragma warning(disable : 4996)
#define SPCAT_VOICES_ONECORE L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech_OneCore\\Voices"
#include <sapi.h>
#include <sphelper.h>
#include <atlbase.h>
class SpeechSynthesis
{
public:
SpeechSynthesis();
~SpeechSynthesis();
HRESULT Speak(LPCWSTR pwcs, DWORD dwFlags = SPF_PURGEBEFORESPEAK | SPF_ASYNC | SPF_IS_NOT_XML, ULONG *pulStreamNumber = NULL);
HRESULT Resume();
HRESULT Pause();
HRESULT Stop();
ISpVoice* getVoice();
private:
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<IEnumSpObjectTokens> cpEnum;
ISpVoice* pVoice;
ULONG count;
};
#endif
speechsynthesis.cpp:
#include "speechsynthesis.h"
SpeechSynthesis::SpeechSynthesis()
{
HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);
if (SUCCEEDED(hr))
hr = SpEnumTokens(SPCAT_VOICES_ONECORE, NULL, NULL, &cpEnum);
if (SUCCEEDED(hr))
hr = cpEnum->GetCount(&count);
if (SUCCEEDED(hr))
{
cpEnum->Item(1, &cpVoiceToken);
pVoice->SetPriority(SPVPRIORITY::SPVPRI_ALERT);
pVoice->SetAlertBoundary(SPEI_PHONEME);
pVoice->SetOutput(NULL, TRUE);
pVoice->SetVoice(cpVoiceToken);
}
if (FAILED(hr))
{
MessageBox(NULL, "A fatal error has occured", "Error Message", MB_ABORTRETRYIGNORE);
}
}
SpeechSynthesis::~SpeechSynthesis()
{
pVoice->Release();
}
HRESULT SpeechSynthesis::Speak(LPCWSTR pwcs, DWORD dwFlags, ULONG *pulStreamNumber)
{
return pVoice->Speak(pwcs, dwFlags, pulStreamNumber);
}
HRESULT SpeechSynthesis::Resume()
{
return pVoice->Resume();
}
HRESULT SpeechSynthesis::Pause()
{
return pVoice->Pause();
}
HRESULT SpeechSynthesis::Stop()
{
return Speak(NULL);
}
ISpVoice * SpeechSynthesis::getVoice()
{
return pVoice;
}
uiautomator.h
#ifndef _UIAUTOMATOR_H_
#define _UIAUTOMATOR_H_
#include <windows.h>
#include <oleacc.h>
#include <uiautomation.h>
#pragma comment(lib, "oleacc.lib")
class UIAutomator
{
public:
UIAutomator();
~UIAutomator();
HRESULT GetItemNameAtMousePoint(BSTR *pStr);
HRESULT GetUIAutomationItemNameAtMousePoint(BSTR *pStr);
private:
HRESULT InitUIAutomation();
private:
IUIAutomation *m_automation;
};
#endif
uiautomator.cpp
#include "uiautomator.h"
UIAutomator::UIAutomator()
{
SetProcessDPIAware();
HRESULT hr = InitUIAutomation();
}
UIAutomator::~UIAutomator()
{
}
HRESULT UIAutomator::GetItemNameAtMousePoint(BSTR * pStr)
{
POINT pt;
GetPhysicalCursorPos(&pt);
VARIANT varItem;
IAccessible *pAcc;
HRESULT hr = AccessibleObjectFromPoint(pt, &pAcc, &varItem);
if (SUCCEEDED(hr))
{
hr = pAcc->get_accName(varItem, pStr);
VariantClear(&varItem);
pAcc->Release();
}
return hr;
}
HRESULT UIAutomator::GetUIAutomationItemNameAtMousePoint(BSTR * pStr)
{
CONTROLTYPEID id;
POINT pt;
IUIAutomationElement *elem;
VARIANT val;
GetCursorPos(&pt);
HRESULT hr = m_automation->ElementFromPoint(pt, &elem);
if (SUCCEEDED(hr))
{
hr = elem->get_CurrentControlType(&id);
if (SUCCEEDED(hr))
{
if (id == UIA_PaneControlTypeId)
GetItemNameAtMousePoint(pStr);
else if (id == UIA_EditControlTypeId)
{
hr = elem->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &val);
if (SUCCEEDED(hr))
{
*pStr = SysAllocString(val.bstrVal);
VariantClear(&val);
}
}
else
{
hr = elem->get_CurrentName(pStr);
}
}
elem->Release();
}
return hr;
}
HRESULT UIAutomator::InitUIAutomation()
{
HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation), (void**)&m_automation);
return hr;
}
main.cpp
#include "vld.h"
#include "screenreader.h"
#include <memory>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pScmdline, int iCmdShow)
{
std::unique_ptr<ScreenReader> app(new ScreenReader(hInstance, hPrevInstance, pScmdline, iCmdShow));
return app->Exec();
}
If you don't have time to compile here's the program.
If you launch it and hover the mouse on the program window there is a lag when highlighting minimize and close button. Also sometimes the tts doesn't stop immediately when you hover at another object.
Compare this one to NVDA Screen Reader. You will notice the big difference.
It works for me with or without setting SetAlertBoundary(SPEI_PHONEME).
The following is my test code, you can have a try.
HRESULT hr = ::CoInitialize(nullptr);
if (FAILED(hr))
{
return EXIT_FAILURE;
}
std::wstring text;
text = L"This is a sentence.";
CComPtr<ISpVoice> cpVoice;
// Create a SAPI voice
hr = cpVoice.CoCreateInstance(CLSID_SpVoice);
//cpVoice->SetAlertBoundary(SPEI_PHONEME);
// set the output to the default audio device
if (SUCCEEDED(hr))
{
hr = cpVoice->SetOutput(NULL, TRUE);
}
// Speak the text
if (SUCCEEDED(hr))
{
hr = cpVoice->Speak(text.c_str(), SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
}
text = L"The third type, a logarithm of the unsigned fold change, is undoubtedly the most tractable.";
Sleep(600);
hr = cpVoice->Pause();
hr = cpVoice->Resume();
hr = cpVoice->Speak(text.c_str(), SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
Sleep(10000);
::CoUninitialize();
if (SUCCEEDED(hr))
{
return EXIT_SUCCESS;
}
return EXIT_FAILURE;
I finally got it!
CComPtr<ISpAudio> audio;
CSpStreamFormat format;
format.AssignFormat(SPSF_11kHz8BitMono);
Initialize audio
SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOOUT, &audio);
Then, set its format and set it as output to pVoice.
audio->SetFormat(format.FormatId(), format.WaveFormatExPtr());
pVoice->SetOutput(audio, FALSE);
Now I have access to the audio stream!
Now to Immediately stop the audio, call:
audio->SetState(SPAS_STOP, 0);
Then speak again using:
audio->SetState(SPAS_RUN, 0);
pVoice->Speak(L"This is a sentence", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);

How to stop Direct3D from flicker on release on multihead setup?

Have a kiosk system with a small touchscreen and a large display, and I have an app that works well playing video on the large display. For some reason, when Direct3D stops (app quits), both screens flicker (turn black, then recover).
I simplified the problem down to a program that simply opens a focus window, initializes a Direct3D device, then releases it. On release (or stopping the program without releasing), both screens flicker.
Can someone who's familiar with Direct3D look at this and tell me if I'm doing something wrong?
#include <iostream>
#include <windows.h>
#include <d3d9.h>
const char g_szClassName[] = "myWindowClass";
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
HWND hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
"Focus Window",
WS_POPUP | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
IDirect3D9* m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (m_pD3D == NULL) {
MessageBox(NULL, "Failed to initialize direct3D!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// second display is 1920 x 1080
D3DPRESENT_PARAMETERS m_param;
ZeroMemory(&m_param, sizeof(m_param));
m_param.BackBufferWidth = 1920;
m_param.BackBufferHeight = 1080;
m_param.BackBufferFormat = D3DFMT_X8R8G8B8;
m_param.BackBufferCount = 1;
m_param.MultiSampleType = D3DMULTISAMPLE_NONE;
m_param.SwapEffect = D3DSWAPEFFECT_COPY;
m_param.hDeviceWindow = hwnd;
m_param.Windowed = FALSE;
m_param.Flags = D3DPRESENTFLAG_VIDEO;
m_param.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
m_param.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
HRESULT hr;
IDirect3DDevice9* m_pD3DD;
hr = m_pD3D->CreateDevice(
1,
D3DDEVTYPE_HAL,
hwnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
&m_param,
&m_pD3DD
);
if (hr != S_OK) {
MessageBox(NULL, "Failed to create direct3D device!", "Error!", MB_ICONEXCLAMATION | MB_OK);
return 0;
}
Sleep(3000);
// flicker occurs here
m_pD3DD->Release();
m_pD3D->Release();
return 0;
}
NOTE: This program should run on any setup with 2 screens on the same video card, but the second screen's width/height might need to be adjusted (it's hardcoded in BackBufferWidth and BackBufferHeight)
Not sure if that is workable for you, but in trying various examples around the interwebs, it seems the only way to avoid the flicker is to use windowed mode with a borderless window:
...
HWND hwnd = CreateWindowEx(
0,
g_szClassName,
"Focus Window",
WS_POPUP | WS_VISIBLE, // <-- borderless window
800, 0, // <-- position on 2nd screen
1920, 1080, // <-- fill entire 2nd screen
NULL, NULL, hInstance, NULL);
...
D3DPRESENT_PARAMETERS m_param;
ZeroMemory(&m_param, sizeof(m_param));
m_param.BackBufferWidth = 1920;
m_param.BackBufferHeight = 1080;
m_param.BackBufferFormat = D3DFMT_X8R8G8B8;
m_param.BackBufferCount = 1;
m_param.MultiSampleType = D3DMULTISAMPLE_NONE;
m_param.SwapEffect = D3DSWAPEFFECT_COPY;
m_param.hDeviceWindow = hwnd;
m_param.Windowed = TRUE; // <-- use windowed mode
m_param.Flags = D3DPRESENTFLAG_VIDEO;
m_param.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
m_param.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

Video Window does not respond, windowed mode, DirectShow

I wanted to use Vmr 9 instead of default Vmr 7, so I created it with CoCreateInstance and added to the graph with AddFilter method. Next, I build graph with RenderFile method. Now renderer is connected, so I query for IVideoWindow interface and call put_Owner method with window handle created in child thread. When Run method is called, video is displayed in the window, but window doesn't handle messages, so you can't move it or resize. Windowless mode works fine. Why window behaves this way?
#include <dshow.h>
#include <D3D9.h>
#include <Vmr9.h>
#pragma comment(lib, "Strmiids.lib")
#pragma comment(lib, "D3d9.lib")
#define WM_GRAPHNOTIFY WM_APP + 1
DWORD HandleGraphEvent(IMediaEventEx* media_event)
{
long EvCode;
LONG_PTR param1, param2;
while (media_event->GetEvent(&EvCode, &param1, &param2, 0) == S_OK)
{
media_event->FreeEventParams(EvCode, param1, param2);
switch (EvCode)
{
case EC_COMPLETE:
printf("All data is rendered\n");
return 0;
case EC_USERABORT:
printf("User has closed the window\n");
return 0;
case EC_ERRORABORT:
printf("Error occured\n");
return 0;
}
}
return 1;
}
struct WindowThreadParam
{
HANDLE event;
HWND window;
IMediaEventEx* media_event;
};
LRESULT WINAPI WindowProcedure(HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
WindowThreadParam* param = (WindowThreadParam*)lp;
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_GRAPHNOTIFY:
HandleGraphEvent(param->media_event);
default:
return DefWindowProc(window, msg, wp, lp);
}
}
DWORD WINAPI WindowThread(WindowThreadParam* param)
{
LPCWSTR myclass = TEXT("myclass");
WNDCLASSEX wndclass =
{ sizeof(WNDCLASSEX), CS_DBLCLKS, WindowProcedure,
0, 0, GetModuleHandle(0), LoadIcon(0, IDI_APPLICATION),
LoadCursor(0, IDC_ARROW), HBRUSH(COLOR_WINDOW + 1),
0, myclass, LoadIcon(0, IDI_APPLICATION) };
HWND window = NULL;
if (RegisterClassEx(&wndclass))
{
window = CreateWindowEx(0, myclass, TEXT("title"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
if (window)
{
ShowWindow(window, SW_SHOWDEFAULT);
param->window = window;
SetEvent(param->event);
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg);
}
else SetEvent(param->event);
UnregisterClass(myclass, GetModuleHandle(0));
}
else SetEvent(param->event);
return 0;
}
void _tmain(int argc, _TCHAR* argv[])
{
HANDLE event_handle;
event_handle = CreateEvent(NULL, 0, 0, NULL); // auto, unsignaled
WindowThreadParam param;
ZeroMemory(&param, sizeof(param));
param.event = event_handle;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WindowThread, &param, 0, NULL);
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
printf("ERROR - Could not initialize COM library");
return;
}
IGraphBuilder* graph_builder;
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&graph_builder);
IBaseFilter* Vrm;
hr = CoCreateInstance(CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&Vrm);
hr = graph_builder->AddFilter(Vrm, L"Video Mixing Renderer");
hr = graph_builder->RenderFile(L"anim.avi", NULL);
hr = graph_builder->QueryInterface(IID_IMediaControl, (void **)&pControl);
hr = graph_builder->QueryInterface(IID_IMediaEventEx, (void **)&pEvent);
IVideoWindow* VideoWindow;
RECT window_client_area;
WaitForSingleObject(param.event, INFINITE);
if (param.window)
{
// set window
hr = graph_builder->QueryInterface(IID_IVideoWindow, (void**)&VideoWindow);
hr = VideoWindow->put_Owner((OAHWND)param.window);
hr = VideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
// set video position
GetClientRect(param.window, &window_client_area);
hr = VideoWindow->SetWindowPosition(0, 0, window_client_area.right, window_client_area.bottom);
param.media_event = pEvent;
hr = pEvent->SetNotifyWindow((OAHWND)param.window, WM_GRAPHNOTIFY, (LONG_PTR)&param);
}
hr = pControl->Run();
// allow child thread to run
getchar();
pControl->Release();
pEvent->Release();
graph_builder->Release();
CoUninitialize();
}
Your main thread sleeps in getchar(); when it is supposed to implement message pump and deliver window messages, both as COM requirement and for the helper window VMR creates in the main thread.
Your background thread is irrelevant here since your controlling DirectShow activity takes place in _tmain.