#include "StdAfx.h" #include "SmartCamFilter.h" // COM global table of objects in this dll CFactoryTemplate g_Templates[] = { { SMART_CAM_FILTER_NAME, &CLSID_SmartCam, CSmartCamFilter::CreateInstance, NULL, NULL } }; int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); // DllEntryPoint extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); } // Exported entry points for registration and unregistration STDAPI DllRegisterServer() { REGPINTYPES pinMediaTypes[] = { { &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24 } }; REGFILTERPINS2 pinRegInfo = { REG_PINFLAG_B_OUTPUT, // dwFlags 1, // cInstances 1, // nMediaTypes pinMediaTypes, // lpMediaType 0, // nMediums NULL, // lpMedium &PIN_CATEGORY_CAPTURE // clsPinCategory }; REGFILTER2 filterRegInfo = { 2, // Version 2 (pin mediums or pin category). MERIT_NORMAL, // Merit. { 1, // Number of pins. (const REGFILTERPINS *)&pinRegInfo // Pointer to pin information. } }; HRESULT hr; IFilterMapper2 *pFM2 = NULL; hr = AMovieDllRegisterServer2(TRUE); if (FAILED(hr)) return hr; hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void **)&pFM2); if (FAILED(hr)) return hr; hr = pFM2->RegisterFilter( CLSID_SmartCam, // Filter CLSID. SMART_CAM_FILTER_NAME, // Filter name. NULL, // Device moniker. &CLSID_VideoInputDeviceCategory, // Video capture category. SMART_CAM_FILTER_NAME, // Instance data. &filterRegInfo); // Pointer to filter information. pFM2->Release(); return hr; } STDAPI DllUnregisterServer() { HRESULT hr = AMovieDllRegisterServer2(FALSE); if (FAILED(hr)) { return hr; } IFilterMapper2 *pFM2 = NULL; hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void **)&pFM2); if (SUCCEEDED(hr)) { hr = pFM2->UnregisterFilter(&CLSID_VideoInputDeviceCategory, SMART_CAM_FILTER_NAME, CLSID_SmartCam); pFM2->Release(); } return hr; } // // ------------------ CSmartCamFilter ------------------ // CUnknown * WINAPI CSmartCamFilter::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) { ASSERT(phr); CUnknown *punk = new CSmartCamFilter(lpunk, phr); if(punk == NULL) { if(phr) *phr = E_OUTOFMEMORY; } return punk; } STDMETHODIMP CSmartCamFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv) { if(riid == IID_ISmartCamWriter) { return GetInterface(static_cast(this), ppv); } return CSource::NonDelegatingQueryInterface(riid,ppv); } CSmartCamFilter::CSmartCamFilter(LPUNKNOWN lpunk, HRESULT *phr) :CSource(NAME("SMARTCAM"), lpunk, CLSID_SmartCam) { DbgOutString("CSmartCamFilter()\n"); ASSERT(phr); CAutoLock cAutoLock(&m_cStateLock); m_paStreams = (CSourceStream **) new CSmartCamCapturePin*[1]; if(m_paStreams == NULL) { if(phr) *phr = E_OUTOFMEMORY; return; } m_paStreams[0] = new CSmartCamCapturePin(phr, this, SMART_CAM_CAPTURE_PIN_NAME); if(m_paStreams[0] == NULL) { if(phr) *phr = E_OUTOFMEMORY; } } // ISmartCamWriter methods STDMETHODIMP CSmartCamFilter::WriteFrame(char* frameBuffer, int size) { return NOERROR; } STDMETHODIMP CSmartCamFilter::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *State) { switch(m_State) { case State_Stopped: { DbgOutString("CSmartCamFilter::GetState() [crtState = State_Stopped]\n"); break; } case State_Paused: { DbgOutString("CSmartCamFilter::GetState() [crtState = State_Paused]\n"); break; } case State_Running: { DbgOutString("CSmartCamFilter::GetState() [crtState = State_Running]\n"); break; } } CheckPointer(State, E_POINTER); *State = m_State; if (m_State == State_Paused) return VFW_S_CANT_CUE; else return S_OK; } STDMETHODIMP CSmartCamFilter::SetSyncSource(IReferenceClock* pClock) { DbgOutString("SetSyncSource()\n"); return CBaseFilter::SetSyncSource(pClock); } CSmartCamFilter::~CSmartCamFilter(void) { DbgOutString("~CSmartCamFilter()\n"); } // // ------------------ CSmartCamCapturePin ------------------ // CSmartCamCapturePin::CSmartCamCapturePin(HRESULT *phr, CSmartCamFilter *pParent, LPCWSTR pPinName): CSourceStream(NAME("SMARTCAM_CAPTURE_PIN"), phr, pParent, pPinName), hFileMapping(NULL), pSharedMem(NULL), //hNewFrameEvt(NULL), isMaster(FALSE), frameNumber(0), sampleTime((LONG) 0) { DbgOutString("CSmartCamCapturePin()\n"); Initialize(); } CSmartCamCapturePin::~CSmartCamCapturePin() { DbgOutString("~CSmartCamCapturePin()\n"); Cleanup(); } STDMETHODIMP CSmartCamCapturePin::NonDelegatingQueryInterface(REFIID riid, void** ppv) { if(riid == IID_IAMPushSource) { return GetInterface(static_cast(this), ppv); } if(riid == IID_IAMStreamConfig) { return GetInterface(static_cast(this), ppv); } if(riid == IID_IKsPropertySet) { return GetInterface(static_cast(this), ppv); } return CSourceStream::NonDelegatingQueryInterface(riid, ppv); } // IAMStreamConfig methods STDMETHODIMP CSmartCamCapturePin::GetFormat(AM_MEDIA_TYPE **ppmt) { DbgOutString("CSmartCamCapturePin::GetFormat()\n"); VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) CoTaskMemAlloc(sizeof(VIDEOINFOHEADER)); *pVih = SmartCamVideoInfoHeader; AM_MEDIA_TYPE * pmt = (AM_MEDIA_TYPE*)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); *pmt = SmartCamMediaType; pmt->pbFormat = (BYTE*) pVih; pmt->cbFormat = sizeof(VIDEOINFOHEADER); *ppmt = pmt; return NOERROR; } STDMETHODIMP CSmartCamCapturePin::GetNumberOfCapabilities(int *piCount, int *piSize) { DbgOutString("CSmartCamCapturePin::GetNumberOfCapabilities()\n"); *piCount = 1; *piSize = sizeof(VIDEO_STREAM_CONFIG_CAPS); return NOERROR; } STDMETHODIMP CSmartCamCapturePin::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **ppmt, BYTE *pSCC) { DbgOutString("CSmartCamCapturePin::GetStreamCaps()\n"); if(iIndex < 0) return E_INVALIDARG; if(iIndex > 0) return S_FALSE; if(pSCC == NULL) return E_POINTER; VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) CoTaskMemAlloc(sizeof(VIDEOINFOHEADER)); *pVih = SmartCamVideoInfoHeader; AM_MEDIA_TYPE * pmt = (AM_MEDIA_TYPE*)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); *pmt = SmartCamMediaType; pmt->pbFormat = (BYTE*) pVih; pmt->cbFormat = sizeof(VIDEOINFOHEADER); *ppmt = pmt; VIDEO_STREAM_CONFIG_CAPS* pVidCap = (VIDEO_STREAM_CONFIG_CAPS*) pSCC; *pVidCap = SmartCamVideoCfgCaps; return S_OK; } STDMETHODIMP CSmartCamCapturePin::SetFormat(AM_MEDIA_TYPE *pmt) { DbgOutString("CSmartCamCapturePin::SetFormat()\n"); CAutoLock cAutoLock(m_pFilter->pStateLock()); if(pmt == NULL) return E_POINTER; // Pass the call up to the base class CMediaType MediaType = *pmt; return CSourceStream::SetMediaType(&MediaType); } // IKsPropertySet methods STDMETHODIMP CSmartCamCapturePin::Set( REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData ) { DbgOutString("CSmartCamCapturePin::Set()\n"); return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::Get( REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD *pcbReturned ) { DbgOutString("CSmartCamCapturePin::Get()\n"); if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED; if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED; if (pPropData == NULL && pcbReturned == NULL) return E_POINTER; if (pcbReturned) *pcbReturned = sizeof(GUID); if (pPropData == NULL) // Caller just wants to know the size. { DbgOutString("CSmartCamCapturePin::Get() [ (SIZE QUERY) OK !!! ]\n"); return S_OK; } if (cbPropData < sizeof(GUID)) // The buffer is too small. return E_UNEXPECTED; *(GUID *)pPropData = PIN_CATEGORY_CAPTURE; DbgOutString("CSmartCamCapturePin::Get() [ OK !!! ]\n"); return S_OK; } STDMETHODIMP CSmartCamCapturePin::QuerySupported( REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport ) { DbgOutString("CSmartCamCapturePin::QuerySupported()\n"); if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED; if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED; if (pTypeSupport) // We support getting this property, but not setting it. *pTypeSupport = KSPROPERTY_SUPPORT_GET; return S_OK; } // IAMPushSource STDMETHODIMP CSmartCamCapturePin::GetMaxStreamOffset(REFERENCE_TIME *prtMaxOffset) { DbgOutString("CSmartCamCapturePin::GetMaxStreamOffset()\n"); return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::GetPushSourceFlags(ULONG *pFlags) { DbgOutString("CSmartCamCapturePin::GetPushSourceFlags()\n"); *pFlags = AM_PUSHSOURCECAPS_INTERNAL_RM; return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::GetStreamOffset(REFERENCE_TIME *prtOffset) { DbgOutString("CSmartCamCapturePin::GetStreamOffset()\n"); return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::SetMaxStreamOffset(REFERENCE_TIME rtMaxOffset) { DbgOutString("CSmartCamCapturePin::SetMaxStreamOffset()\n"); return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::SetPushSourceFlags(ULONG Flags) { DbgOutString("CSmartCamCapturePin::SetPushSourceFlags()\n"); return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::SetStreamOffset(REFERENCE_TIME rtOffset) { DbgOutString("CSmartCamCapturePin::SetStreamOffset()\n"); return E_NOTIMPL; } STDMETHODIMP CSmartCamCapturePin::GetLatency(REFERENCE_TIME *prtLatency) { DbgOutString("CSmartCamCapturePin::GetLatency()\n"); return E_NOTIMPL; } // CBasePin methods HRESULT CSmartCamCapturePin::SetMediaType(const CMediaType *pMediaType) { DbgOutString("CSmartCamCapturePin::SetMediaType()\n"); CAutoLock cAutoLock(m_pFilter->pStateLock()); // Pass the call up to the base class return CSourceStream::SetMediaType(pMediaType); } HRESULT CSmartCamCapturePin::GetMediaType(int iPosition, CMediaType* pMediaType) { DbgLog((LOG_ERROR, 0, TEXT("CSmartCamCapturePin::GetMediaType( iPosition = %d )\n"), iPosition)); CheckPointer(pMediaType,E_POINTER); CAutoLock cAutoLock(m_pFilter->pStateLock()); if(iPosition < 0) { return E_INVALIDARG; } // Have we run off the end of types? if(iPosition > 0) { return VFW_S_NO_MORE_ITEMS; } *pMediaType = SmartCamMediaType; VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) pMediaType->AllocFormatBuffer(sizeof(VIDEOINFOHEADER)); if(pVih == NULL) { DbgOutString("CSmartCamCapturePin::GetMediaType(iPosition) - Format allocation failed !\n"); return(E_OUTOFMEMORY); } *pVih = SmartCamVideoInfoHeader; DbgOutString("CSmartCamCapturePin::GetMediaType(iPosition) - OK! -\n"); return NOERROR; } HRESULT CSmartCamCapturePin::GetMediaType(CMediaType *pMediaType) { DbgOutString("CSmartCamCapturePin::GetMediaType()\n"); return GetMediaType(0, pMediaType); } HRESULT CSmartCamCapturePin::CheckMediaType(const CMediaType *pMediaType) { DbgOutString("CSmartCamCapturePin::CheckMediaType()\n"); CheckPointer(pMediaType,E_POINTER); const GUID* Type = pMediaType->Type(); if (Type == NULL) return E_INVALIDARG; if((*Type != MEDIATYPE_Video) || // we only output video !(pMediaType->IsFixedSize())) // in fixed size samples { return E_INVALIDARG; } // Check for the RGB24 subtype we support const GUID* SubType = pMediaType->Subtype(); if (SubType == NULL) return E_INVALIDARG; if(*SubType != MEDIASUBTYPE_RGB24) { return E_INVALIDARG; } // Get the format area of the media type VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) pMediaType->Format(); if(pVih == NULL) return E_INVALIDARG; // Check the image size if((pVih->bmiHeader.biWidth > FRAME_WIDTH) || (abs(pVih->bmiHeader.biHeight) > FRAME_HEIGHT) || (pVih->bmiHeader.biSizeImage != GetBitmapSize(&pVih->bmiHeader))) { return E_INVALIDARG; } // Check the color depth and compression if((pVih->bmiHeader.biBitCount != 24) || (pVih->bmiHeader.biCompression != BI_RGB)) { return E_INVALIDARG; } DbgOutString("CSmartCamCapturePin::CheckMediaType() - OK! -\n"); return S_OK; // This format is acceptable. } HRESULT CSmartCamCapturePin::FillBuffer(IMediaSample *pMediaSample) { DbgOutString("CSmartCamCapturePin::FillBuffer()\n"); CAutoLock cAutoLock(m_pFilter->pStateLock()); CheckPointer(pMediaSample, E_POINTER); DWORD crtFrameNumber = *((DWORD*) pSharedMem); if(crtFrameNumber == SC_END_OF_STREAM) return S_FALSE; BYTE* pData = NULL; pMediaSample->GetPointer(&pData); long lDataLen = pMediaSample->GetSize(); BYTE* pSharedFrame = (BYTE*) pSharedMem + FRAME_NUMBER_SIZE; RtlCopyMemory(pData, pSharedFrame, lDataLen); CRefTime rtStart = sampleTime; sampleTime += CRefTime(MILLISECONDS_TO_100NS_UNITS(AVG_FRAME_TIME_MS)); pMediaSample->SetTime((REFERENCE_TIME *)&rtStart, (REFERENCE_TIME *)&sampleTime); Sleep(AVG_FRAME_TIME_MS); return S_OK; } HRESULT CSmartCamCapturePin::DecideBufferSize( IMemAllocator *pIMemAlloc, ALLOCATOR_PROPERTIES *pProperties) { DbgOutString("CSmartCamCapturePin::DecideBufferSize()\n"); CheckPointer(pIMemAlloc, E_POINTER); CheckPointer(pProperties, E_POINTER); CAutoLock cAutoLock(m_pFilter->pStateLock()); HRESULT hr = NOERROR; VIDEOINFOHEADER* pVih = (VIDEOINFOHEADER*) m_mt.Format(); pProperties->cBuffers = 1; pProperties->cbBuffer = pVih->bmiHeader.biSizeImage; ASSERT(pProperties->cbBuffer); // Ask the allocator to reserve us some sample memory, NOTE the function // can succeed (that is return NOERROR) but still not have allocated the // memory that we requested, so we must check we got whatever we wanted ALLOCATOR_PROPERTIES Actual; hr = pIMemAlloc->SetProperties(pProperties,&Actual); if(FAILED(hr)) { return hr; } // Is this allocator unsuitable if(Actual.cbBuffer < pProperties->cbBuffer) { return E_FAIL; } // Make sure that we have only 1 buffer (we erase the ball in the // old buffer to save having to zero a 200k+ buffer every time // we draw a frame) ASSERT(Actual.cBuffers == 1); DbgOutString("CSmartCamCapturePin::DecideBufferSize() - OK! -\n"); return NOERROR; } HRESULT CSmartCamCapturePin::OnThreadCreate() { DbgOutString("CSmartCamCapturePin::OnThreadCreate()\n"); return CSourceStream::OnThreadCreate(); } HRESULT CSmartCamCapturePin::OnThreadDestroy() { DbgOutString("CSmartCamCapturePin::OnThreadDestroy()\n"); return NOERROR; } STDMETHODIMP CSmartCamCapturePin::Notify(IBaseFilter * pSelf , Quality q) { return E_NOTIMPL; //return CSourceStream::Notify(pSelf, q); } STDMETHODIMP CSmartCamCapturePin::SetSink(IQualityControl * piqc) { return E_NOTIMPL; //return CSourceStream::SetSink(piqc); } HRESULT CSmartCamCapturePin::Initialize() { Util::CreateSharedMem(&hFileMapping, &pSharedMem); //Util::CreateOpenNewFrameEvt(&hNewFrameEvt); char procFullFileName[MAX_PROC_FILE_NAME], dllFullFileName[MAX_PROC_FILE_NAME]; GetModuleFileName(GetModuleHandle(SMART_CAM_FILTER_FILE_NAME), dllFullFileName, MAX_PROC_FILE_NAME); GetModuleFileName(NULL, procFullFileName, MAX_PROC_FILE_NAME); string installDir, procDir, procFileName; Util::ParseFileName(dllFullFileName, &installDir, NULL); Util::ParseFileName(procFullFileName, &procDir, &procFileName); isMaster = (procDir == installDir) && (procFileName == MASTER_APP_FILE_NAME); if(!isMaster) { HWND hMasterWnd = FindWindow(SMARTCAM_CLASSNAME, SMARTCAM_CAPTION); if(hMasterWnd == NULL) { string masterAppFullFileName = installDir + MASTER_APP_FILE_NAME; STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_MINIMIZE; PROCESS_INFORMATION pi; CreateProcess(masterAppFullFileName.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); } } return S_OK; } void CSmartCamCapturePin::Cleanup() { // Close the shared memory // Unmap shared memory from the process's address space. UnmapViewOfFile(pSharedMem); // Close the process's handle to the file-mapping object. CloseHandle(hFileMapping); // Close the handle to the synchronization event object //CloseHandle(hNewFrameEvt); } void CSmartCamCapturePin::GetVideoInfoParameters( const VIDEOINFOHEADER *pvih, // Pointer to the format header BYTE * const pbData, // Pointer to first address in buffer DWORD *pdwWidth, // Returns the width in pixels DWORD *pdwHeight, // Returns the height in pixels LONG *plStrideInBytes, // Add this to a row to get the new row down BYTE **ppbTop, // Pointer to first byte in top row of pixels bool bYuv ) { LONG lStride; // For 'normal' formats, biWidth is in pixels. // Expand to bytes and round up to a multiple of 4. if (pvih->bmiHeader.biBitCount != 0 && 0 == (7 & pvih->bmiHeader.biBitCount)) { lStride = (pvih->bmiHeader.biWidth * (pvih->bmiHeader.biBitCount / 8) + 3) & ~3; } else // Otherwise, biWidth is in bytes. { lStride = pvih->bmiHeader.biWidth; } // If rcTarget is empty, use the whole image. if (IsRectEmpty(&pvih->rcTarget)) { *pdwWidth = (DWORD)pvih->bmiHeader.biWidth; *pdwHeight = (DWORD)(abs(pvih->bmiHeader.biHeight)); if (pvih->bmiHeader.biHeight < 0 || bYuv) // Top-down bitmap { *plStrideInBytes = lStride; // Stride goes "down" *ppbTop = pbData; // Top row is first } else // Bottom-up bitmap { *plStrideInBytes = -lStride; // Stride goes "up" *ppbTop = pbData + lStride * (*pdwHeight - 1); // Bottom row is first } } else // rcTarget is NOT empty. Use a sub-rectangle in the image. { *pdwWidth = (DWORD)(pvih->rcTarget.right - pvih->rcTarget.left); *pdwHeight = (DWORD)(pvih->rcTarget.bottom - pvih->rcTarget.top); if (pvih->bmiHeader.biHeight < 0 || bYuv) // Top-down bitmap { // Same stride as above, but first pixel is modified down // and over by the target rectangle. *plStrideInBytes = lStride; *ppbTop = pbData + lStride * pvih->rcTarget.top + (pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8; } else // Bottom-up bitmap { *plStrideInBytes = -lStride; *ppbTop = pbData + lStride * (pvih->bmiHeader.biHeight - pvih->rcTarget.top - 1) + (pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8; } } }