574 lines
13 KiB
C++
574 lines
13 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// winmain.cpp : Application entry-point
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "MFCaptureD3D.h"
|
|
#include "resource.h"
|
|
|
|
// Include the v6 common controls in the manifest
|
|
#pragma comment(linker, \
|
|
"\"/manifestdependency:type='Win32' "\
|
|
"name='Microsoft.Windows.Common-Controls' "\
|
|
"version='6.0.0.0' "\
|
|
"processorArchitecture='*' "\
|
|
"publicKeyToken='6595b64144ccf1df' "\
|
|
"language='*'\"")
|
|
|
|
|
|
//
|
|
// ChooseDeviceParam structure
|
|
//
|
|
// Holds an array of IMFActivate pointers that represent video
|
|
// capture devices.
|
|
//
|
|
|
|
struct ChooseDeviceParam
|
|
{
|
|
IMFActivate **ppDevices; // Array of IMFActivate pointers.
|
|
UINT32 count; // Number of elements in the array.
|
|
UINT32 selection; // Selected device, by array index.
|
|
};
|
|
|
|
|
|
|
|
BOOL InitializeApplication();
|
|
BOOL InitializeWindow(HWND *pHwnd);
|
|
void CleanUp();
|
|
INT MessageLoop(HWND hwnd);
|
|
|
|
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
void ShowErrorMessage(PCWSTR format, HRESULT hr);
|
|
|
|
// Window message handlers
|
|
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
|
|
void OnClose(HWND hwnd);
|
|
void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
|
|
void OnSize(HWND hwnd, UINT state, int cx, int cy);
|
|
void OnDeviceChange(HWND hwnd, DEV_BROADCAST_HDR *pHdr);
|
|
|
|
// Command handlers
|
|
void OnChooseDevice(HWND hwnd, BOOL bPrompt);
|
|
|
|
|
|
// Constants
|
|
const WCHAR CLASS_NAME[] = L"MFCapture Window Class";
|
|
const WCHAR WINDOW_NAME[] = L"MFCapture Sample Application";
|
|
|
|
|
|
// Global variables
|
|
|
|
CPreview *g_pPreview = NULL;
|
|
HDEVNOTIFY g_hdevnotify = NULL;
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// WinMain
|
|
//
|
|
// Application entry-point.
|
|
//-------------------------------------------------------------------
|
|
|
|
INT WINAPI wWinMain(HINSTANCE,HINSTANCE,LPWSTR,INT)
|
|
{
|
|
HWND hwnd = 0;
|
|
|
|
(void)HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
|
|
|
if (InitializeApplication() && InitializeWindow(&hwnd))
|
|
{
|
|
MessageLoop(hwnd);
|
|
}
|
|
|
|
CleanUp();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// WindowProc
|
|
//
|
|
// Window procedure.
|
|
//-------------------------------------------------------------------
|
|
|
|
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
|
|
HANDLE_MSG(hwnd, WM_CLOSE, OnClose);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hwnd, WM_SIZE, OnSize);
|
|
|
|
case WM_APP_PREVIEW_ERROR:
|
|
ShowErrorMessage(L"Error", (HRESULT)wParam);
|
|
break;
|
|
|
|
case WM_DEVICECHANGE:
|
|
OnDeviceChange(hwnd, (PDEV_BROADCAST_HDR)lParam);
|
|
break;
|
|
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
}
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// InitializeApplication
|
|
//
|
|
// Initializes the application.
|
|
//-------------------------------------------------------------------
|
|
|
|
BOOL InitializeApplication()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = MFStartup(MF_VERSION);
|
|
}
|
|
|
|
return (SUCCEEDED(hr));
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// CleanUp
|
|
//
|
|
// Releases resources.
|
|
//-------------------------------------------------------------------
|
|
|
|
void CleanUp()
|
|
{
|
|
if (g_hdevnotify)
|
|
{
|
|
UnregisterDeviceNotification(g_hdevnotify);
|
|
}
|
|
|
|
if (g_pPreview)
|
|
{
|
|
g_pPreview->CloseDevice();
|
|
}
|
|
|
|
SafeRelease(&g_pPreview);
|
|
|
|
MFShutdown();
|
|
CoUninitialize();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// InitializeWindow
|
|
//
|
|
// Creates the application window.
|
|
//-------------------------------------------------------------------
|
|
|
|
BOOL InitializeWindow(HWND *pHwnd)
|
|
{
|
|
WNDCLASS wc = {0};
|
|
|
|
wc.lpfnWndProc = WindowProc;
|
|
wc.hInstance = GetModuleHandle(NULL);
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.lpszClassName = CLASS_NAME;
|
|
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
|
|
|
|
if (!RegisterClass(&wc))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
HWND hwnd = CreateWindow(
|
|
CLASS_NAME,
|
|
WINDOW_NAME,
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
GetModuleHandle(NULL),
|
|
NULL
|
|
);
|
|
|
|
if (!hwnd)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ShowWindow(hwnd, SW_SHOWDEFAULT);
|
|
UpdateWindow(hwnd);
|
|
|
|
*pHwnd = hwnd;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// MessageLoop
|
|
//
|
|
// Implements the window message loop.
|
|
//-------------------------------------------------------------------
|
|
|
|
INT MessageLoop(HWND hwnd)
|
|
{
|
|
MSG msg = {0};
|
|
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
DestroyWindow(hwnd);
|
|
|
|
return INT(msg.wParam);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnCreate
|
|
//
|
|
// Handles the WM_CREATE message.
|
|
//-------------------------------------------------------------------
|
|
|
|
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Register this window to get device notification messages.
|
|
|
|
DEV_BROADCAST_DEVICEINTERFACE di = { 0 };
|
|
di.dbcc_size = sizeof(di);
|
|
di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
di.dbcc_classguid = KSCATEGORY_CAPTURE;
|
|
|
|
g_hdevnotify = RegisterDeviceNotification(
|
|
hwnd,
|
|
&di,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE
|
|
);
|
|
|
|
if (g_hdevnotify == NULL)
|
|
{
|
|
ShowErrorMessage(L"RegisterDeviceNotification failed.", HRESULT_FROM_WIN32(GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the object that manages video preview.
|
|
hr = CPreview::CreateInstance(hwnd, hwnd, &g_pPreview);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ShowErrorMessage(L"CPreview::CreateInstance failed.", hr);
|
|
return FALSE;
|
|
}
|
|
|
|
// Select the first available device (if any).
|
|
OnChooseDevice(hwnd, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnClose
|
|
//
|
|
// Handles WM_CLOSE messages.
|
|
//-------------------------------------------------------------------
|
|
|
|
void OnClose(HWND /*hwnd*/)
|
|
{
|
|
PostQuitMessage(0);
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnSize
|
|
//
|
|
// Handles WM_SIZE messages.
|
|
//-------------------------------------------------------------------
|
|
|
|
void OnSize(HWND hwnd, UINT /*state */, int cx, int cy)
|
|
{
|
|
if (g_pPreview)
|
|
{
|
|
g_pPreview->ResizeVideo((WORD)cx, (WORD)cy);
|
|
|
|
InvalidateRect(hwnd, NULL, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnCommand
|
|
//
|
|
// Handles WM_COMMAND messages
|
|
//-------------------------------------------------------------------
|
|
|
|
void OnCommand(HWND hwnd, int id, HWND /*hwndCtl*/, UINT /*codeNotify*/)
|
|
{
|
|
switch (id)
|
|
{
|
|
case ID_FILE_CHOOSEDEVICE:
|
|
OnChooseDevice(hwnd, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnChooseDevice
|
|
//
|
|
// Select a video capture device.
|
|
//
|
|
// hwnd: A handle to the application window.
|
|
/// bPrompt: If TRUE, prompt to user to select the device. Otherwise,
|
|
// select the first device in the list.
|
|
//-------------------------------------------------------------------
|
|
|
|
void OnChooseDevice(HWND hwnd, BOOL bPrompt)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ChooseDeviceParam param = { 0 };
|
|
|
|
UINT iDevice = 0; // Index into the array of devices
|
|
BOOL bCancel = FALSE;
|
|
|
|
IMFAttributes *pAttributes = NULL;
|
|
|
|
// Initialize an attribute store to specify enumeration parameters.
|
|
|
|
hr = MFCreateAttributes(&pAttributes, 1);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Ask for source type = video capture devices.
|
|
|
|
hr = pAttributes->SetGUID(
|
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
|
|
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
|
|
);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// Enumerate devices.
|
|
hr = MFEnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count);
|
|
|
|
if (FAILED(hr)) { goto done; }
|
|
|
|
// NOTE: param.count might be zero.
|
|
|
|
if (bPrompt)
|
|
{
|
|
// Ask the user to select a device.
|
|
|
|
INT_PTR result = DialogBoxParam(
|
|
GetModuleHandle(NULL),
|
|
MAKEINTRESOURCE(IDD_CHOOSE_DEVICE),
|
|
hwnd,
|
|
DlgProc,
|
|
(LPARAM)¶m
|
|
);
|
|
|
|
if (result == IDOK)
|
|
{
|
|
iDevice = param.selection;
|
|
}
|
|
else
|
|
{
|
|
bCancel = TRUE; // User cancelled
|
|
}
|
|
}
|
|
|
|
if (!bCancel && (param.count > 0))
|
|
{
|
|
// Give this source to the CPlayer object for preview.
|
|
hr = g_pPreview->SetDevice( param.ppDevices[iDevice] );
|
|
}
|
|
|
|
done:
|
|
|
|
SafeRelease(&pAttributes);
|
|
|
|
for (DWORD i = 0; i < param.count; i++)
|
|
{
|
|
SafeRelease(¶m.ppDevices[i]);
|
|
}
|
|
CoTaskMemFree(param.ppDevices);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ShowErrorMessage(L"Cannot create a video capture device", hr);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnDeviceChange
|
|
//
|
|
// Handles WM_DEVICECHANGE messages.
|
|
//-------------------------------------------------------------------
|
|
|
|
void OnDeviceChange(HWND hwnd, DEV_BROADCAST_HDR *pHdr)
|
|
{
|
|
if (g_pPreview == NULL || pHdr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
BOOL bDeviceLost = FALSE;
|
|
|
|
// Check if the current device was lost.
|
|
|
|
hr = g_pPreview->CheckDeviceLost(pHdr, &bDeviceLost);
|
|
|
|
if (FAILED(hr) || bDeviceLost)
|
|
{
|
|
g_pPreview->CloseDevice();
|
|
|
|
MessageBox(hwnd, L"Lost the capture device.", WINDOW_NAME, MB_OK);
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// Dialog functions
|
|
|
|
void OnInitDialog(HWND hwnd, ChooseDeviceParam *pParam);
|
|
HRESULT OnOK(HWND hwnd, ChooseDeviceParam *pParam);
|
|
|
|
//-------------------------------------------------------------------
|
|
// DlgProc
|
|
//
|
|
// Dialog procedure for the "Select Device" dialog.
|
|
//-------------------------------------------------------------------
|
|
|
|
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static ChooseDeviceParam *pParam = NULL;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
pParam = (ChooseDeviceParam*)lParam;
|
|
OnInitDialog(hwnd, pParam);
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDOK:
|
|
OnOK(hwnd, pParam);
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
return TRUE;
|
|
|
|
case IDCANCEL:
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------
|
|
// OnInitDialog
|
|
//
|
|
// Handles the WM_INITDIALOG message.
|
|
//-------------------------------------------------------------------
|
|
|
|
void OnInitDialog(HWND hwnd, ChooseDeviceParam *pParam)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Populate the list with the friendly names of the devices.
|
|
|
|
HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);
|
|
|
|
for (DWORD i = 0; i < pParam->count; i++)
|
|
{
|
|
WCHAR *szFriendlyName = NULL;
|
|
|
|
hr = pParam->ppDevices[i]->GetAllocatedString(
|
|
MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
|
|
&szFriendlyName,
|
|
NULL
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
int index = ListBox_AddString(hList, szFriendlyName);
|
|
|
|
ListBox_SetItemData(hList, index, i);
|
|
|
|
CoTaskMemFree(szFriendlyName);
|
|
}
|
|
|
|
// Assume no selection for now.
|
|
pParam->selection = (UINT32)-1;
|
|
|
|
if (pParam->count == 0)
|
|
{
|
|
// If there are no devices, disable the "OK" button.
|
|
EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT OnOK(HWND hwnd, ChooseDeviceParam *pParam)
|
|
{
|
|
HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);
|
|
|
|
int sel = ListBox_GetCurSel(hList);
|
|
|
|
if (sel != LB_ERR)
|
|
{
|
|
pParam->selection = (UINT32)ListBox_GetItemData(hList, sel);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void ShowErrorMessage(PCWSTR format, HRESULT hrErr)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
WCHAR msg[MAX_PATH];
|
|
|
|
hr = StringCbPrintf(msg, sizeof(msg), L"%s (hr=0x%X)", format, hrErr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MessageBox(NULL, msg, L"Error", MB_ICONERROR);
|
|
}
|
|
else
|
|
{
|
|
DebugBreak();
|
|
}
|
|
}
|