// Copyright (c) 1998 - 1999 Microsoft Corporation. All Rights Reserved. #include #include #include #include "VPMPin.h" #include #include #include // IVideoPortControl #include // AMINTERLACE_* #include #include "DRect.h" extern "C" const TCHAR szPropPage[] = TEXT("Property Pages"); // // Flipping surface implementation // // To allow decoders to hold on to surfaces for out of order decode // we flip directly to the surface pass on Receive rather than // use the default NULL target surface for Flip(). // // This works in the following way // // The COMPinputPin::m_pDirectDrawSurface points to the FRONT buffer // // When Receive is called we Flip() the front buffer and because we // do an explicit Flip() DirectDraw swaps the memory pointers for the // current Front buffer and the surface passed in which is then attached // to the front buffer. // // The received buffer is then put at the back of the queue so (correctly) // the previous front buffer is now at the back of the queue to be handed // to the application // // The allocator actually has one more buffer than was actually requested // so the previous front buffer won't actually be requested until the next // Receive and hence the previous Flip() has time to complete. // // Video accelerator disable interface // ///////////////////////////////////// // CLASS CVPMInputPin implemented here ///////////////////////////////////// // constructor CVPMInputPin::CVPMInputPin( TCHAR *pObjectName, CVPMFilter& pFilter, HRESULT *phr, LPCWSTR pPinName, DWORD dwPinNo) : CBaseInputPin(pObjectName, &pFilter, &pFilter.GetFilterLock(), phr, pPinName) , CVPMPin( dwPinNo, pFilter ) , m_cOurRef( 0 ) , m_pIVPObject( NULL ) , m_pIVPInfo(NULL) , m_CategoryGUID( GUID_NULL ) , m_Communication( KSPIN_COMMUNICATION_SOURCE ) , m_bStreamingInKernelMode( FALSE ) , m_dwBackBufferCount( 0 ) , m_dwDirectDrawSurfaceWidth( 0 ) , m_dwMinCKStretchFactor( 0 ) , m_bSyncOnFill( FALSE ) , m_bDontFlip( FALSE ) , m_bDynamicFormatNeeded( TRUE ) , m_bNewPaletteSet( TRUE ) , m_dwInterlaceFlags( 0 ) , m_dwFlipFlag( 0 ) , m_bConnected( FALSE ) , m_bUsingOurAllocator( FALSE ) , m_hMemoryDC( NULL ) , m_bCanOverAllocateBuffers( TRUE ) , m_hEndOfStream( NULL ) , m_bDecimating( FALSE ) , m_lWidth( 0L ) , m_lHeight( 0L ) , m_bRuntimeNegotiationFailed( FALSE) , m_dwUpdateOverlayFlags( 0 ) , m_dwFlipFlag2( 0 ) , m_trLastFrame( 0 ) , m_lSrcWidth( 0 ) , m_lSrcHeight( 0 ) , m_rtNextSample( 0 ) , m_rtLastRun( 0 ) { AMTRACE((TEXT("CVPMInputPin::Constructor"))); memset( &m_WinInfo, 0, sizeof(m_WinInfo) ); m_bWinInfoSet = false; *phr = S_OK; m_Medium.Set = GUID_NULL; m_Medium.Id = 0; m_Medium.Flags = 0; HRESULT hr = NOERROR; LPUNKNOWN pUnkOuter; SetReconnectWhenActive(true); #ifdef PERF m_PerfFrameFlipped = MSR_REGISTER(TEXT("Frame Drawn")); #endif // See combase.cpp(107) for comments on this IUnknown* pThisUnknown = reinterpret_cast( static_cast(this) ); m_pVideoPortObject = new CVideoPortObj( pThisUnknown, phr, this ); // alias pointer to interfaces (instead of QI'ing) m_pIVPObject = m_pVideoPortObject; m_pIVPInfo = m_pVideoPortObject; hr = m_pIVPObject->SetObjectLock( &m_pVPMFilter.GetFilterLock() ); if (FAILED(hr)) { *phr = hr; } return; } // destructor CVPMInputPin::~CVPMInputPin(void) { AMTRACE((TEXT("CVPMInputPin::Destructor"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // delete the inner object delete m_pVideoPortObject; m_pVideoPortObject = NULL; } // overriden to expose IMediaPosition and IMediaSeeking control interfaces STDMETHODIMP CVPMInputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::NonDelegatingQueryInterface"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (riid == IID_IVPNotify ) { hr = GetInterface( static_cast(m_pVideoPortObject), ppv); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPUnknown->QueryInterface failed, hr = 0x%x"), hr)); } } else if (riid == IID_IVPNotify2 ) { hr = GetInterface( static_cast(m_pVideoPortObject), ppv); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPUnknown->QueryInterface failed, hr = 0x%x"), hr)); } } else if (riid == IID_IKsPin) { hr = GetInterface(static_cast(this), ppv); if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("GetInterface(IKsPin*) failed, hr = 0x%x"), hr)); } } else if (riid == IID_IKsPropertySet) { hr = GetInterface(static_cast(this), ppv); if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("GetInterface(IKsPropertySet*) failed, hr = 0x%x"), hr)); } } else if (riid == IID_IPinConnection) { hr = GetInterface(static_cast(this), ppv); if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("GetInterface(IPinConnection, ppv) failed, hr = 0x%x"), hr)); } } else if (riid == IID_ISpecifyPropertyPages&& 0 != VPMUtil::GetPropPagesRegistryDword( 0)) { return GetInterface(static_cast(this), ppv); } else { // call the base class hr = CBaseInputPin::NonDelegatingQueryInterface(riid, ppv); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::NonDelegatingQueryInterface failed, hr = 0x%x"), hr)); } } return hr; } // // NonDelegatingAddRef/NonDelegatingRelease // // STDMETHODIMP_(ULONG) CVPMInputPin::NonDelegatingAddRef(void) { return m_pVPMFilter.AddRef(); } // NonDelegatingAddRef STDMETHODIMP_(ULONG) CVPMInputPin::NonDelegatingRelease(void) { return m_pVPMFilter.Release(); } // --- ISpecifyPropertyPages --- STDMETHODIMP CVPMInputPin::GetPages(CAUUID *pPages) { #if 0 pPages->cElems = 1; pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID)*1); if (pPages->pElems == NULL) { return E_OUTOFMEMORY; } pPages->pElems[0] = CLSID_COMPinConfigProperties; return NOERROR; #else return E_NOTIMPL; #endif } // this function just tells whether each sample consists of one or two fields BOOL DisplayingFields(DWORD dwInterlaceFlags) { if ((dwInterlaceFlags & AMINTERLACE_IsInterlaced) && (dwInterlaceFlags & AMINTERLACE_1FieldPerSample)) return TRUE; else return FALSE; } BOOL CheckTypeSpecificFlags(DWORD dwInterlaceFlags, DWORD dwTypeSpecificFlags) { // first determine which field do we want to display here if ((dwInterlaceFlags & AMINTERLACE_1FieldPerSample) && ((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_INTERLEAVED_FRAME)) { return FALSE; } if ((!(dwInterlaceFlags & AMINTERLACE_1FieldPerSample)) && (((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_FIELD1) || ((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_FIELD2))) { return FALSE; } if (dwTypeSpecificFlags & AM_VIDEO_FLAG_REPEAT_FIELD) { return FALSE; } return TRUE; } // given the interlace flags and the type-specific flags, this function determines whether we // are supposed to display the sample in bob-mode or not. It also tells us, which direct-draw flag // are we supposed to use when flipping. When displaying an interleaved frame, it assumes we are // talking about the field which is supposed to be displayed first. BOOL NeedToFlipOddEven(DWORD dwInterlaceFlags, DWORD dwTypeSpecificFlags, DWORD *pdwFlipFlag) { BOOL bDisplayField1 = TRUE; BOOL bField1IsOdd = TRUE; BOOL bNeedToFlipOddEven = FALSE; DWORD dwFlipFlag = 0; // if not interlaced content, mode is not bob if (!(dwInterlaceFlags & AMINTERLACE_IsInterlaced)) { bNeedToFlipOddEven = FALSE; goto CleanUp; } // if sample have a single field, then check the field pattern if ((dwInterlaceFlags & AMINTERLACE_1FieldPerSample) && (((dwInterlaceFlags & AMINTERLACE_FieldPatternMask) == AMINTERLACE_FieldPatField1Only) || ((dwInterlaceFlags & AMINTERLACE_FieldPatternMask) == AMINTERLACE_FieldPatField2Only))) { bNeedToFlipOddEven = FALSE; goto CleanUp; } if (((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) == AMINTERLACE_DisplayModeBobOnly) || (((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) == AMINTERLACE_DisplayModeBobOrWeave) && (!(dwTypeSpecificFlags & AM_VIDEO_FLAG_WEAVE)))) { // first determine which field do we want to display here if (dwInterlaceFlags & AMINTERLACE_1FieldPerSample) { // if we are in 1FieldPerSample mode, check which field is it ASSERT(((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_FIELD1) || ((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_FIELD2)); bDisplayField1 = ((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_FIELD1); } else { // ok the sample is an interleaved frame ASSERT((dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD_MASK) == AM_VIDEO_FLAG_INTERLEAVED_FRAME); bDisplayField1 = (dwTypeSpecificFlags & AM_VIDEO_FLAG_FIELD1FIRST); } bField1IsOdd = (dwInterlaceFlags & AMINTERLACE_Field1First); // if we displaying field 1 and field 1 is odd or we are displaying field2 and field 2 is odd // then use DDFLIP_ODD. Exactly the opposite for DDFLIP_EVEN if ((bDisplayField1 && bField1IsOdd) || (!bDisplayField1 && !bField1IsOdd)) dwFlipFlag = DDFLIP_ODD; else dwFlipFlag = DDFLIP_EVEN; bNeedToFlipOddEven = TRUE; goto CleanUp; } CleanUp: if (pdwFlipFlag) *pdwFlipFlag = dwFlipFlag; return bNeedToFlipOddEven; } // given the interlace flags and the type-specific flags, this function determines whether we // are supposed to display the sample in bob-mode or not. It also tells us, which direct-draw flag // are we supposed to use when flipping. When displaying an interleaved frame, it assumes we are // talking about the field which is supposed to be displayed first. DWORD GetUpdateOverlayFlags(DWORD dwInterlaceFlags, DWORD dwTypeSpecificFlags) { DWORD dwFlags = DDOVER_SHOW | DDOVER_KEYDEST; DWORD dwFlipFlag; if (NeedToFlipOddEven(dwInterlaceFlags, dwTypeSpecificFlags, &dwFlipFlag)) { dwFlags |= DDOVER_BOB; if (!DisplayingFields(dwInterlaceFlags)) dwFlags |= DDOVER_INTERLEAVED; } return dwFlags; } // this function checks if the InterlaceFlags are suitable or not HRESULT CVPMInputPin::CheckInterlaceFlags(DWORD dwInterlaceFlags) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::CheckInterlaceFlags"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (dwInterlaceFlags & AMINTERLACE_UNUSED) { hr = VFW_E_TYPE_NOT_ACCEPTED; goto CleanUp; } // check that the display mode is one of the three allowed values if (((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) != AMINTERLACE_DisplayModeBobOnly) && ((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) != AMINTERLACE_DisplayModeWeaveOnly) && ((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) != AMINTERLACE_DisplayModeBobOrWeave)) { hr = VFW_E_TYPE_NOT_ACCEPTED; goto CleanUp; } // if content is not interlaced, other bits are irrelavant, so we are done if (!(dwInterlaceFlags & AMINTERLACE_IsInterlaced)) { goto CleanUp; } // samples are frames, not fields (so we can handle any display mode) if (!(dwInterlaceFlags & AMINTERLACE_1FieldPerSample)) { goto CleanUp; } // can handle a stream of just field1 or field2, whatever the display mode if (((dwInterlaceFlags & AMINTERLACE_FieldPatternMask) == AMINTERLACE_FieldPatField1Only) || ((dwInterlaceFlags & AMINTERLACE_FieldPatternMask) == AMINTERLACE_FieldPatField2Only)) { goto CleanUp; } // can handle only bob-mode for field samples if ((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) == AMINTERLACE_DisplayModeBobOnly) { goto CleanUp; } // cannot handle only Weave mode or BobOrWeave mode for field samples if (((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) == AMINTERLACE_DisplayModeWeaveOnly) || ((dwInterlaceFlags & AMINTERLACE_DisplayModeMask) == AMINTERLACE_DisplayModeBobOrWeave)) { hr = VFW_E_TYPE_NOT_ACCEPTED; goto CleanUp; } // we should have covered all possible scenarios by now, so assert here ASSERT(1); CleanUp: // we cannot handle bob mode with an offscreen surface or if the driver can't support it if (SUCCEEDED(hr)) { const DDCAPS* pDirectCaps = m_pVPMFilter.GetHardwareCaps(); if ( pDirectCaps ) { // call NeedToFlipOddEven with dwTypeSpecificFlags=0, to pretend that the // type-specific-flags is asking us to do bob-mode. bool bCanBob = false; if ( !bCanBob && NeedToFlipOddEven(dwInterlaceFlags, 0, NULL) ) { hr = VFW_E_TYPE_NOT_ACCEPTED; } } } return hr; } // this function check if the mediatype on a dynamic format change is suitable. // No lock is taken here. It is the callee's responsibility to maintain integrity! HRESULT CVPMInputPin::DynamicCheckMediaType(const CMediaType* pmt) { HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED; DWORD dwOldInterlaceFlags = 0, dwNewInterlaceFlags = 0, dwCompareSize = 0; BOOL bOld1FieldPerSample = FALSE, bNew1FieldPerSample = FALSE; BOOL b1, b2; AMTRACE((TEXT("CVPMInputPin::DynamicCheckMediaType"))); // majortype and SubType are not allowed to change dynamically, // format type can change. CMediaType mtNew; hr = m_pIVPObject->CurrentMediaType( &mtNew ); if (FAILED(hr) || NULL == pmt || (!(IsEqualGUID(pmt->majortype, mtNew.majortype))) || (!(IsEqualGUID(pmt->subtype, mtNew.subtype)))) { goto CleanUp; } // get the interlace flags of the new mediatype hr = VPMUtil::GetInterlaceFlagsFromMediaType( *pmt, &dwNewInterlaceFlags); if (FAILED(hr)) { goto CleanUp; } // get the interlace flags of the new mediatype hr = VPMUtil::GetInterlaceFlagsFromMediaType( mtNew, &dwOldInterlaceFlags); if (FAILED(hr)) { goto CleanUp; } // // There are several bugs in the following code !! // We goto CleanUp but hr has not been updated with a valid error code!! // bOld1FieldPerSample = (dwOldInterlaceFlags & AMINTERLACE_IsInterlaced) && (dwOldInterlaceFlags & AMINTERLACE_1FieldPerSample); bNew1FieldPerSample = (dwNewInterlaceFlags & AMINTERLACE_IsInterlaced) && (dwNewInterlaceFlags & AMINTERLACE_1FieldPerSample); // we do not allow dynamic format changes where you go from 1FieldsPerSample to // 2FieldsPerSample or vica-versa since that means reallocating the surfaces. if (bNew1FieldPerSample != bOld1FieldPerSample) { goto CleanUp; } const BITMAPINFOHEADER* pNewHeader = VPMUtil::GetbmiHeader(pmt); if (!pNewHeader) { goto CleanUp; } const BITMAPINFOHEADER* pOldHeader = VPMUtil::GetbmiHeader(&mtNew); if (!pNewHeader) { goto CleanUp; } dwCompareSize = FIELD_OFFSET(BITMAPINFOHEADER, biClrUsed); ASSERT(dwCompareSize < sizeof(BITMAPINFOHEADER)); if (memcmp(pNewHeader, pOldHeader, dwCompareSize) != 0) { goto CleanUp; } hr = NOERROR; CleanUp: // CVPMInputPin::DynamicCheckMediaType"))); return hr; } // check that the mediatype is acceptable. No lock is taken here. It is the callee's // responsibility to maintain integrity! HRESULT CVPMInputPin::CheckMediaType(const CMediaType* pmt) { AMTRACE((TEXT("CVPMInputPin::CheckMediaType"))); // check if the VP component likes this mediatype // check if the videoport object likes it HRESULT hr = m_pIVPObject->CheckMediaType(pmt); if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("m_pIVPObject->CheckMediaType failed, hr = 0x%x"), hr)); ASSERT( hr == VFW_E_TYPE_NOT_ACCEPTED ); // can't fail with anything else } else { DbgLog((LOG_TRACE, 2, TEXT("m_pIVPObject->CheckMediaType succeeded, bAcceptableVPMediatype is TRUE"))); } return hr; } // called after we have agreed a media type to actually set it HRESULT CVPMInputPin::SetMediaType(const CMediaType* pmt) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::SetMediaType"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // make sure the mediatype is correct hr = CheckMediaType(pmt); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CheckMediaType failed, hr = 0x%x"), hr)); goto CleanUp; } const BITMAPINFOHEADER *pHeader = VPMUtil::GetbmiHeader(pmt); if (pHeader) { // store the interlace flags since we use them again and again hr = VPMUtil::GetInterlaceFlagsFromMediaType( *pmt, &m_dwInterlaceFlags); ASSERT(SUCCEEDED(hr)); // store the update overlay flags (give the type specific flag is WEAVE so that for BOB or WEAVE // mode, we not bob m_dwUpdateOverlayFlags = GetUpdateOverlayFlags(m_dwInterlaceFlags, AM_VIDEO_FLAG_WEAVE); } // Set the base class media type (should always succeed) hr = CBaseInputPin::SetMediaType(pmt); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::SetMediaType failed, hr = 0x%x"), hr)); goto CleanUp; } hr = m_pIVPObject->CheckMediaType(pmt); if (SUCCEEDED(hr)) { m_pVPMFilter.SetDecimationUsage(DECIMATION_LEGACY); hr = m_pIVPObject->SetMediaType(pmt); ASSERT(SUCCEEDED(hr)); } // tell the proxy not to allocate buffers if it is a videoport or overlay connection SetStreamingInKernelMode(TRUE); // tell the owning filter hr = m_pVPMFilter.SetMediaType(m_dwPinId, pmt); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pVPMFilter.SetMediaType failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: return hr; } HRESULT CVPMInputPin::CurrentMediaType(CMediaType *pmt) { ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE)); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); /* Copy constructor of m_mt allocates the memory */ if (IsConnected()) { if( m_pIVPObject ) { return m_pIVPObject->CurrentMediaType( pmt ); } else { // shouldn't happen, we alloc this in our constructor pmt->InitMediaType(); return E_FAIL; } } else { pmt->InitMediaType(); return VFW_E_NOT_CONNECTED; } } #ifdef DEBUG /*****************************Private*Routine******************************\ * VideoFormat2String * * Converts a video format block to a string - useful for debugging * * History: * Tue 12/07/1999 - StEstrop - Created * \**************************************************************************/ void VideoFormat2String( LPTSTR szBuffer, const GUID* pFormatType, BYTE* pFormat, ULONG lFormatLength ) { if (!pFormat) { lstrcpy(szBuffer, TEXT("No format data specified")); return; } // // Video Format // if (IsEqualGUID(*pFormatType, FORMAT_VideoInfo) || IsEqualGUID(*pFormatType, FORMAT_MPEGVideo)) { VIDEOINFO * pVideoFormat = (VIDEOINFO *) pFormat; wsprintf(szBuffer, TEXT("%4.4hs %dx%d, %d bits"), (pVideoFormat->bmiHeader.biCompression == 0) ? "RGB " : ((pVideoFormat->bmiHeader.biCompression == BI_BITFIELDS) ? "BITF" : (LPSTR) &pVideoFormat->bmiHeader.biCompression), pVideoFormat->bmiHeader.biWidth, pVideoFormat->bmiHeader.biHeight, pVideoFormat->bmiHeader.biBitCount); } else if (IsEqualGUID(*pFormatType, FORMAT_VideoInfo2) || IsEqualGUID(*pFormatType, FORMAT_MPEG2Video)) { VIDEOINFOHEADER2 * pVideoFormat = (VIDEOINFOHEADER2 *) pFormat; wsprintf(szBuffer, TEXT("%4.4hs %dx%d, %d bits"), (pVideoFormat->bmiHeader.biCompression == 0) ? "RGB " : ((pVideoFormat->bmiHeader.biCompression == BI_BITFIELDS) ? "BITF" : (LPSTR) &pVideoFormat->bmiHeader.biCompression ), pVideoFormat->bmiHeader.biWidth, pVideoFormat->bmiHeader.biHeight, pVideoFormat->bmiHeader.biBitCount); } else { lstrcpy(szBuffer, TEXT("Unknown format")); } } #endif // pConnector is the initiating connecting pin // pmt is the media type we will exchange // This function is also called while the graph is running when the // up stream decoder filter wants to change the size of the // decoded video. // // If the up stream decoder wants to change from one transport // type to another, eg. from MoComp back to IMemInputPin then it // should perform a dynamic filter reconnect via the IGraphConfig // Reconnect method. // STDMETHODIMP CVPMInputPin::ReceiveConnection(IPin * pConnector, const AM_MEDIA_TYPE *pmt) { HRESULT hr = NOERROR; CVPMInputAllocator * pAlloc = NULL; CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); CheckPointer(pmt, E_POINTER); CMediaType cmt(*pmt); if (m_Connected != pConnector || pConnector == NULL) { hr = CBaseInputPin::ReceiveConnection(pConnector, &cmt); goto CleanUp; } #ifdef DEBUG DbgLog((LOG_TRACE, 2, TEXT("ReceiveConnection when connected"))); if (pmt) { TCHAR szFmt[128]; VideoFormat2String(szFmt, &pmt->formattype, pmt->pbFormat, pmt->cbFormat); DbgLog((LOG_TRACE, 2, TEXT("Format is: %s"), szFmt)); } #endif { /* Can only do this if the allocator can be reconfigured */ pAlloc = (CVPMInputAllocator *)m_pAllocator; if (!pAlloc) { hr = E_FAIL; DbgLog((LOG_TRACE, 2, TEXT("ReceiveConnection: Failed because of no allocator"))); goto CleanUp; } if (!pAlloc->CanFree()) { hr = VFW_E_WRONG_STATE; DbgLog((LOG_TRACE, 2, TEXT("ReceiveConnection: Failed because allocator can't free"))); goto CleanUp; } } m_bConnected = FALSE; hr = CheckMediaType(&cmt); if (FAILED(hr)) { DbgLog((LOG_TRACE, 2, TEXT("ReceiveConnection: CheckMediaType failed"))); goto CleanUp; } ALLOCATOR_PROPERTIES Props; { pAlloc->Decommit(); pAlloc->GetProperties(&Props); } // back buffers are not addref'd so just set them to NULL m_dwBackBufferCount = 0; m_dwDirectDrawSurfaceWidth = 0; SetMediaType(&cmt); { ALLOCATOR_PROPERTIES PropsActual; Props.cbBuffer = pmt->lSampleSize; hr = pAlloc->SetProperties(&Props, &PropsActual); if (SUCCEEDED(hr)) { hr = pAlloc->Commit(); } } hr = UpdateMediaType(); ASSERT(SUCCEEDED(hr)); m_bConnected = TRUE; CleanUp: return hr; } HRESULT CVPMInputPin::CheckConnect(IPin * pReceivePin) { HRESULT hr = NOERROR; PKSMULTIPLE_ITEM pMediumList = NULL; IKsPin *pIKsPin = NULL; PKSPIN_MEDIUM pMedium = NULL; AMTRACE((TEXT("CVPMInputPin::CheckConnect"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); hr = pReceivePin->QueryInterface(IID_IKsPin, (void **)&pIKsPin); if (SUCCEEDED(hr)) { ASSERT(pIKsPin); hr = pIKsPin->KsQueryMediums(&pMediumList); } if( SUCCEEDED( hr )) { ASSERT(pMediumList); pMedium = (KSPIN_MEDIUM *)(pMediumList+1); SetKsMedium((const KSPIN_MEDIUM *)pMedium); } // CleanUp: // call the base class hr = CBaseInputPin::CheckConnect(pReceivePin); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::CheckConnect failed, hr = 0x%x"), hr)); } RELEASE(pIKsPin); if (pMediumList) { CoTaskMemFree((void*)pMediumList); pMediumList = NULL; } return hr; } HRESULT CVPMInputPin::UpdateMediaType() { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::UpdateMediaType"))); return hr; } // final connect HRESULT CVPMInputPin::FinalConnect() { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::FinalConnect"))); if (m_bConnected) { hr = E_FAIL; goto CleanUp; } // update the mediatype, tell the filter about the updated dimensions hr = UpdateMediaType(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("UpdateMediaType failed, hr = 0x%x"), hr)); goto CleanUp; } // tell the filter (might involve a reconnection with the output pin) hr = m_pVPMFilter.CompleteConnect(m_dwPinId); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pVPMFilter.CompleteConnect failed, hr = 0x%x"), hr)); goto CleanUp; } m_bConnected = TRUE; CleanUp: return hr; } // Complete Connect HRESULT CVPMInputPin::CompleteConnect(IPin *pReceivePin) { HRESULT hr = NOERROR; AMVPDATAINFO amvpDataInfo; BITMAPINFOHEADER *pHeader = NULL; AMTRACE((TEXT("CVPMInputPin::CompleteConnect"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); { // tell the videoport object hr = m_pIVPObject->CompleteConnect(pReceivePin); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->CompleteConnect failed, hr = 0x%x"), hr)); goto CleanUp; } m_bRuntimeNegotiationFailed = FALSE; } // call the base class hr = CBaseInputPin::CompleteConnect(pReceivePin); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::CompleteConnect failed, hr = 0x%x"), hr)); goto CleanUp; } ASSERT(SUCCEEDED(hr)); { // tell the proxy not to allocate buffers if it is a videoport or overlay connection SetStreamingInKernelMode(TRUE); hr = FinalConnect(); // ASSERT(SUCCEEDED(hr)); if( FAILED(hr) ) { SetStreamingInKernelMode(FALSE); DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::FinalConnect failed, hr = 0x%x"), hr)); goto CleanUp; } } // the decoders can support a particular property set to tell the ovmixer to not to try to over-allocate // buffers incase they want complete control over the buffers etc { HRESULT hr1 = NOERROR; IKsPropertySet *pIKsPropertySet = NULL; DWORD dwVal = 0, dwBytesReturned = 0; hr1 = pReceivePin->QueryInterface(IID_IKsPropertySet, (void**)&pIKsPropertySet); if (SUCCEEDED(hr1)) { ASSERT(pIKsPropertySet); if (!pIKsPropertySet) { DbgLog((LOG_ERROR, 1, TEXT("pIKsPropertySet == NULL, even though QI returned success"))); goto CleanUp; } hr1 = pIKsPropertySet->Get( PROPSETID_ALLOCATOR_CONTROL, KSPROPERTY_ALLOCATOR_CONTROL_HONOR_COUNT, NULL, 0, &dwVal, sizeof(dwVal), &dwBytesReturned); DbgLog((LOG_TRACE, 2, TEXT("pIKsPropertySet->Get(KSPROPSETID_ALLOCATOR_CONTROL), hr1 = 0x%x, dwVal == %d, dwBytesReturned == %d"), hr1, dwVal, dwBytesReturned)); // if the decoder supports this property // and its value is 1 and the decoder supports DDKERNELCAPS_FLIPOVERLAY, // than we will do exactly honour its request and the // and not make any attempt to allocate more in order to prevent tearing // if ((SUCCEEDED(hr1)) && (dwVal == 1) && (dwBytesReturned == sizeof(dwVal)) && (DDKERNELCAPS_FLIPOVERLAY & m_pVPMFilter.KernelCaps())) { DbgLog((LOG_TRACE, 2, TEXT("setting m_bCanOverAllocateBuffers == FALSE"))); m_bCanOverAllocateBuffers = FALSE; } pIKsPropertySet->Release(); } } CleanUp: return hr; } HRESULT CVPMInputPin::OnSetProperties(ALLOCATOR_PROPERTIES* pRequest, ALLOCATOR_PROPERTIES* pActual) { HRESULT hr = NOERROR; IPin *pReceivePin = NULL; DDSURFACEDESC ddSurfaceDesc; IEnumMediaTypes *pEnumMediaTypes = NULL; CMediaType cMediaType; AM_MEDIA_TYPE *pNewMediaType = NULL, *pEnumeratedMediaType = NULL; ULONG ulFetched = 0; DWORD dwMaxBufferCount = 0; BOOL bFoundSuitableSurface = FALSE; BITMAPINFOHEADER *pHeader = NULL; LPDDCAPS pDirectCaps = NULL; AMTRACE((TEXT("CVPMInputPin::OnSetProperties"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // this function is only called after the base class CBaseAllocator::SetProperties() has been called // with the above parameters, so we don't have to do any parameter validation ASSERT(IsConnected()); pReceivePin = CurrentPeer(); ASSERT(pReceivePin); // we only care about the number of buffers requested, rest everything is ignored if (pRequest->cBuffers <= 0) { hr = E_FAIL; goto CleanUp; } CleanUp: return hr; } HRESULT CVPMInputPin::BreakConnect(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::BreakConnect"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); { // tell the videoport object ASSERT(m_pIVPObject); hr = m_pIVPObject->BreakConnect(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->BreakConnect failed, hr = 0x%x"), hr)); } } { // back buffers are not addref'd so just set them to NULL m_dwBackBufferCount = 0; m_dwDirectDrawSurfaceWidth = 0; } // initialize the behaviour to telling the proxy to allocate buffers SetStreamingInKernelMode(FALSE); m_bUsingOurAllocator = FALSE; m_bCanOverAllocateBuffers = TRUE; if (m_hMemoryDC) { EXECUTE_ASSERT(DeleteDC(m_hMemoryDC)); m_hMemoryDC = NULL; } // call the base class hr = CBaseInputPin::BreakConnect(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::BreakConnect failed, hr = 0x%x"), hr)); } // tell the owning filter hr = m_pVPMFilter.BreakConnect(m_dwPinId); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pVPMFilter.BreakConnect failed, hr = 0x%x"), hr)); } m_bConnected = FALSE; //CleanUp: return hr; } STDMETHODIMP CVPMInputPin::GetState(DWORD dwMSecs,FILTER_STATE *pState) { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // if not connected or VideoPort Connection or IOverlay connection, then let the base class handle it // otherwise (overlay, offcreen, gdi, motion-comp) let the sync object handle it return E_NOTIMPL; } HRESULT CVPMInputPin::CompleteStateChange(FILTER_STATE OldState) { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); return S_OK; } // transition from stop to pause state HRESULT CVPMInputPin::Active(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::Active"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); m_hEndOfStream = NULL; { // tell the videoport object hr = m_pIVPObject->Active(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->Active failed, hr = 0x%x"), hr)); goto CleanUp; } } // call the base class hr = CBaseInputPin::Active(); // if it is a VP connection, this error is ok if (hr == VFW_E_NO_ALLOCATOR) { hr = NOERROR; } if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::Active failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: return hr; } // transition from pause to stop state HRESULT CVPMInputPin::Inactive(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::Inactive"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); { // tell the videoport object hr = m_pIVPObject->Inactive(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->Inactive failed, hr = 0x%x"), hr)); goto CleanUp; } // make sure that if there is a run time error, stop succeeds if (m_bRuntimeNegotiationFailed && hr == VFW_E_NOT_CONNECTED) { hr = NOERROR; } } // call the base class hr = CBaseInputPin::Inactive(); // if it is a VP connection, this error is ok if ( hr == VFW_E_NO_ALLOCATOR) { hr = NOERROR; } if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::Inactive failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: return hr; } // transition from pause to run state HRESULT CVPMInputPin::Run(REFERENCE_TIME tStart) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::Run"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); m_bDontFlip = FALSE ; // need to reset it to do the right things in this session { // tell the videoport object hr = m_pIVPObject->Run(tStart); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->Run() failed, hr = 0x%x"), hr)); goto CleanUp; } } // call the base class hr = CBaseInputPin::Run(tStart); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::Run failed, hr = 0x%x"), hr)); goto CleanUp; } // TBD: figure out ... stream time m_rtNextSample = 0; m_rtLastRun = tStart; // just start the src video running, we'll have an output image when we get a sample hr = InitVideo(); CleanUp: m_trLastFrame = -1; return hr; } // transition from run to pause state HRESULT CVPMInputPin::RunToPause(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::RunToPause"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // only if a vp pin if( m_pIVPObject ) { // tell the videoport object hr = m_pIVPObject->RunToPause(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->RunToPause() failed, hr = 0x%x"), hr)); goto CleanUp; } } CleanUp: return hr; } // signals start of flushing on the input pin HRESULT CVPMInputPin::BeginFlush(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::BeginFlush"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); m_hEndOfStream = 0; if (m_bFlushing) { return E_FAIL; } // if the conection is VideoPort or IOverlay, we do not care about flushing // call the base class hr = CBaseInputPin::BeginFlush(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::BeginFlush() failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: return hr; } // signals end of flushing on the input pin HRESULT CVPMInputPin::EndFlush(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::EndFlush"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (!m_bFlushing) { return E_FAIL; } // if the conection is VideoPort or IOverlay, we do not care about flushing // call the base class hr = CBaseInputPin::EndFlush(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseInputPin::EndFlush() failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: return hr; } // Send a quality message if required - this is the hack version // that just passes the lateness void CVPMInputPin::DoQualityMessage() { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (m_pVPMFilter.m_State == State_Running && SampleProps()->dwSampleFlags & AM_SAMPLE_TIMEVALID) { CRefTime CurTime; if (S_OK == m_pVPMFilter.StreamTime(CurTime)) { const REFERENCE_TIME tStart = SampleProps()->tStart; Quality msg; msg.Proportion = 1000; msg.Type = CurTime > tStart ? Flood : Famine; msg.Late = CurTime - tStart; msg.TimeStamp = tStart; PassNotify(msg); m_trLastFrame = CurTime; } } } // called when the upstream pin delivers us a sample HRESULT CVPMInputPin::Receive(IMediaSample *pMediaSample) { HRESULT hr = NOERROR; BOOL bNeedToFlipOddEven = FALSE; BOOL bDisplayingFields = FALSE; DWORD dwTypeSpecificFlags = 0; LPDIRECTDRAWSURFACE7 pPrimarySurface = NULL; AMTRACE((TEXT("CVPMInputPin::Receive"))); // a videoport connection does not receive samples so bail out { hr = VFW_E_NOT_SAMPLE_CONNECTION; goto CleanUp; } CleanUp: return hr; } HRESULT CVPMInputPin::OnReceiveFirstSample(IMediaSample *pMediaSample) { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); ASSERT( !"OnReceiveFirstSample" ); return NOERROR; } HRESULT CVPMInputPin::InitVideo() { HRESULT hr = m_pIVPObject->StartVideo( &m_WinInfo ); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pIVPObject->StartVideo failed, hr = 0x%x"), hr)); } return hr; } // this function just tells whether each sample consists of one or two fields static HRESULT SetTypeSpecificFlags(IMediaSample *pSample, DWORD dwTypeSpecificFlags ) { IMediaSample2 *pSample2 = NULL; /* Check for IMediaSample2 */ HRESULT hr = pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2); if (SUCCEEDED(hr)) { AM_SAMPLE2_PROPERTIES SampleProps; hr = pSample2->GetProperties(sizeof(SampleProps), (PBYTE)&SampleProps); if( SUCCEEDED( hr )) { SampleProps.dwTypeSpecificFlags = dwTypeSpecificFlags; hr = pSample2->SetProperties(sizeof(SampleProps), (PBYTE)&SampleProps); } pSample2->Release(); } return hr; } static REFERENCE_TIME ScaleMicroToRefTime( DWORD dwMicroseconds ) { // Reference time is in 100 ns = 0.1us, so multiply by 10 ASSERT( 10*1000000 == UNITS ); switch( dwMicroseconds ) { case 16667: case 16666: // 60hz return 166667; case 16683: // 59.94hz return 166834; case 20000: // 50hz PAL return REFERENCE_TIME(dwMicroseconds)*10; default: ASSERT( !"Missing ref scale" ); return REFERENCE_TIME(dwMicroseconds)*10; } } HRESULT CVPMInputPin::DoRenderSample(IMediaSample* pSample, LPDIRECTDRAWSURFACE7 pDDDestSurface, const DDVIDEOPORTNOTIFY& notify, const VPInfo& vpInfo ) { if( !pDDDestSurface ) { return E_INVALIDARG; } AMTRACE((TEXT("CVPMInputPin::DoRenderSample"))); CAutoLock cLock(&m_pVPMFilter.GetReceiveLock()); HRESULT hr = S_OK; if( SUCCEEDED( hr )) { hr = m_pIVPObject->CallUpdateSurface( notify.dwSurfaceIndex, pDDDestSurface ); if( SUCCEEDED( hr )) { REFERENCE_TIME rtStart = m_rtNextSample; // for debugging, assume continuous hr = m_pVPMFilter.GetRefClockTime( &rtStart ); ASSERT( SUCCEEDED( hr )); // make time relative to last run time, i.e. timestamps after run begin at 0 rtStart -= m_rtLastRun; // get the actual time REFERENCE_TIME rtInterval = ScaleMicroToRefTime( vpInfo.vpDataInfo.dwMicrosecondsPerField ); // now set the field info DWORD dwTypeFlags=0; #ifdef DEBUG static bool checked=false; #endif switch( vpInfo.mode ) { case AMVP_MODE_BOBNONINTERLEAVED: switch( notify.lField ) { case 0: dwTypeFlags = AM_VIDEO_FLAG_FIELD1; break; case 1: dwTypeFlags = AM_VIDEO_FLAG_FIELD2; break; case -1: #ifdef DEBUG if( !checked ) { ASSERT( !"Video driver doesn't known field for sample, VPM assuming Field1" ); checked=true; } #endif dwTypeFlags = AM_VIDEO_FLAG_FIELD1; break; default: #ifdef DEBUG if( !checked ) { ASSERT( !"Bogus field value returned by video driver for sample, assuming Field1" ); checked=true; } #endif dwTypeFlags = AM_VIDEO_FLAG_FIELD1; break; } break; case AMVP_MODE_BOBINTERLEAVED: if( !vpInfo.vpDataInfo.bFieldPolarityInverted ) { // Device inverts the polarity by default dwTypeFlags = AM_VIDEO_FLAG_FIELD1FIRST; } rtInterval *= 2; // 2 fields break; case AMVP_MODE_WEAVE: dwTypeFlags = AM_VIDEO_FLAG_WEAVE; rtInterval *= 2; // 2 fields break; case AMVP_MODE_SKIPEVEN: dwTypeFlags = AM_VIDEO_FLAG_FIELD1; break; case AMVP_MODE_SKIPODD: dwTypeFlags = AM_VIDEO_FLAG_FIELD2; break; default: break; } REFERENCE_TIME rtStop = rtStart+rtInterval; // set flags & timestamps hr = SetTypeSpecificFlags( pSample, dwTypeFlags); hr = pSample->SetTime(&rtStart, &rtStop); // assume next sample comes immediately afterwards m_rtNextSample += rtInterval; } } return hr; } HRESULT CVPMInputPin::StartVideo() { HRESULT hr = m_pIVPObject->StartVideo( &m_WinInfo ); ASSERT( SUCCEEDED( hr )); if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("InPin::StartVideo() failed, hr = %d"), hr & 0xffff)); } else { // hack for now, force a new dest recalc SetRect( &m_WinInfo.DestRect, 0,0,0,0); } return hr; } // signals end of data stream on the input pin STDMETHODIMP CVPMInputPin::EndOfStream(void) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::EndOfStream"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (m_hEndOfStream) { EXECUTE_ASSERT(SetEvent(m_hEndOfStream)); return S_OK; } // Make sure we're streaming ok hr = CheckStreaming(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CheckStreaming() failed, hr = 0x%x"), hr)); goto CleanUp; } { // Pass EOS to the filter graph hr = m_pVPMFilter.EventNotify(m_dwPinId, EC_COMPLETE, S_OK, 0); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pVPMFilter.EventNotify failed, hr = 0x%x"), hr)); } } CleanUp: return hr; } // signals end of data stream on the input pin HRESULT CVPMInputPin::EventNotify(long lEventCode, DWORD_PTR lEventParam1, DWORD_PTR lEventParam2) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::EventNotify"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // if (lEventCode == EC_OVMIXER_REDRAW_ALL || lEventCode == EC_REPAINT) // { // m_pVPMFilter.EventNotify(m_dwPinId, lEventCode, lEventParam1, lEventParam2); // goto CleanUp; // } // WARNING : we are assuming here that the input pin will be the first pin to be created if (lEventCode == EC_COMPLETE && m_dwPinId == 0) { m_pVPMFilter.EventNotify(m_dwPinId, lEventCode, lEventParam1, lEventParam2); goto CleanUp; } if (lEventCode == EC_ERRORABORT) { m_pVPMFilter.EventNotify(m_dwPinId, lEventCode, lEventParam1, lEventParam2); m_bRuntimeNegotiationFailed = TRUE; goto CleanUp; } if (lEventCode == EC_STEP_COMPLETE) { m_pVPMFilter.EventNotify(m_dwPinId, lEventCode, lEventParam1, lEventParam2); goto CleanUp; } CleanUp: return hr; } /******************************Public*Routine******************************\ * GetCaptureInfo * * * * History: * 3/12/1999 - StEstrop - Created * \**************************************************************************/ HRESULT CVPMInputPin::GetCaptureInfo( BOOL *lpCapturing, DWORD *lpdwWidth, DWORD *lpdwHeight, BOOL *lpInterleave ) { AMTRACE((TEXT("CVPMInputPin::GetCaptureInfo"))); HRESULT hr = NOERROR; IKsPropertySet *pIKsPropertySet = NULL; DWORD dwVal[2], dwBytesReturned = 0; *lpCapturing = FALSE; if (!m_Connected) { DbgLog((LOG_TRACE, 1, TEXT("Input pin not connected!!"))); hr = E_FAIL; goto CleanUp; } #if defined(DEBUG) else { PIN_INFO PinInfo; hr = m_Connected->QueryPinInfo(&PinInfo); if (SUCCEEDED(hr)) { DbgLog((LOG_TRACE, 1, TEXT("Up stream pin name %ls"), PinInfo.achName)); PinInfo.pFilter->Release(); } } #endif hr = m_Connected->QueryInterface(IID_IKsPropertySet, (void**)&pIKsPropertySet); if (SUCCEEDED(hr)) { ASSERT(pIKsPropertySet); hr = pIKsPropertySet->Set( PROPSETID_ALLOCATOR_CONTROL, AM_KSPROPERTY_ALLOCATOR_CONTROL_CAPTURE_CAPS, NULL, 0, lpInterleave, sizeof(*lpInterleave)); if (SUCCEEDED(hr)) { hr = pIKsPropertySet->Get( PROPSETID_ALLOCATOR_CONTROL, AM_KSPROPERTY_ALLOCATOR_CONTROL_CAPTURE_INTERLEAVE, NULL, 0, lpInterleave, sizeof(*lpInterleave), &dwBytesReturned); if (FAILED(hr) || dwBytesReturned != sizeof(*lpInterleave)) { *lpInterleave = FALSE; } } else { *lpInterleave = FALSE; } hr = pIKsPropertySet->Get( PROPSETID_ALLOCATOR_CONTROL, KSPROPERTY_ALLOCATOR_CONTROL_SURFACE_SIZE, NULL, 0, dwVal, sizeof(dwVal), &dwBytesReturned); DbgLog((LOG_TRACE, 2, TEXT("pIKsPropertySet->Get(") TEXT("PROPERTY_ALLOCATOR_CONTROL_SURFACE_SIZE),\n") TEXT("\thr = 0x%x, dwVal[0] == %d, dwVal[1] == %d, ") TEXT("dwBytesReturned == %d"), hr, dwVal[0], dwVal[1], dwBytesReturned)); // if the decoder supports this property then we are capturing // and the intended capturing is size is given by // dwVal[0] and dwVal[1] // if (SUCCEEDED(hr) && dwBytesReturned == sizeof(dwVal)) { *lpCapturing = TRUE; *lpdwWidth = dwVal[0]; *lpdwHeight = dwVal[1]; DbgLog((LOG_TRACE, 1, TEXT("We are CAPTURING, intended size (%d, %d) interleave = %d"), dwVal[0], dwVal[1], *lpInterleave)); } pIKsPropertySet->Release(); } CleanUp: return hr; } /******************************Public*Routine******************************\ * GetDecimationUsage * * * * History: * Thu 07/15/1999 - StEstrop - Created * \**************************************************************************/ HRESULT CVPMInputPin::GetDecimationUsage( DECIMATION_USAGE *lpdwUsage ) { return m_pVPMFilter.QueryDecimationUsage(lpdwUsage); } // This overrides the CBaseInputPin virtual method to return our allocator HRESULT CVPMInputPin::GetAllocator(IMemAllocator **ppAllocator) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::GetAllocator"))); if (!ppAllocator) { DbgLog((LOG_ERROR, 1, TEXT("ppAllocator is NULL"))); hr = E_POINTER; goto CleanUp; } { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // if vp connection, don't return any allocator { *ppAllocator = NULL; hr = VFW_E_NO_ALLOCATOR; goto CleanUp; } } CleanUp: return hr; } // GetAllocator // This overrides the CBaseInputPin virtual method to return our allocator HRESULT CVPMInputPin::NotifyAllocator(IMemAllocator *pAllocator,BOOL bReadOnly) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::NotifyAllocator"))); if (!pAllocator) { DbgLog((LOG_ERROR, 1, TEXT("ppAllocator is NULL"))); hr = E_INVALIDARG; goto CleanUp; } { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // if vp connection, don't care { goto CleanUp; } } CleanUp: return hr; } // NotifyAllocator HRESULT CVPMInputPin::OnAlloc(CDDrawMediaSample **ppSampleList, DWORD dwSampleCount) { HRESULT hr = NOERROR; DWORD i; LPDIRECTDRAWSURFACE7 pDDrawSurface = NULL, pBackBuffer = NULL; DDSCAPS ddSurfaceCaps; DWORD dwDDrawSampleSize = 0; BITMAPINFOHEADER *pHeader = NULL; DIBDATA DibData; AMTRACE((TEXT("CVPMInputPin::OnAlloc"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); ASSERT(IsConnected()); // get the image size { CMediaType mtNew; hr = m_pIVPObject->CurrentMediaType( &mtNew ); if( FAILED( hr )) { goto CleanUp; } pHeader = VPMUtil::GetbmiHeader(&mtNew); if ( ! pHeader ) { hr = E_FAIL; goto CleanUp; } dwDDrawSampleSize = pHeader->biSizeImage; } ASSERT(dwDDrawSampleSize > 0); if (!ppSampleList) { DbgLog((LOG_ERROR, 1, TEXT("ppSampleList is NULL"))); hr = E_INVALIDARG; goto CleanUp; } for (i = 0; i < dwSampleCount; i++) { if (!ppSampleList[i]) { DbgLog((LOG_ERROR, 1, TEXT("ppSampleList[%d] is NULL"), i)); hr = E_INVALIDARG; goto CleanUp; } hr = ppSampleList[i]->SetDDrawSampleSize(dwDDrawSampleSize); if (FAILED(hr)) { DbgLog((LOG_ERROR, 0, TEXT("ppSampleList[%d]->SetSampleSize failed, hr = 0x%x"), i, hr)); goto CleanUp; } } // end of for (i < dwSampleCount) loop CleanUp: return hr; } // sets the pointer to directdraw HRESULT CVPMInputPin::OnGetBuffer(IMediaSample **ppSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags) { HRESULT hr = NOERROR; CDDrawMediaSample *pCDDrawMediaSample = NULL; LPDIRECTDRAWSURFACE7 pBackBuffer = NULL; DDSURFACEDESC ddSurfaceDesc; BOOL bWaitForDraw = FALSE; BOOL bPalettised = FALSE; AMTRACE((TEXT("CVPMInputPin::OnGetBuffer"))); // not valid for videoport ASSERT( FALSE ) ; return hr; } // In case of flipping surfaces, gets the back buffer HRESULT CVPMInputPin::OnReleaseBuffer(IMediaSample *pMediaSample) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputPin::OnReleaseBuffer"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); return hr; } #if 0 /*****************************Private*Routine******************************\ * GetUpstreamFilterName * * * * History: * Tue 11/30/1999 - StEstrop - Created * \**************************************************************************/ HRESULT CVPMInputPin::GetUpstreamFilterName( TCHAR* FilterName ) { PIN_INFO PinInfo; if (!m_Connected) { return VFW_E_NOT_CONNECTED; } HRESULT hr = m_Connected->QueryPinInfo(&PinInfo); if (SUCCEEDED(hr)) { FILTER_INFO FilterInfo; hr = PinInfo.pFilter->QueryFilterInfo(&FilterInfo); if (SUCCEEDED(hr)) { #ifdef UNICODE wcscpy( FilterName, FilterInfo.achName ); #else wsprintf(FilterName, TEXT("%ls"), FilterInfo.achName); #endif if (FilterInfo.pGraph) { FilterInfo.pGraph->Release(); } } PinInfo.pFilter->Release(); } return hr; } #endif HRESULT CVPMInputPin::CreateDDrawSurface(CMediaType *pMediaType, DWORD *pdwMaxBufferCount, LPDIRECTDRAWSURFACE7 *ppDDrawSurface) { HRESULT hr = NOERROR; DDSURFACEDESC2 SurfaceDesc; DWORD dwInterlaceFlags = 0, dwTotalBufferCount = 0, dwMinBufferCount = 0; DDSCAPS ddSurfaceCaps; BITMAPINFOHEADER *pHeader; FOURCCMap amFourCCMap(pMediaType->Subtype()); AMTRACE((TEXT("CVPMInputPin::CreateDDrawSurface"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); LPDIRECTDRAW7 pDirectDraw = m_pVPMFilter.GetDirectDraw(); ASSERT(pDirectDraw); if (!pMediaType) { DbgLog((LOG_ERROR, 1, TEXT("pMediaType is NULL"))); hr = E_INVALIDARG; goto CleanUp; } if (!ppDDrawSurface) { DbgLog((LOG_ERROR, 1, TEXT("ppDDrawSurface is NULL"))); hr = E_INVALIDARG; goto CleanUp; } { hr = E_INVALIDARG; goto CleanUp; } CleanUp: return hr; } // this function is used to restore the ddraw surface. In the videoport case, we just recreate // the whole thing from scratch. HRESULT CVPMInputPin::RestoreDDrawSurface() { HRESULT hr = NOERROR; { // stop the video m_pIVPObject->Inactive(); // don't have to give up the IVPConfig interface here m_pIVPObject->BreakConnect(TRUE); // redo the connection process hr = m_pIVPObject->CompleteConnect(NULL, TRUE); } return hr; } HRESULT CVPMInputPin::GetSourceAndDest(RECT *prcSource, RECT *prcDest, DWORD *dwWidth, DWORD *dwHeight) { { m_pIVPObject->GetRectangles(prcSource, prcDest); } CMediaType mt; HRESULT hr = CurrentMediaType(&mt); if (SUCCEEDED(hr)) { BITMAPINFOHEADER *pHeader = VPMUtil::GetbmiHeader(&mt); if ( ! pHeader ) { hr = E_FAIL; } else { *dwWidth = abs(pHeader->biWidth); *dwHeight = abs(pHeader->biHeight); } } return hr; } STDMETHODIMP CVPMInputPin::Set(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData) { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); return E_PROP_SET_UNSUPPORTED ; } STDMETHODIMP CVPMInputPin::Get(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData, DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD *pcbReturned) { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); return E_PROP_SET_UNSUPPORTED; } STDMETHODIMP CVPMInputPin::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport) { CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (AMPROPSETID_Pin == guidPropSet) { if (AMPROPERTY_PIN_CATEGORY != dwPropID && AMPROPERTY_PIN_MEDIUM != dwPropID ) return E_PROP_ID_UNSUPPORTED ; if (pTypeSupport) *pTypeSupport = KSPROPERTY_SUPPORT_GET ; return S_OK; } return E_PROP_SET_UNSUPPORTED ; } STDMETHODIMP CVPMInputPin::KsQueryMediums(PKSMULTIPLE_ITEM* pMediumList) { PKSPIN_MEDIUM pMedium; CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); *pMediumList = reinterpret_cast(CoTaskMemAlloc(sizeof(**pMediumList) + sizeof(*pMedium))); if (!*pMediumList) { return E_OUTOFMEMORY; } (*pMediumList)->Count = 1; (*pMediumList)->Size = sizeof(**pMediumList) + sizeof(*pMedium); pMedium = reinterpret_cast(*pMediumList + 1); pMedium->Set = m_Medium.Set; pMedium->Id = m_Medium.Id; pMedium->Flags = m_Medium.Flags; // The following special return code notifies the proxy that this pin is // not available as a kernel mode connection return S_FALSE; } STDMETHODIMP CVPMInputPin::KsQueryInterfaces(PKSMULTIPLE_ITEM* pInterfaceList) { PKSPIN_INTERFACE pInterface; CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); *pInterfaceList = reinterpret_cast(CoTaskMemAlloc(sizeof(**pInterfaceList) + sizeof(*pInterface))); if (!*pInterfaceList) { return E_OUTOFMEMORY; } (*pInterfaceList)->Count = 1; (*pInterfaceList)->Size = sizeof(**pInterfaceList) + sizeof(*pInterface); pInterface = reinterpret_cast(*pInterfaceList + 1); pInterface->Set = KSINTERFACESETID_Standard; pInterface->Id = KSINTERFACE_STANDARD_STREAMING; pInterface->Flags = 0; return NOERROR; } STDMETHODIMP CVPMInputPin::KsGetCurrentCommunication(KSPIN_COMMUNICATION* pCommunication, KSPIN_INTERFACE* pInterface, KSPIN_MEDIUM* pMedium) { HRESULT hr = NOERROR; CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); if (!m_bStreamingInKernelMode) hr = S_FALSE; if (pCommunication != NULL) { *pCommunication = m_Communication; } if (pInterface != NULL) { pInterface->Set = KSINTERFACESETID_Standard; pInterface->Id = KSINTERFACE_STANDARD_STREAMING; pInterface->Flags = 0; } if (pMedium != NULL) { *pMedium = m_Medium; } return hr; } /******************************Public*Routine******************************\ * DynamicQueryAccept * * Do you accept this type change in your current state? * * History: * Wed 12/22/1999 - StEstrop - Created * \**************************************************************************/ STDMETHODIMP CVPMInputPin::DynamicQueryAccept( const AM_MEDIA_TYPE *pmt ) { AMTRACE((TEXT("CVPMInputPin::DynamicQueryAccept"))); CheckPointer(pmt, E_POINTER); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); // // I want CheckMedia type to behave as though we aren't connected to // anything yet - hence the messing about with m_bConnected. // CMediaType cmt(*pmt); BOOL bConnected = m_bConnected; m_bConnected = FALSE; HRESULT hr = CheckMediaType(&cmt); m_bConnected = bConnected; return hr; } /******************************Public*Routine******************************\ * NotifyEndOfStream * * * Set event when EndOfStream receive - do NOT pass it on * This condition is cancelled by a flush or Stop * * History: * Wed 12/22/1999 - StEstrop - Created * \**************************************************************************/ STDMETHODIMP CVPMInputPin::NotifyEndOfStream( HANDLE hNotifyEvent ) { AMTRACE((TEXT("CVPMInputPin::NotifyEndOfStream"))); CAutoLock cLock(&m_pVPMFilter.GetFilterLock()); m_hEndOfStream = hNotifyEvent; return S_OK; } /******************************Public*Routine******************************\ * IsEndPin * * Are you an 'end pin' * * History: * Wed 12/22/1999 - StEstrop - Created * \**************************************************************************/ STDMETHODIMP CVPMInputPin::IsEndPin() { AMTRACE((TEXT("CVPMInputPin::IsEndPin"))); return S_OK; } /******************************Public*Routine******************************\ * DynamicDisconnect * * Disconnect while running * * History: * Wed 2/7/1999 - SyonB - Created * \**************************************************************************/ STDMETHODIMP CVPMInputPin::DynamicDisconnect() { AMTRACE((TEXT("CVPMInputPin::DynamicDisconnect"))); CAutoLock l(m_pLock); return CBaseInputPin::DisconnectInternal(); } HRESULT CVPMInputPin::GetAllOutputFormats( const PixelFormatList** ppList ) { HRESULT hr; CAutoLock l(m_pLock); if (IsConnected() ) { hr = m_pIVPObject->GetAllOutputFormats( ppList ); } else { hr = VFW_E_NOT_CONNECTED; } return hr; } HRESULT CVPMInputPin::GetOutputFormat( DDPIXELFORMAT* pFormat ) { HRESULT hr; CAutoLock l(m_pLock); if (IsConnected() ) { hr = m_pIVPObject->GetOutputFormat( pFormat ); } else { hr = VFW_E_NOT_CONNECTED; } return hr; } HRESULT CVPMInputPin::SetVideoPortID( DWORD dwIndex ) { HRESULT hr = S_OK; CAutoLock l(m_pLock); if (m_pIVPObject ) { hr = m_pIVPObject->SetVideoPortID( dwIndex ); } return hr; } HRESULT CVPMInputPin::InPin_GetVPInfo( VPInfo* pVPInfo ) { HRESULT hr = E_FAIL; // Private: must hold streaming lock CAutoLock l(&m_pVPMFilter.GetReceiveLock()); if (m_pIVPInfo ) { hr = m_pIVPInfo->GetVPDataInfo( &pVPInfo->vpDataInfo ); if( SUCCEEDED( hr )) { hr = m_pIVPInfo->GetVPInfo( &pVPInfo->vpInfo ); } if( SUCCEEDED( hr )) { hr = m_pIVPObject->GetMode( &pVPInfo->mode ); } } return hr; } LPDIRECTDRAW7 CVPMInputPin::GetDirectDraw() { return m_pVPMFilter.GetDirectDraw(); } const DDCAPS* CVPMInputPin::GetHardwareCaps() { return m_pVPMFilter.GetHardwareCaps(); } HRESULT CVPMInputPin::SignalNewVP( LPDIRECTDRAWVIDEOPORT pVP ) { return m_pVPMFilter.SignalNewVP( pVP ); } //========================================================================== HRESULT CVPMInputPin::GetMediaType(int iPosition, CMediaType *pmt) { CAutoLock cLock( &m_pVPMFilter.GetFilterLock() ); AMTRACE((TEXT("Entering CVBIInputPin::GetMediaType"))); HRESULT hr = m_pIVPObject->GetMediaType(iPosition, pmt); return hr; }