//////////////////////////////////////////////////////////////////////////
//
// Media Foundation video capture sample.
//
// 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 <windows.h>
#include <windowsx.h>
#include <mfapi.h>
#include <mfplay.h>
#include <strsafe.h>
#include <Dbt.h>
#include <ks.h>
#include <ksmedia.h>
#include "videoInput.h"
#pragma comment(lib, "videoInput")
#include "resource.h"
#include "preview.h"
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
// 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 struct
//
// Contains an array of IMFActivate pointers. Each pointer represents
// a video capture device. This struct is passed to the dialog where
// the user selects a device.
//-------------------------------------------------------------------
//struct ChooseDeviceParam
//{
// IMFActivate **ppDevices;
// UINT32 count;
// UINT32 selection;
// UINT32 selectedtype;
// UINT32 selectedtypecount;
//};
wchar_t *createTypeTitle(MediaType type)
{
wchar_t *out = new wchar_t[512];
wsprintf(out, L"size frame: %i x %i, framerate: %i fps, TypeVideoStream: %s, VIDEO_LIGHTING: %i",type.width, type.height, type.MF_MT_FRAME_RATE, type.pMF_MT_SUBTYPEName, type.MF_MT_VIDEO_LIGHTING);
return out;
}
BOOL InitializeApp();
BOOL InitializeWindow(HWND *pHwnd);
void CleanUp();
INT MessageLoop(HWND hwnd);
void ShowErrorMessage(HWND hwnd, PCWSTR format, HRESULT hr);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR CALLBACK DlgTypeProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// Window message handlers
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
void OnClose(HWND hwnd);
void OnPaint(HWND hwnd);
void OnSize(HWND hwnd, UINT state, int cx, int cy);
void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify);
void OnDeviceLost(HWND hwnd, DEV_BROADCAST_HDR *pHdr);
// Command handlers
void OnChooseDevice(HWND hwnd);
// Constants
const WCHAR CLASS_NAME[] = L"SimpleCapture Window Class";
const WCHAR WINDOW_NAME[] = L"SimpleCapture Sample Application";
// Global variables
CPreview *g_pPreview = NULL;
HDEVNOTIFY g_hdevnotify = NULL;
//-------------------------------------------------------------------
// WinMain: Application entry point
//-------------------------------------------------------------------
INT WINAPI wWinMain(HINSTANCE,HINSTANCE,LPWSTR,INT)
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
HWND hwnd = 0;
if (InitializeApp() && 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_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_SIZE, OnSize);
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
case WM_DEVICECHANGE:
OnDeviceLost(hwnd, (PDEV_BROADCAST_HDR)lParam);
return TRUE;
case WM_ERASEBKGND:
return 1;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
//-------------------------------------------------------------------
// InitializeApp: One-time initialization
//-------------------------------------------------------------------
BOOL InitializeApp()
{
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
return (SUCCEEDED(hr));
}
//-------------------------------------------------------------------
// CleanUp: Frees resources before the application exits
//-------------------------------------------------------------------
void CleanUp()
{
if (g_pPreview)
{
g_pPreview->CloseDevice();
SafeRelease(&g_pPreview);
}
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: Message loop for the main application window.
//-------------------------------------------------------------------
INT MessageLoop(HWND hwnd)
{
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DestroyWindow(hwnd);
return INT(msg.wParam);
}
//-------------------------------------------------------------------
// OnCreate: Handler for WM_CREATE
//-------------------------------------------------------------------
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT /*lpCreateStruct*/)
{
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(
hwnd,
L"RegisterDeviceNotification failed.",
HRESULT_FROM_WIN32(GetLastError())
);
return FALSE;
}
return TRUE;
}
//-------------------------------------------------------------------
// OnClose: Handler for WM_CLOSE
//-------------------------------------------------------------------
void OnClose(HWND /*hwnd*/)
{
if (g_hdevnotify)
{
UnregisterDeviceNotification(g_hdevnotify);
}
PostQuitMessage(0);
}
//-------------------------------------------------------------------
// OnPaint: Handler for WM_PAINT
//-------------------------------------------------------------------
void OnPaint(HWND hwnd)
{
PAINTSTRUCT ps;
HDC hdc = 0;
hdc = BeginPaint(hwnd, &ps);
if (hdc)
{
if (g_pPreview && g_pPreview->HasVideo())
{
g_pPreview->UpdateVideo();
}
else
{
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_APPWORKSPACE+1));
}
}
EndPaint(hwnd, &ps);
}
//-------------------------------------------------------------------
// OnSize: Handler for WM_SIZE
//-------------------------------------------------------------------
void OnSize(HWND /*hwnd*/, UINT state, int /*cx*/, int /*cy*/)
{
if (state == SIZE_RESTORED)
{
if (g_pPreview)
{
// Resize the video to cover the entire client area.
g_pPreview->UpdateVideo();
}
}
}
//-------------------------------------------------------------------
// OnCommand: Handler for WM_COMMAND
//-------------------------------------------------------------------
void OnCommand(HWND hwnd, int id, HWND /*hwndCtl*/, UINT /*codeNotify*/)
{
switch (id)
{
case ID_FILE_CHOOSEDEVICE:
OnChooseDevice(hwnd);
break;
}
}
//-------------------------------------------------------------------
// OnChooseDevice
//
// Displays a dialog for the user to select a capture device.
//-------------------------------------------------------------------
void OnChooseDevice(HWND hwnd)
{
HRESULT hr = S_OK;
ChooseDeviceParam param = { 0 };
//IMFAttributes *pAttributes = NULL;
// Release the previous instance of the preview object, if any.
if (g_pPreview)
{
g_pPreview->CloseDevice();
SafeRelease(&g_pPreview);
}
//// Create a new instance of the preview object.
hr = CPreview::CreateInstance(hwnd, &g_pPreview);
//// Create an attribute store to specify the enumeration parameters.
//if (SUCCEEDED(hr))
//{
// hr = MFCreateAttributes(&pAttributes, 1);
//}
// Ask for source type = video capture devices
//if (SUCCEEDED(hr))
//{
// hr = pAttributes->SetGUID(
// MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
// MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
// );
//}
//// Enumerate devices.
//if (SUCCEEDED(hr))
//{
// hr = MFEnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count);
//}
if (SUCCEEDED(hr))
{
// Ask the user to select one.
INT_PTR result = DialogBoxParam(
GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_CHOOSE_DEVICE),
hwnd,
DlgProc,
(LPARAM)¶m
);
if ((result == IDOK) && (param.selection != (UINT32)-1))
{
INT_PTR result = DialogBoxParam(
GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_CHOOSE_DEVICE),
hwnd,
DlgTypeProc,
(LPARAM)¶m
);
if ((result == IDOK) && (param.selectedtype != (UINT32)-1))
{
if ((param.selection >= param.count)||(param.selectedtype >= param.selectedtypecount))
{
hr = E_UNEXPECTED;
}
else
{
// Give this source to the CPreview object for preview.
hr = g_pPreview->SetDevice(param);
}
}
}
}
//SafeRelease(&pAttributes);
//for (DWORD i = 0; i < param.count; i++)
//{
// SafeRelease(¶m.ppDevices[i]);
//}
//CoTaskMemFree(param.ppDevices);
if (FAILED(hr))
{
ShowErrorMessage(hwnd, L"Cannot create the video capture device", hr);
}
}
void OnDeviceLost(HWND hwnd, DEV_BROADCAST_HDR *pHdr)
{
if (g_pPreview == NULL || pHdr == NULL)
{
return;
}
HRESULT hr = S_OK;
BOOL bDeviceLost = FALSE;
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
HRESULT OnInitDialog(HWND hwnd, ChooseDeviceParam *pParam);
HRESULT OnInitTypeDialog(HWND hwnd, ChooseDeviceParam *pParam);
HRESULT OnOK(HWND hwnd, ChooseDeviceParam *pParam);
HRESULT OnTypeOK(HWND hwnd, ChooseDeviceParam *pParam);
//-------------------------------------------------------------------
// DlgTypeProc: Window procedure for the dialog of chose of Type.
//-------------------------------------------------------------------
INT_PTR CALLBACK DlgTypeProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static ChooseDeviceParam *pParam = NULL;
switch (msg)
{
case WM_INITDIALOG:
pParam = (ChooseDeviceParam*)lParam;
OnInitTypeDialog(hwnd, pParam);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDOK:
OnTypeOK(hwnd, pParam);
EndDialog(hwnd, LOWORD(wParam));
return TRUE;
case IDCANCEL:
EndDialog(hwnd, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
//-------------------------------------------------------------------
// OnInitDialog: Handler for WM_INITDIALOG
//-------------------------------------------------------------------
HRESULT OnInitTypeDialog(HWND hwnd, ChooseDeviceParam *pParam)
{
HRESULT hr = S_OK;
HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);
videoInput *VI = &videoInput::getInstance();
// Display a list of the types.
pParam->selectedtypecount = VI->getCountFormats(pParam->selection);
for (DWORD i = 0; i < pParam->selectedtypecount; i++)
{
WCHAR *szFriendlyName = NULL;
int index = ListBox_AddString(hList, createTypeTitle(VI->getFormat(pParam->selection, i)));
ListBox_SetItemData(hList, index, i);
CoTaskMemFree(szFriendlyName);
}
// Assume no selection for now.
pParam->selectedtype = (UINT32)-1;
if (pParam->count == 0)
{
// If there are no devices, disable the "OK" button.
EnableWindow(GetDlgItem(hwnd, IDOK), FALSE);
}
else
{
// Select the first device in the list.
ListBox_SetCurSel(hList, 0);
}
return hr;
}
//-------------------------------------------------------------------
// DlgProc: Window procedure for the 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: Handler for WM_INITDIALOG
//-------------------------------------------------------------------
HRESULT OnInitDialog(HWND hwnd, ChooseDeviceParam *pParam)
{
HRESULT hr = S_OK;
HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);
videoInput *VI = &videoInput::getInstance();
// Display a list of the devices.
pParam->count = VI->listDevices(true);
for (DWORD i = 0; i < pParam->count; i++)
{
WCHAR *szFriendlyName = NULL;
int index = ListBox_AddString(hList, VI->getNameVideoDevice(i));
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);
}
else
{
// Select the first device in the list.
ListBox_SetCurSel(hList, 0);
}
return hr;
}
//-------------------------------------------------------------------
// OnOK: Handler for the OK button
//-------------------------------------------------------------------
HRESULT OnOK(HWND hwnd, ChooseDeviceParam *pParam)
{
HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);
// Get the current selection and return it to the application.
int sel = ListBox_GetCurSel(hList);
if (sel != LB_ERR)
{
pParam->selection = (UINT32)ListBox_GetItemData(hList, sel);
}
return S_OK;
}
//-------------------------------------------------------------------
// OnOK: Handler for the OK button
//-------------------------------------------------------------------
HRESULT OnTypeOK(HWND hwnd, ChooseDeviceParam *pParam)
{
HWND hList = GetDlgItem(hwnd, IDC_DEVICE_LIST);
// Get the current selection and return it to the application.
int sel = ListBox_GetCurSel(hList);
if (sel != LB_ERR)
{
pParam->selectedtype = (UINT32)ListBox_GetItemData(hList, sel);
}
return S_OK;
}
//-------------------------------------------------------------------
// ShowErrorMessage
//
// Displays an error message.
//-------------------------------------------------------------------
void ShowErrorMessage(HWND hwnd, 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(hwnd, msg, L"Error", MB_ICONERROR);
}
}