You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2234 lines
63 KiB
2234 lines
63 KiB
// Copyright (c) 1998 - 1999 Microsoft Corporation. All Rights Reserved.
|
|
#include <streams.h>
|
|
#include <ddraw.h>
|
|
#include <VPManager.h>
|
|
#include "VPMPin.h"
|
|
#include <VPMUtil.h>
|
|
#include <ddkernel.h>
|
|
|
|
#include <ksmedia.h>
|
|
|
|
|
|
// IVideoPortControl
|
|
#include <VPObj.h>
|
|
|
|
// AMINTERLACE_*
|
|
#include <dvdmedia.h>
|
|
#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<LPUNKNOWN>( static_cast<PNDUNKNOWN>(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<IVPNotify*>(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<IVPNotify2*>(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<IKsPin *>(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<IKsPropertySet *>(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<IPinConnection*>(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<ISpecifyPropertyPages *>(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<PKSMULTIPLE_ITEM>(CoTaskMemAlloc(sizeof(**pMediumList) + sizeof(*pMedium)));
|
|
if (!*pMediumList)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
(*pMediumList)->Count = 1;
|
|
(*pMediumList)->Size = sizeof(**pMediumList) + sizeof(*pMedium);
|
|
pMedium = reinterpret_cast<PKSPIN_MEDIUM>(*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<PKSMULTIPLE_ITEM>(CoTaskMemAlloc(sizeof(**pInterfaceList) + sizeof(*pInterface)));
|
|
if (!*pInterfaceList)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
(*pInterfaceList)->Count = 1;
|
|
(*pInterfaceList)->Size = sizeof(**pInterfaceList) + sizeof(*pInterface);
|
|
pInterface = reinterpret_cast<PKSPIN_INTERFACE>(*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;
|
|
}
|
|
|