////////////////////////////////////////////////////////////////////////// // // 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 #include #include #include #include #include #include #include #include #include #include template void SafeRelease(T **ppT) { if (*ppT) { (*ppT)->Release(); *ppT = NULL; } } #include "capture.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='*'\"") enum FileContainer { FileContainer_MP4 = IDC_CAPTURE_MP4, FileContainer_WMV = IDC_CAPTURE_WMV }; DeviceList g_devices; CCapture *g_pCapture = NULL; HDEVNOTIFY g_hdevnotify = NULL; const UINT32 TARGET_BIT_RATE = 240 * 1000; INT_PTR CALLBACK DialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); void OnInitDialog(HWND hDlg); void OnCloseDialog(); void UpdateUI(HWND hDlg); void StopCapture(HWND hDlg); void StartCapture(HWND hDlg); void OnSelectEncodingType(HWND hDlg, FileContainer file); HRESULT GetSelectedDevice(HWND hDlg, IMFActivate **ppActivate); HRESULT UpdateDeviceList(HWND hDlg); void OnDeviceChange(HWND hwnd, WPARAM reason, DEV_BROADCAST_HDR *pHdr); void NotifyError(HWND hwnd, const WCHAR *sErrorMessage, HRESULT hrErr); void EnableDialogControl(HWND hDlg, int nIDDlgItem, BOOL bEnable); INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPWSTR /*lpCmdLine*/, INT /*nCmdShow*/) { (void)HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); INT_PTR ret = DialogBox( hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc ); if (ret == 0 || ret == -1) { MessageBox( NULL, L"Could not create dialog", L"Error", MB_OK | MB_ICONERROR ); } return 0; } //----------------------------------------------------------------------------- // Dialog procedure //----------------------------------------------------------------------------- INT_PTR CALLBACK DialogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: OnInitDialog(hDlg); break; case WM_DEVICECHANGE: OnDeviceChange(hDlg, wParam, (PDEV_BROADCAST_HDR)lParam); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_CAPTURE_MP4: // Fall through case IDC_CAPTURE_WMV: OnSelectEncodingType(hDlg, (FileContainer)(LOWORD(wParam))); return TRUE; case IDC_CAPTURE: if (g_pCapture && g_pCapture->IsCapturing()) { StopCapture(hDlg); } else { StartCapture(hDlg); } return TRUE; case IDCANCEL: OnCloseDialog(); ::EndDialog(hDlg, IDCANCEL); return TRUE; } break; } return FALSE; } //----------------------------------------------------------------------------- // OnInitDialog // Handler for WM_INITDIALOG message. //----------------------------------------------------------------------------- void OnInitDialog(HWND hDlg) { HRESULT hr = S_OK; HWND hEdit = GetDlgItem(hDlg, IDC_OUTPUT_FILE); SetWindowText(hEdit, TEXT("capture.mp4")); CheckRadioButton(hDlg, IDC_CAPTURE_MP4, IDC_CAPTURE_WMV, IDC_CAPTURE_MP4); // Initialize the COM library hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // Initialize Media Foundation if (SUCCEEDED(hr)) { hr = MFStartup(MF_VERSION); } // Register for device notifications if (SUCCEEDED(hr)) { DEV_BROADCAST_DEVICEINTERFACE di = { 0 }; di.dbcc_size = sizeof(di); di.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; di.dbcc_classguid = KSCATEGORY_CAPTURE; g_hdevnotify = RegisterDeviceNotification( hDlg, &di, DEVICE_NOTIFY_WINDOW_HANDLE ); if (g_hdevnotify == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } } // Enumerate the video capture devices. if (SUCCEEDED(hr)) { hr = UpdateDeviceList(hDlg); } if (SUCCEEDED(hr)) { UpdateUI(hDlg); if (g_devices.Count() == 0) { ::MessageBox( hDlg, TEXT("Could not find any video capture devices."), TEXT("MFCaptureToFile"), MB_OK ); } } else { OnCloseDialog(); ::EndDialog(hDlg, 0); } } //----------------------------------------------------------------------------- // OnCloseDialog // // Frees resources before closing the dialog. //----------------------------------------------------------------------------- void OnCloseDialog() { if (g_pCapture) { g_pCapture->EndCaptureSession(); } SafeRelease(&g_pCapture); g_devices.Clear(); if (g_hdevnotify) { UnregisterDeviceNotification(g_hdevnotify); } MFShutdown(); CoUninitialize(); } //----------------------------------------------------------------------------- // StartCapture // // Starts video capture. //----------------------------------------------------------------------------- void StartCapture(HWND hDlg) { EncodingParameters params; if (BST_CHECKED == IsDlgButtonChecked(hDlg, IDC_CAPTURE_WMV)) { params.subtype = MFVideoFormat_WMV3; } else { params.subtype = MFVideoFormat_H264; } params.bitrate = TARGET_BIT_RATE; HRESULT hr = S_OK; WCHAR pszFile[MAX_PATH] = { 0 }; HWND hEdit = GetDlgItem(hDlg, IDC_OUTPUT_FILE); IMFActivate *pActivate = NULL; // Get the name of the target file. if (0 == GetWindowText(hEdit, pszFile, MAX_PATH)) { hr = HRESULT_FROM_WIN32(GetLastError()); } // Create the media source for the capture device. if (SUCCEEDED(hr)) { hr = GetSelectedDevice(hDlg, &pActivate); } // Start capturing. if (SUCCEEDED(hr)) { hr = CCapture::CreateInstance(hDlg, &g_pCapture); } if (SUCCEEDED(hr)) { hr = g_pCapture->StartCapture(pActivate, pszFile, params); } if (SUCCEEDED(hr)) { UpdateUI(hDlg); } SafeRelease(&pActivate); if (FAILED(hr)) { NotifyError(hDlg, L"Error starting capture.", hr); } } //----------------------------------------------------------------------------- // StopCapture // // Stops video capture. //----------------------------------------------------------------------------- void StopCapture(HWND hDlg) { HRESULT hr = S_OK; hr = g_pCapture->EndCaptureSession(); SafeRelease(&g_pCapture); UpdateDeviceList(hDlg); // NOTE: Updating the device list releases the existing IMFActivate // pointers. This ensures that the current instance of the video capture // source is released. UpdateUI(hDlg); if (FAILED(hr)) { NotifyError(hDlg, L"Error stopping capture. File might be corrupt.", hr); } } //----------------------------------------------------------------------------- // CreateSelectedDevice // // Create a media source for the video capture device selected by the user. //----------------------------------------------------------------------------- HRESULT GetSelectedDevice(HWND hDlg, IMFActivate **ppActivate) { HWND hDeviceList = GetDlgItem(hDlg, IDC_DEVICE_LIST); // First get the index of the selected item in the combo box. int iListIndex = ComboBox_GetCurSel(hDeviceList); if (iListIndex == CB_ERR) { return HRESULT_FROM_WIN32(GetLastError()); } // Now find the index of the device within the device list. // // This index is stored as item data in the combo box, so that // the order of the combo box items does not need to match the // order of the device list. LRESULT iDeviceIndex = ComboBox_GetItemData(hDeviceList, iListIndex); if (iDeviceIndex == CB_ERR) { return HRESULT_FROM_WIN32(GetLastError()); } // Now create the media source. return g_devices.GetDevice((UINT32)iDeviceIndex, ppActivate); } //----------------------------------------------------------------------------- // UpdateDeviceList // // Enumerates the video capture devices and populates the list of device // names in the dialog UI. //----------------------------------------------------------------------------- HRESULT UpdateDeviceList(HWND hDlg) { HRESULT hr = S_OK; WCHAR *szFriendlyName = NULL; HWND hCombobox = GetDlgItem(hDlg, IDC_DEVICE_LIST); ComboBox_ResetContent( hCombobox ); g_devices.Clear(); hr = g_devices.EnumerateDevices(); if (FAILED(hr)) { goto done; } for (UINT32 iDevice = 0; iDevice < g_devices.Count(); iDevice++) { // Get the friendly name of the device. hr = g_devices.GetDeviceName(iDevice, &szFriendlyName); if (FAILED(hr)) { goto done; } // Add the string to the combo-box. This message returns the index in the list. int iListIndex = ComboBox_AddString(hCombobox, szFriendlyName); if (iListIndex == CB_ERR || iListIndex == CB_ERRSPACE) { hr = E_FAIL; goto done; } // The list might be sorted, so the list index is not always the same as the // array index. Therefore, set the array index as item data. int result = ComboBox_SetItemData(hCombobox, iListIndex, iDevice); if (result == CB_ERR) { hr = E_FAIL; goto done; } CoTaskMemFree(szFriendlyName); szFriendlyName = NULL; } if (g_devices.Count() > 0) { // Select the first item. ComboBox_SetCurSel(hCombobox, 0); } done: return hr; } //----------------------------------------------------------------------------- // OnSelectEncodingType // // Called when the user toggles between file-format types. //----------------------------------------------------------------------------- void OnSelectEncodingType(HWND hDlg, FileContainer file) { WCHAR pszFile[MAX_PATH] = { 0 }; HWND hEdit = GetDlgItem(hDlg, IDC_OUTPUT_FILE); GetWindowText(hEdit, pszFile, MAX_PATH); switch (file) { case FileContainer_MP4: PathRenameExtension(pszFile, L".mp4"); break; case FileContainer_WMV: PathRenameExtension(pszFile, L".wmv"); break; default: assert(false); break; } SetWindowText(hEdit, pszFile); } //----------------------------------------------------------------------------- // UpdateUI // // Updates the dialog UI for the current state. //----------------------------------------------------------------------------- void UpdateUI(HWND hDlg) { BOOL bEnable = (g_devices.Count() > 0); // Are there any capture devices? BOOL bCapturing = (g_pCapture != NULL); // Is video capture in progress now? HWND hButton = GetDlgItem(hDlg, IDC_CAPTURE); if (bCapturing) { SetWindowText(hButton, L"Stop Capture"); } else { SetWindowText(hButton, L"Start Capture"); } EnableDialogControl(hDlg, IDC_CAPTURE, bCapturing || bEnable); EnableDialogControl(hDlg, IDC_DEVICE_LIST, !bCapturing && bEnable); // The following cannot be changed while capture is in progress, // but are OK to change when there are no capture devices. EnableDialogControl(hDlg, IDC_CAPTURE_MP4, !bCapturing); EnableDialogControl(hDlg, IDC_CAPTURE_WMV, !bCapturing); EnableDialogControl(hDlg, IDC_OUTPUT_FILE, !bCapturing); } //----------------------------------------------------------------------------- // OnDeviceChange // // Handles WM_DEVICECHANGE messages. //----------------------------------------------------------------------------- void OnDeviceChange(HWND hDlg, WPARAM reason, DEV_BROADCAST_HDR *pHdr) { if (reason == DBT_DEVNODES_CHANGED || reason == DBT_DEVICEARRIVAL) { // Check for added/removed devices, regardless of whether // the application is capturing video at this time. UpdateDeviceList(hDlg); UpdateUI(hDlg); } // Now check if the current video capture device was lost. if (pHdr == NULL) { return; } if (pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) { return; } HRESULT hr = S_OK; BOOL bDeviceLost = FALSE; if (g_pCapture && g_pCapture->IsCapturing()) { hr = g_pCapture->CheckDeviceLost(pHdr, &bDeviceLost); if (FAILED(hr) || bDeviceLost) { StopCapture(hDlg); MessageBox(hDlg, L"The capture device was removed or lost.", L"Lost Device", MB_OK); } } } void NotifyError(HWND hwnd, const WCHAR *sErrorMessage, HRESULT hrErr) { const size_t MESSAGE_LEN = 512; WCHAR message[MESSAGE_LEN]; HRESULT hr = StringCchPrintf (message, MESSAGE_LEN, L"%s (HRESULT = 0x%X)", sErrorMessage, hrErr); if (SUCCEEDED(hr)) { MessageBox(hwnd, message, NULL, MB_OK | MB_ICONERROR); } } void EnableDialogControl(HWND hDlg, int nIDDlgItem, BOOL bEnable) { HWND hwnd = GetDlgItem(hDlg, nIDDlgItem); if (!bEnable && hwnd == GetFocus()) { // When disabling a control that has focus, set the // focus to the next control. ::SendMessage(GetParent(hwnd), WM_NEXTDLGCTL, 0, FALSE); } EnableWindow(hwnd, bEnable); }