////////////////////////////////////////////////////////////////////////// // // preview.cpp: Manages video preview. // // 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 //------------------------------------------------------------------- // CreateInstance // // Static class method to create the CPreview object. //------------------------------------------------------------------- HRESULT CPreview::CreateInstance( HWND hVideo, // Handle to the video window. HWND hEvent, // Handle to the window to receive notifications. CPreview **ppPlayer // Receives a pointer to the CPreview object. ) { assert(hVideo != NULL); assert(hEvent != NULL); if (ppPlayer == NULL) { return E_POINTER; } CPreview *pPlayer = new (std::nothrow) CPreview(hVideo, hEvent); // The CPlayer constructor sets the ref count to 1. if (pPlayer == NULL) { return E_OUTOFMEMORY; } HRESULT hr = pPlayer->Initialize(); if (SUCCEEDED(hr)) { *ppPlayer = pPlayer; (*ppPlayer)->AddRef(); } SafeRelease(&pPlayer); return hr; } //------------------------------------------------------------------- // constructor //------------------------------------------------------------------- CPreview::CPreview(HWND hVideo, HWND hEvent) : m_pReader(NULL), m_hwndVideo(hVideo), m_hwndEvent(hEvent), m_nRefCount(1), m_pwszSymbolicLink(NULL), m_cchSymbolicLink(0) { InitializeCriticalSection(&m_critsec); } //------------------------------------------------------------------- // destructor //------------------------------------------------------------------- CPreview::~CPreview() { CloseDevice(); m_draw.DestroyDevice(); DeleteCriticalSection(&m_critsec); } //------------------------------------------------------------------- // Initialize // // Initializes the object. //------------------------------------------------------------------- HRESULT CPreview::Initialize() { HRESULT hr = S_OK; hr = m_draw.CreateDevice(m_hwndVideo); return hr; } //------------------------------------------------------------------- // CloseDevice // // Releases all resources held by this object. //------------------------------------------------------------------- HRESULT CPreview::CloseDevice() { EnterCriticalSection(&m_critsec); SafeRelease(&m_pReader); CoTaskMemFree(m_pwszSymbolicLink); m_pwszSymbolicLink = NULL; m_cchSymbolicLink = 0; LeaveCriticalSection(&m_critsec); return S_OK; } /////////////// IUnknown methods /////////////// //------------------------------------------------------------------- // AddRef //------------------------------------------------------------------- ULONG CPreview::AddRef() { return InterlockedIncrement(&m_nRefCount); } //------------------------------------------------------------------- // Release //------------------------------------------------------------------- ULONG CPreview::Release() { ULONG uCount = InterlockedDecrement(&m_nRefCount); if (uCount == 0) { delete this; } // For thread safety, return a temporary variable. return uCount; } //------------------------------------------------------------------- // QueryInterface //------------------------------------------------------------------- HRESULT CPreview::QueryInterface(REFIID riid, void** ppv) { static const QITAB qit[] = { QITABENT(CPreview, IMFSourceReaderCallback), { 0 }, }; return QISearch(this, qit, riid, ppv); } /////////////// IMFSourceReaderCallback methods /////////////// //------------------------------------------------------------------- // OnReadSample // // Called when the IMFMediaSource::ReadSample method completes. //------------------------------------------------------------------- HRESULT CPreview::OnReadSample( HRESULT hrStatus, DWORD /* dwStreamIndex */, DWORD /* dwStreamFlags */, LONGLONG /* llTimestamp */, IMFSample *pSample // Can be NULL ) { HRESULT hr = S_OK; IMFMediaBuffer *pBuffer = NULL; EnterCriticalSection(&m_critsec); if (FAILED(hrStatus)) { hr = hrStatus; } if (SUCCEEDED(hr)) { if (pSample) { // Get the video frame buffer from the sample. hr = pSample->GetBufferByIndex(0, &pBuffer); // Draw the frame. if (SUCCEEDED(hr)) { hr = m_draw.DrawFrame(pBuffer); } } } // Request the next frame. if (SUCCEEDED(hr)) { hr = m_pReader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, // actual NULL, // flags NULL, // timestamp NULL // sample ); } if (FAILED(hr)) { NotifyError(hr); } SafeRelease(&pBuffer); LeaveCriticalSection(&m_critsec); return hr; } //------------------------------------------------------------------- // TryMediaType // // Test a proposed video format. //------------------------------------------------------------------- HRESULT CPreview::TryMediaType(IMFMediaType *pType) { HRESULT hr = S_OK; BOOL bFound = FALSE; GUID subtype = { 0 }; hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); if (FAILED(hr)) { return hr; } // Do we support this type directly? if (m_draw.IsFormatSupported(subtype)) { bFound = TRUE; } else { // Can we decode this media type to one of our supported // output formats? for (DWORD i = 0; ; i++) { // Get the i'th format. hr = m_draw.GetFormat(i, &subtype); if (FAILED(hr)) { break; } hr = pType->SetGUID(MF_MT_SUBTYPE, subtype); if (FAILED(hr)) { break; } // Try to set this type on the source reader. hr = m_pReader->SetCurrentMediaType( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pType ); if (SUCCEEDED(hr)) { bFound = TRUE; break; } } } if (bFound) { hr = m_draw.SetVideoType(pType); } return hr; } //------------------------------------------------------------------- // SetDevice // // Set up preview for a specified video capture device. //------------------------------------------------------------------- HRESULT CPreview::SetDevice(IMFActivate *pActivate) { HRESULT hr = S_OK; IMFMediaSource *pSource = NULL; IMFAttributes *pAttributes = NULL; IMFMediaType *pType = NULL; EnterCriticalSection(&m_critsec); // Release the current device, if any. hr = CloseDevice(); // Create the media source for the device. if (SUCCEEDED(hr)) { hr = pActivate->ActivateObject( __uuidof(IMFMediaSource), (void**)&pSource ); } // Get the symbolic link. if (SUCCEEDED(hr)) { hr = pActivate->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &m_pwszSymbolicLink, &m_cchSymbolicLink ); } // // Create the source reader. // // Create an attribute store to hold initialization settings. if (SUCCEEDED(hr)) { hr = MFCreateAttributes(&pAttributes, 2); } if (SUCCEEDED(hr)) { hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE); } // Set the callback pointer. if (SUCCEEDED(hr)) { hr = pAttributes->SetUnknown( MF_SOURCE_READER_ASYNC_CALLBACK, this ); } if (SUCCEEDED(hr)) { hr = MFCreateSourceReaderFromMediaSource( pSource, pAttributes, &m_pReader ); } // Try to find a suitable output type. if (SUCCEEDED(hr)) { for (DWORD i = 0; ; i++) { hr = m_pReader->GetNativeMediaType( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, i, &pType ); if (FAILED(hr)) { break; } hr = TryMediaType(pType); SafeRelease(&pType); if (SUCCEEDED(hr)) { // Found an output type. break; } } } if (SUCCEEDED(hr)) { // Ask for the first sample. hr = m_pReader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, NULL, NULL, NULL ); } if (FAILED(hr)) { if (pSource) { pSource->Shutdown(); // NOTE: The source reader shuts down the media source // by default, but we might not have gotten that far. } CloseDevice(); } SafeRelease(&pSource); SafeRelease(&pAttributes); SafeRelease(&pType); LeaveCriticalSection(&m_critsec); return hr; } //------------------------------------------------------------------- // ResizeVideo // Resizes the video rectangle. // // The application should call this method if the size of the video // window changes; e.g., when the application receives WM_SIZE. //------------------------------------------------------------------- HRESULT CPreview::ResizeVideo(WORD /*width*/, WORD /*height*/) { HRESULT hr = S_OK; EnterCriticalSection(&m_critsec); hr = m_draw.ResetDevice(); if (FAILED(hr)) { MessageBox(NULL, L"ResetDevice failed!", NULL, MB_OK); } LeaveCriticalSection(&m_critsec); return hr; } //------------------------------------------------------------------- // CheckDeviceLost // Checks whether the current device has been lost. // // The application should call this method in response to a // WM_DEVICECHANGE message. (The application must register for // device notification to receive this message.) //------------------------------------------------------------------- HRESULT CPreview::CheckDeviceLost(DEV_BROADCAST_HDR *pHdr, BOOL *pbDeviceLost) { DEV_BROADCAST_DEVICEINTERFACE *pDi = NULL; if (pbDeviceLost == NULL) { return E_POINTER; } *pbDeviceLost = FALSE; if (pHdr == NULL) { return S_OK; } if (pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) { return S_OK; } pDi = (DEV_BROADCAST_DEVICEINTERFACE*)pHdr; EnterCriticalSection(&m_critsec); if (m_pwszSymbolicLink) { if (_wcsicmp(m_pwszSymbolicLink, pDi->dbcc_name) == 0) { *pbDeviceLost = TRUE; } } LeaveCriticalSection(&m_critsec); return S_OK; }