/** * * Direct show input. * * @file * Copyright © Audi Electronics Venture GmbH. All rights reserved * * $Author: WNEROLF $ * $Date: 2012-06-11 18:13:02 +0200 (Mo, 11 Jun 2012) $ * $Revision: 32109 $ * * @remarks * */ #include "./dsvideo.h" //#include #define RELEASE_IF_NOT_NULL(pIObj) if ((pIObj)!=NULL) { (pIObj)->Release(); (pIObj)=NULL; } //***************************************************************************** #define SET_STREAM_CONFIGURATION 1 tInt cDSInput::m_nCOMIntances = 0; //***************************************************************************** cString cDSInput::GetDSError(HRESULT hResult) { tChar szMsg[1024]; AMGetErrorText(hResult, szMsg, sizeof(szMsg)); return cString(szMsg); } //***************************************************************************** cDSInput::cDSInput() { // ::CoInitialize(NULL); m_hWnd = NULL; m_pGraph = NULL; m_pMediaControl = NULL; m_pEvent = NULL; m_pRenderer = NULL; m_psStreamEventSink = NULL; m_pCaptureGraphBuilder = NULL; m_sBitmapFormat.nPaletteSize = 0; m_pPalette = NULL; m_pGreyscalePalette = NULL; m_nAvgTimePerFrame = 0; m_bFormatChanged = tTrue; m_bFlipVertical = tFalse; m_bConfigureBitsPerPixel = tTrue; // if false, always use default depth } //***************************************************************************** cDSInput::~cDSInput() { Stop(); Release(); //::CoUninitialize(); } //***************************************************************************** bool cDSInput::Release() { CleanUp(); if (m_nCOMIntances > 0) { m_nCOMIntances--; if (m_nCOMIntances == 0) { ::CoUninitialize(); m_nCOMIntances = 0; } } return true; } //***************************************************************************** bool cDSInput::Open(const char* strDeviceName, HWND hWnd, IDSStreamEventSink* psEventSink, tDeviceConfig* pDeviceConfig, int nGraphEventMsg, HRESULT* phr) { if (m_nCOMIntances <= 0) { ::CoInitialize(NULL); m_nCOMIntances++; } HRESULT hr = S_OK; m_nRequestedSubFormat = pDeviceConfig->nSubFormat; m_nSubFormatIdx = 0; memcpy(&m_sRequestedFormat, &pDeviceConfig->sBitmapFormat, sizeof(m_sRequestedFormat)); if (m_bConfigureBitsPerPixel) { switch (pDeviceConfig->sBitmapFormat.nBitsPerPixel) { case 8: m_RequestedSubtype = MEDIASUBTYPE_RGB8; break; case 16: m_RequestedSubtype = MEDIASUBTYPE_RGB555; break; case 24: m_RequestedSubtype = MEDIASUBTYPE_RGB24; break; case 32: m_RequestedSubtype = MEDIASUBTYPE_RGB32; break; default: // bits per pixel not specified - use any available memset(&m_RequestedSubtype, 0, sizeof(m_RequestedSubtype)); break; } } m_bFormatChanged = tTrue; if (nGraphEventMsg == 0) { nGraphEventMsg = WM_USER + 0x123; } m_psStreamEventSink = psEventSink; m_hWnd = hWnd; if (m_hWnd == NULL) { if (!m_oBlindWnd.Create(this, nGraphEventMsg)) { return false; } m_hWnd = m_oBlindWnd.GetHandle(); } IBaseFilter* pSourceFilter = NULL; ::IPin* pPinSource_Output = NULL; ::IPin* pPinRenderer_Input = NULL; bool l_bFailure = false; // filter graph / graph builder hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IFilterGraph2, (void **)&m_pGraph); if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } if (!l_bFailure) { hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (void **) &m_pCaptureGraphBuilder); } if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } if (!l_bFailure) { hr = m_pCaptureGraphBuilder->SetFiltergraph(m_pGraph); if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } } m_bMonikerSetToGraph = tFalse; if (!l_bFailure) { hr = FindCaptureDevice(strDeviceName, &pSourceFilter); if (hr != S_OK) { // Don't display a message because FindCaptureDevice will handle it l_bFailure = true; } } if (!l_bFailure && !m_bMonikerSetToGraph) { hr = m_pGraph->AddFilter(pSourceFilter, L"VideoCapture"); if (FAILED(hr)) { /* DisplayText(TEXT("Couldn't add the capture filter to the graph! hr=0x%x\r\n\r\n") TEXT("If you have a working video capture device, please make sure\r\n") TEXT("that it is connected and is not being used by another application.\r\n\r\n") TEXT("The sample will now close."), hr); */ LOG_ERROR(GetDSError(hr)); l_bFailure = true; } } else if (m_bMonikerSetToGraph) { //to to set name to filter } if (!l_bFailure) { #if (SET_STREAM_CONFIGURATION) hr = S_OK; tResult nErr = ConfigureCaptureDevice(pSourceFilter, pDeviceConfig, &hr); if (IS_FAILED(nErr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } #endif } // renderer if (!l_bFailure) { m_pRenderer = new cDSRenderer(this); if (m_pRenderer == NULL) { l_bFailure = true; } } if (!l_bFailure) { hr = m_pGraph->AddFilter(m_pRenderer, L"ADTFDefaultRenderer"); if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } else { pPinRenderer_Input = GetPin(m_pRenderer, PINDIR_INPUT); if (pPinRenderer_Input == NULL) { l_bFailure = true; } } } if (!l_bFailure) { // Render the capture or preview pin on the video capture filter hr = m_pCaptureGraphBuilder->RenderStream (&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pSourceFilter, NULL, m_pRenderer); if (FAILED(hr)) { hr = m_pCaptureGraphBuilder->RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pSourceFilter, NULL, m_pRenderer); if (FAILED(hr)) { // device may already be in use by another application LOG_ERROR(GetDSError(hr)); l_bFailure = true; } } } if (!l_bFailure) { // get controller interfaces m_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&m_pEvent); if (m_pEvent != NULL) { m_pEvent->SetNotifyWindow((OAHWND)m_hWnd, nGraphEventMsg, 0); } /* AM_MEDIA_TYPE mediaType; hr = pPinRenderer_Input->ConnectionMediaType(&mediaType); if (IS_FAILED(UpdateFormat(&mediaType))) { memset(&m_sBitmapFormat, 0, sizeof(m_sBitmapFormat)); } FreeMediaType(mediaType); */ } RELEASE_IF_NOT_NULL(pPinSource_Output); RELEASE_IF_NOT_NULL(pPinRenderer_Input); RELEASE_IF_NOT_NULL(pSourceFilter); // get media control interface if (!l_bFailure) { hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl); if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } } if (phr != NULL) { *phr = hr; } if (l_bFailure) { Release(); return false; } return true; } ::IPin* cDSInput::GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir) { BOOL bFound = FALSE; IEnumPins *pEnum; ::IPin *pPin; HRESULT hr = pFilter->EnumPins(&pEnum); if (FAILED(hr)) { return NULL; } while(pEnum->Next(1, &pPin, 0) == S_OK) { PIN_DIRECTION PinDirThis; pPin->QueryDirection(&PinDirThis); if (bFound = (PinDir == PinDirThis)) break; pPin->Release(); } pEnum->Release(); return (bFound ? pPin : NULL); } //***************************************************************************** HRESULT cDSInput::FindCaptureDevice(const char* strDeviceName, IBaseFilter ** ppSrcFilter) { IBaseFilter* pSrc = NULL; IMoniker* pMoniker = NULL; IEnumMoniker* pClassEnum = NULL; ICreateDevEnum* pDevEnum = NULL; bool l_bFailure = false; char strDeviceDisplayName[512]; if (ppSrcFilter == NULL || strDeviceName == NULL) { return E_POINTER; } *ppSrcFilter = NULL; cString strDeviceSpec(strDeviceName); strDeviceSpec.Trim(); strDeviceSpec.ToLower(); tUInt32 nNameSeparator = (tUInt32)strDeviceSpec.Find(':'); if (nNameSeparator != UINT32_MAX) { strDeviceSpec.Delete(0, nNameSeparator + 1); } tInt nDeviceNum = -1; if (strDeviceSpec.Compare("default") == 0) { nDeviceNum = 1; strDeviceSpec = ""; } else { nDeviceNum = atoi(strDeviceSpec.GetPtr()); if (nDeviceNum == 0 && strDeviceSpec.Compare("0") != 0) { nDeviceNum = -1; } else { strDeviceSpec = ""; } } // Create the system device enumerator HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void **) &pDevEnum); if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } if (!l_bFailure) { // Create an enumerator for the video capture devices hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0); if (FAILED(hr)) { LOG_ERROR(GetDSError(hr)); l_bFailure = true; } } if (!l_bFailure) { // If there are no enumerators for the requested type, then // CreateClassEnumerator will succeed, but pClassEnum will be NULL. if (pClassEnum == NULL) { LOG_ERROR("No video capture device was detected."); l_bFailure = true; hr = ERROR_NOT_READY; } } bool bDeviceFound = false; if (!l_bFailure) { // Process the device list. // Note that if the Next() call succeeds but there are no monikers, // it will return S_FALSE (which is not a failure). Therefore, we // check that the return code is S_OK instead of using SUCCEEDED() macro. ULONG cFetched; pClassEnum->Reset(); tInt nDeviceIdx = 1; LPOLESTR wstrMonikerName = NULL; while (!bDeviceFound && (S_OK == pClassEnum->Next (1, &pMoniker, &cFetched))) { // get a human-readable string for evaluation during debugging strcpy_s(strDeviceDisplayName, 512, "Failed to read friendly name!"); IPropertyBag *pBag = NULL; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag); if (SUCCEEDED(hr)) { VARIANT var; VariantInit(&var); hr = pBag->Read(L"FriendlyName", &var, NULL); if (SUCCEEDED(hr)) { WideCharToMultiByte(CP_ACP, 0, var.bstrVal, -1, strDeviceDisplayName, sizeof(strDeviceDisplayName), NULL, NULL); } VariantClear(&var); pBag->Release(); } LOG_INFO(cString::Format("Found device #%d: \"%s\"", nDeviceIdx, strDeviceDisplayName)); if (!strDeviceSpec.IsEmpty()) { cString strFoundDeviceNameLowerCase(strDeviceDisplayName); strFoundDeviceNameLowerCase.ToLower(); if (strFoundDeviceNameLowerCase.Find(strDeviceSpec) != -1) { // found device specified by name bDeviceFound = tTrue; } } else if (nDeviceNum == nDeviceIdx) { // found device specified by number bDeviceFound = tTrue; } if (bDeviceFound) { hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**) &pSrc); if (FAILED(hr)) { //try to add with moniker to graph hr = m_pGraph->AddSourceFilterForMoniker(pMoniker, 0, L"VideoCapture", &pSrc); if (FAILED(hr)) { LOG_ERROR("Device not ready"); l_bFailure = tTrue; } } if (!l_bFailure) { LOG_INFO(cString::Format("Using device #%d: \"%s\"", nDeviceIdx, strDeviceDisplayName)); } } nDeviceIdx++; RELEASE_IF_NOT_NULL(pMoniker); } } if (!l_bFailure && !bDeviceFound) { LOG_ERROR("Device not found \"" + cString(strDeviceName) + "\""); l_bFailure = true; hr = ERROR_BAD_UNIT; } RELEASE_IF_NOT_NULL(pMoniker); RELEASE_IF_NOT_NULL(pClassEnum); RELEASE_IF_NOT_NULL(pDevEnum); // Copy the found filter pointer to the output parameter. // Do NOT Release() the reference, since it will still be used // by the calling function. if (!l_bFailure) { *ppSrcFilter = pSrc; } return hr; } //***************************************************************************** bool cDSInput::Play() { m_bFormatChanged = tTrue; // Run the graph IVideoWindow *pVidWin = NULL; if (m_pMediaControl != NULL) { OAFilterState eFilterState ; HRESULT hr = m_pMediaControl->GetState(INFINITE, &eFilterState ); if (FAILED(hr)) { return false; } if (eFilterState == State_Running) { hr = m_pMediaControl->Stop(); } if (m_pEvent != NULL) { long levCode = 0; m_pEvent->WaitForCompletion(INFINITE, &levCode); } m_pMediaControl->Run(); return true; } return false; } //***************************************************************************** bool cDSInput::Pause() { if (m_pMediaControl != NULL) { m_pMediaControl->Pause(); return true; } return false; } //***************************************************************************** bool cDSInput::Stop() { if (m_pMediaControl != NULL) { LONGLONG pos = 0; HRESULT hr = m_pMediaControl->Stop(); if (m_pEvent != NULL) { long levCode = 0; m_pEvent->WaitForCompletion(INFINITE, &levCode); } return true; } return false; } //***************************************************************************** void cDSInput::CleanUp() { long levCode = 0; Stop(); if (m_pEvent != NULL) { m_pEvent->WaitForCompletion(INFINITE, &levCode); } if (m_pMediaControl != NULL) { m_pMediaControl->Release(); m_pMediaControl = NULL; } if (m_pEvent != NULL) { m_pEvent->Release(); m_pEvent = NULL; } if (m_pRenderer != NULL) { // delete m_pRenderer; --> don't delete: Graph->Release() will do that! m_pRenderer = NULL; } if (m_pCaptureGraphBuilder != NULL) { m_pCaptureGraphBuilder->Release(); m_pCaptureGraphBuilder = NULL; } if (m_pGraph != NULL) { m_pGraph->Release(); m_pGraph = NULL; } m_oBlindWnd.Release(); FreePalette(); } //***************************************************************************** bool cDSInput::UpdateStatus() { if (m_pEvent == NULL) { return FALSE; } long lEventCode = 0; LONG_PTR lParam1 = 0; LONG_PTR lParam2 = 0; HRESULT hr = m_pEvent->GetEvent(&lEventCode, &lParam1, &lParam2, INFINITE); if (FAILED(hr)) { return false; } if (lEventCode == EC_COMPLETE) { Stop(); } else if (lEventCode == EC_USERABORT) { Stop(); } if (m_psStreamEventSink != NULL) { m_psStreamEventSink->OnStreamEvent(lEventCode, (tInt)lParam1, (tInt)lParam2); } m_pEvent->FreeEventParams(lEventCode, lParam1, lParam2); return true; } //***************************************************************************** HRESULT cDSInput::GetEvent(long *lEventCode, long *lParam1, long *lParam2, long msTimeout) { if (m_pEvent == NULL) { return E_NOTIMPL; } LONG_PTR* lpParam1 = (LONG_PTR*)&lParam1; LONG_PTR* lpParam2 = (LONG_PTR*)&lParam2; HRESULT hr = m_pEvent->GetEvent(lEventCode, lpParam1, lpParam2, msTimeout); if (SUCCEEDED(hr)) { m_pEvent->FreeEventParams(*lEventCode, *lParam1, *lParam2); } return hr; } //***************************************************************************** void cDSInput::OnGraphEvent() { UpdateStatus(); } //***************************************************************************** HWND cDSInput::GetHWnd() { return m_hWnd; } //***************************************************************************** bool cDSInput::DoModal() { return m_oBlindWnd.DoModal(); } //***************************************************************************** bool cDSInput::ProcessMessages(int nWait) { return m_oBlindWnd.ProcessMessages(nWait); } //***************************************************************************** HRESULT cDSInput::OnQueryMediaType(const CMediaType* pMediaType) { if (pMediaType == NULL) { return E_POINTER; } if (pMediaType->majortype != MEDIATYPE_Video) { return S_FALSE; } if (m_bConfigureBitsPerPixel && m_sRequestedFormat.nBitsPerPixel != 0) { // check if specified subtype is available if (m_RequestedSubtype != pMediaType->subtype) { return S_FALSE; } } else if (pMediaType->subtype != MEDIASUBTYPE_RGB8 && pMediaType->subtype != MEDIASUBTYPE_RGB555 && pMediaType->subtype != MEDIASUBTYPE_RGB565 && pMediaType->subtype != MEDIASUBTYPE_RGB24 && pMediaType->subtype != MEDIASUBTYPE_RGB32) { // none of the supported formats is available return S_FALSE; } const BITMAPINFOHEADER* pBitmapInfoHeader = NULL; const VIDEOINFOHEADER* pInfo = NULL; const VIDEOINFOHEADER2* pInfo2 = NULL; if (pMediaType->formattype == FORMAT_VideoInfo2) { pInfo2 = (VIDEOINFOHEADER2*) pMediaType->pbFormat; pBitmapInfoHeader = &pInfo2->bmiHeader; } else if (pMediaType->formattype == FORMAT_VideoInfo) { pInfo = (VIDEOINFOHEADER*) pMediaType->pbFormat; pBitmapInfoHeader = &pInfo->bmiHeader; } else { return S_FALSE; } if (m_bConfigureBitsPerPixel) { // check specified attributes if (m_sRequestedFormat.nBitsPerPixel != 0 && m_sRequestedFormat.nBitsPerPixel != pBitmapInfoHeader->biBitCount ) { return S_FALSE; } if (m_sRequestedFormat.nBitsPerPixel == 8 && m_sRequestedFormat.nPixelFormat == cImage::PF_GREYSCALE_8 && pBitmapInfoHeader->biClrUsed != 0) { // don't accept palette for greyscale images return S_FALSE; } } // check sub-format idx if specified m_nSubFormatIdx++; if (m_nRequestedSubFormat != 0 && m_nSubFormatIdx != m_nRequestedSubFormat) { return S_FALSE; } return S_OK; } //***************************************************************************** HRESULT cDSInput::OnSetMediaType(const CMediaType* pMediaType) { if (pMediaType == NULL) { return E_POINTER; } if (IS_FAILED(UpdateFormat(pMediaType))) { return S_FALSE; } return S_OK; } tResult cDSInput::ConfigureCaptureDevice(IBaseFilter* pObject, tDeviceConfig* pDeviceConfig, HRESULT* phr) { LOG_INFO("ConfigureCaptureDevice start"); RETURN_IF_POINTER_NULL(pDeviceConfig); IAMStreamConfig* pStreamConfig = NULL; HRESULT hr = m_pCaptureGraphBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, NULL, pObject, IID_IAMStreamConfig, (tVoid**) &pStreamConfig); if (IS_FAILED(hr)) { if (phr != NULL) { *phr = hr; } LOG_ERROR(GetDSError(hr)); LOG_ERROR("FindInterface failed"); RETURN_ERROR(ERR_NOT_SUPPORTED); } VIDEO_STREAM_CONFIG_CAPS sConfigCaps; memset(&sConfigCaps, 0, sizeof(sConfigCaps)); int iCount = 0; int iSize = 0; hr = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize); if (sizeof(sConfigCaps) != iSize) { pStreamConfig->Release(); if (phr != NULL) { *phr = hr; } LOG_ERROR(GetDSError(hr)); LOG_ERROR("The video stream capture config that will be returned has not the right size! " "Can not ask for VIDEO_STREAM_CONFIG_CAPS in DirectShow (capabilities)"); RETURN_ERROR(ERR_FAILED); } AM_MEDIA_TYPE* pMediaType = NULL; VIDEOINFOHEADER* pInfo = NULL; VIDEOINFOHEADER2* pInfo2 = NULL; BITMAPINFOHEADER* pBitmapInfoHeader = NULL; LOG_INFO(cString::Format("Iterate capabilities. Count == %d", iCount)); for (tInt nIdx = 0; nIdx < iCount; nIdx++) { hr = pStreamConfig->GetStreamCaps(nIdx, &pMediaType, reinterpret_cast(&sConfigCaps)); if (IS_FAILED(hr)) { LOG_ERROR(GetDSError(hr)); LOG_ERROR("Could not find valid stream type"); pStreamConfig->Release(); if (phr != NULL) { *phr = hr; } RETURN_ERROR(ERR_NOT_SUPPORTED); } if ((pMediaType->formattype == FORMAT_VideoInfo2 || pMediaType->formattype == FORMAT_VideoInfo) && (pMediaType->subtype == MEDIASUBTYPE_RGB8 || pMediaType->subtype == MEDIASUBTYPE_RGB555 || pMediaType->subtype == MEDIASUBTYPE_RGB565 || pMediaType->subtype == MEDIASUBTYPE_RGB24 || pMediaType->subtype == MEDIASUBTYPE_RGB32 || pMediaType->subtype == MEDIASUBTYPE_MJPG )) { LOG_INFO("MediaType found!"); break; } } if (pMediaType->formattype == FORMAT_VideoInfo2) { pInfo2 = (VIDEOINFOHEADER2*) pMediaType->pbFormat; pInfo2->AvgTimePerFrame = (10000000) / (tInt64) pDeviceConfig->nFrameRate; pBitmapInfoHeader = &pInfo2->bmiHeader; LOG_INFO("FORMAT_VideoInfo2"); } else if (pMediaType->formattype == FORMAT_VideoInfo) { pInfo = (VIDEOINFOHEADER*) pMediaType->pbFormat; pInfo->AvgTimePerFrame = (10000000) / (tInt64) pDeviceConfig->nFrameRate; pBitmapInfoHeader = &pInfo->bmiHeader; LOG_INFO("FORMAT_VideoInfo"); } else if (pMediaType->formattype == FORMAT_MPEG2Video) { LOG_ERROR("unhandled formattype: FORMAT_MPEG2Video"); RETURN_ERROR(ERR_NOT_SUPPORTED); } else if (pMediaType->formattype == FORMAT_None) { LOG_ERROR("unhandled formattype: FORMAT_None"); RETURN_ERROR(ERR_NOT_SUPPORTED); } else if (pMediaType->formattype == FORMAT_DolbyAC3) { LOG_ERROR("unhandled formattype: FORMAT_DolbyAC3"); RETURN_ERROR(ERR_NOT_SUPPORTED); } else if (pMediaType->formattype == FORMAT_MPEG2Audio) { LOG_ERROR("unhandled formattype: FORMAT_MPEG2Audio"); RETURN_ERROR(ERR_NOT_SUPPORTED); } else if (pMediaType->formattype == FORMAT_DVD_LPCMAudio) { LOG_ERROR("unhandled formattype: FORMAT_DVD_LPCMAudio"); RETURN_ERROR(ERR_NOT_SUPPORTED); } else { RETURN_ERROR(ERR_UNKNOWN_FORMAT); } LOG_INFO(cString::Format("BitmapFormat.Width = %d", pDeviceConfig->sBitmapFormat.nWidth)); if (pDeviceConfig->sBitmapFormat.nWidth != 0) { pBitmapInfoHeader->biWidth = pDeviceConfig->sBitmapFormat.nWidth; } LOG_INFO(cString::Format("BitmapFormat.Height = %d", pDeviceConfig->sBitmapFormat.nHeight)); if (pDeviceConfig->sBitmapFormat.nHeight != 0) { pBitmapInfoHeader->biHeight = pDeviceConfig->sBitmapFormat.nHeight; } LOG_INFO(cString::Format("BitmapFormat.BitsPerPixel = %d", pDeviceConfig->sBitmapFormat.nBitsPerPixel)); if (m_bConfigureBitsPerPixel && pDeviceConfig->sBitmapFormat.nBitsPerPixel != 0) { //pMediaType->subtype = m_RequestedSubtype; pBitmapInfoHeader->biBitCount = (WORD) pDeviceConfig->sBitmapFormat.nBitsPerPixel; if (pBitmapInfoHeader->biBitCount <= 8) { pBitmapInfoHeader->biClrUsed = 0; pBitmapInfoHeader->biClrImportant = 0; } } // set the sample size and image size. round the image width up to a DWORD boundary. pBitmapInfoHeader->biSizeImage = (((pBitmapInfoHeader->biWidth + 3) & ~3) * abs(pBitmapInfoHeader->biHeight) * pBitmapInfoHeader->biBitCount) / 8; pStreamConfig->SetFormat(pMediaType); DeleteMediaType(pMediaType); pStreamConfig->Release(); LOG_INFO("ConfigureCaptureDevice end"); RETURN_NOERROR; } //***************************************************************************** tResult cDSInput::UpdateFormat(const AM_MEDIA_TYPE* pMediaType) { RETURN_IF_POINTER_NULL(pMediaType); if (pMediaType->majortype != MEDIATYPE_Video) { RETURN_ERROR(ERR_INVALID_ARG); } if (pMediaType->subtype != MEDIASUBTYPE_RGB8 && pMediaType->subtype != MEDIASUBTYPE_RGB555 && pMediaType->subtype != MEDIASUBTYPE_RGB565 && pMediaType->subtype != MEDIASUBTYPE_RGB24 && pMediaType->subtype != MEDIASUBTYPE_RGB32) { RETURN_ERROR(ERR_NOT_SUPPORTED); } BITMAPINFOHEADER* pBitmapInfoHeader = NULL; RGBQUAD* pPalette = NULL; long nPaletteSize = 0; tInt16 nPixelFormat = cImage::PF_BGR_888; if (pMediaType->subtype == MEDIASUBTYPE_RGB8) { nPixelFormat = cImage::PF_RGB_8; } else if (pMediaType->subtype == MEDIASUBTYPE_RGB555) { nPixelFormat = cImage::PF_BGR_555; } else if (pMediaType->subtype == MEDIASUBTYPE_RGB565) { nPixelFormat = cImage::PF_BGR_565; } else if (pMediaType->subtype == MEDIASUBTYPE_RGB24) { nPixelFormat = cImage::PF_RGB_888; } else if (pMediaType->subtype == MEDIASUBTYPE_RGB32) { nPixelFormat = cImage::PF_RGBA_8888; } if (pMediaType->formattype == FORMAT_VideoInfo2 && pMediaType->cbFormat >= sizeof(VIDEOINFOHEADER2)) { VIDEOINFOHEADER2* pInfo = (VIDEOINFOHEADER2*) pMediaType->pbFormat; m_nAvgTimePerFrame = pInfo->AvgTimePerFrame; pBitmapInfoHeader = &pInfo->bmiHeader; if (pBitmapInfoHeader->biBitCount <= 8 && pMediaType->cbFormat > sizeof(VIDEOINFOHEADER2)) { pPalette = (RGBQUAD*) (((BYTE*) pMediaType->pbFormat) + sizeof(VIDEOINFOHEADER2)); nPaletteSize = (pMediaType->cbFormat - sizeof(VIDEOINFOHEADER2)) / sizeof(RGBQUAD); } if (pBitmapInfoHeader->biSizeImage == 0) { pBitmapInfoHeader->biSizeImage = pMediaType->lSampleSize; } RETURN_IF_FAILED(UpdateFormat(pBitmapInfoHeader, pPalette, nPaletteSize, nPixelFormat)); } else if (pMediaType->formattype == FORMAT_VideoInfo && pMediaType->cbFormat >= sizeof(VIDEOINFOHEADER)) { VIDEOINFOHEADER* pInfo = (VIDEOINFOHEADER*) pMediaType->pbFormat; m_nAvgTimePerFrame = pInfo->AvgTimePerFrame; pBitmapInfoHeader = &pInfo->bmiHeader; if (pBitmapInfoHeader->biBitCount <= 8 && pMediaType->cbFormat > sizeof(VIDEOINFOHEADER)) { pPalette = (RGBQUAD*) (((BYTE*) pMediaType->pbFormat) + sizeof(VIDEOINFOHEADER)); nPaletteSize = (pMediaType->cbFormat - sizeof(VIDEOINFOHEADER)) / sizeof(RGBQUAD); } if (pBitmapInfoHeader->biSizeImage == 0) { pBitmapInfoHeader->biSizeImage = pMediaType->lSampleSize; } RETURN_IF_FAILED(UpdateFormat(pBitmapInfoHeader, pPalette, nPaletteSize, nPixelFormat)); } else { m_nAvgTimePerFrame = 25; RETURN_IF_FAILED(UpdateFormat(pBitmapInfoHeader, pPalette, nPaletteSize, nPixelFormat)); } RETURN_NOERROR; } //***************************************************************************** tResult cDSInput::UpdateFormat(BITMAPINFOHEADER* pBitmapInfoHeader, RGBQUAD* pPalette, long nPaletteSize, tInt16 nPixelFormat) { memset(&m_sBitmapFormat, 0, sizeof(m_sBitmapFormat)); m_sBitmapFormat.nWidth = pBitmapInfoHeader->biWidth; m_sBitmapFormat.nHeight = pBitmapInfoHeader->biHeight; if (m_sBitmapFormat.nHeight < 0) { m_sBitmapFormat.nHeight = -m_sBitmapFormat.nHeight; m_bFlipVertical = tFalse; } else { m_bFlipVertical = tTrue; } m_sBitmapFormat.nBitsPerPixel = pBitmapInfoHeader->biBitCount; m_sBitmapFormat.nBytesPerLine = (pBitmapInfoHeader->biWidth * m_sBitmapFormat.nBitsPerPixel) / 8; m_sBitmapFormat.nSize = pBitmapInfoHeader->biSizeImage; if (m_sBitmapFormat.nSize < 0 && m_bFlipVertical) { m_sBitmapFormat.nSize = - m_sBitmapFormat.nSize; } m_sBitmapFormat.nPixelFormat = nPixelFormat; if (m_sBitmapFormat.nBitsPerPixel <= 8 && nPaletteSize == 0) { m_sBitmapFormat.nPixelFormat = cImage::PF_GREYSCALE_8; } RETURN_IF_FAILED(AllocPalette(nPaletteSize, tFalse)); if (m_pPalette != NULL) { memcpy(m_pPalette, pPalette, nPaletteSize * sizeof(RGBQUAD)); for (tInt nIdx=0; nIdx 0) { m_pPalette = new tColor[nPaletteSize]; if (m_pPalette == NULL) { RETURN_ERROR(ERR_MEMORY); } m_pGreyscalePalette = new tUInt8[nPaletteSize]; if (m_pGreyscalePalette == NULL) { RETURN_ERROR(ERR_MEMORY); } m_sBitmapFormat.nPaletteSize = nPaletteSize; } RETURN_NOERROR; } //***************************************************************************** tResult cDSInput::FreePalette() { if (m_pPalette != NULL) { delete [] m_pPalette; m_pPalette = NULL; } if (m_pGreyscalePalette != NULL) { delete [] m_pGreyscalePalette; m_pGreyscalePalette = NULL; } m_sBitmapFormat.nPaletteSize = 0; RETURN_NOERROR; } //***************************************************************************** tBitmapFormat* cDSInput::GetBitmapFormat() { return &m_sBitmapFormat; } //***************************************************************************** tInt64 cDSInput::GetAvgTimePerFrame() { return m_nAvgTimePerFrame / 10; } //***************************************************************************** HRESULT cDSInput::OnNewFrame(::IMediaSample* pMediaSample) { tTimeStamp tmCurrent = cHighResTimer::GetTime(); HRESULT hr = S_OK; AM_MEDIA_TYPE* pMediaType; hr = pMediaSample->GetMediaType(&pMediaType); if (hr == S_OK && pMediaType != NULL) { if (IS_OK(UpdateFormat(pMediaType))) { m_bFormatChanged = tTrue; } DeleteMediaType(pMediaType); } long nSampleSize = pMediaSample->GetSize(); BYTE* pData = NULL; hr = pMediaSample->GetPointer(&pData); if (m_psStreamEventSink != NULL) { REFERENCE_TIME nStartTime, nEndTime; hr = pMediaSample->GetTime(&nStartTime, &nEndTime); tTimeStamp nTime; if (FAILED(hr)) { if (VFW_S_NO_STOP_TIME == hr) { nTime = (nStartTime + m_nAvgTimePerFrame / 2) / 10; } else if (VFW_E_SAMPLE_TIME_NOT_SET == hr) { nTime = tmCurrent - m_nAvgTimePerFrame / 20; } else { return hr; } } else { nTime = ((tInt64) nStartTime + (tInt64) nEndTime) / 20; } if (m_bFormatChanged) { m_psStreamEventSink->OnNewFrame(nTime, pData, nSampleSize, &m_sBitmapFormat, m_pPalette, m_bFlipVertical); m_bFormatChanged = tFalse; } else { m_psStreamEventSink->OnNewFrame(nTime, pData, nSampleSize, NULL, NULL, m_bFlipVertical); } } return S_OK; }