Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1931 lines
60 KiB

//==========================================================================
//
// Copyright (C) Microsoft Corporation, 1997 - 1999 All Rights Reserved.
//
//--------------------------------------------------------------------------
#include <streams.h>
#include <ddraw.h>
#include <VBIObj.h>
#include <VPMUtil.h>
#include <vpconfig.h>
#include <ddkernel.h>
#include <KHandleArray.h>
#include <FormatList.h>
//==========================================================================
// constructor
CVBIVideoPort::CVBIVideoPort(LPUNKNOWN pUnk, HRESULT* phr)
: CUnknown(NAME("VBI Object"), pUnk)
, m_pDDVPContainer(NULL)
, m_pOffscreenSurf(NULL)
, m_pOffscreenSurf1( NULL )
, m_bConnected(FALSE)
, m_pIVPConfig(NULL)
, m_VPState(VP_STATE_NO_VP)
, m_bFilterRunning(FALSE)
, m_Communication(KSPIN_COMMUNICATION_SOURCE)
, m_CategoryGUID(GUID_NULL)
, m_pDirectDraw(NULL)
, m_dwPixelsPerSecond(0)
, m_dwVideoPortId(0)
, m_pVideoPort(NULL)
, m_dwDefaultOutputFormat( 0 )
{
AMTRACE((TEXT("CVBIVideoPort::Constructor")));
m_Medium.Set = GUID_NULL;
m_Medium.Id = 0;
m_Medium.Flags = 0;
ZeroStruct( m_capVPDataInfo );
ZeroStruct( m_svpInfo );
ZeroStruct( m_vpCaps );
ZeroStruct( m_vpConnectInfo );
ZeroStruct( m_ddVPInputVideoFormat );
ZeroStruct( m_ddVPOutputVideoFormat );
}
//==========================================================================
// destructor
CVBIVideoPort::~CVBIVideoPort()
{
AMTRACE((TEXT("CVBIVideoPort::Destructor")));
if (m_bConnected)
{
DbgLog((LOG_ERROR, 0, TEXT("Destructor called without calling breakconnect")));
BreakConnect();
}
return;
}
//==========================================================================
// overridden to expose IVPVBINotify and IVideoPortVBIObject
STDMETHODIMP CVBIVideoPort::NonDelegatingQueryInterface(REFIID riid, void** ppv)
{
HRESULT hr = NOERROR;
if (riid == IID_IVPVBINotify)
{
DbgLog((LOG_TRACE, 4, TEXT("CVBIVideoPort::NonDelegatingQueryInterface(IID_IVPVBINotify)")));
hr = GetInterface( static_cast<IVPVBINotify*>( this ), ppv);
if (FAILED(hr)) {
DbgLog((LOG_ERROR, 0, TEXT("GetInterface(IVPVBINotify*) failed, hr = 0x%x"), hr));
}
}
else if (riid == IID_IKsPin)
{
DbgLog((LOG_TRACE, 4, TEXT("CVBIVideoPort::NonDelegatingQueryInterface(IID_IKsPin)")));
hr = GetInterface( static_cast<IKsPin*>( this ), ppv);
if (FAILED(hr)) {
DbgLog((LOG_ERROR, 0, TEXT("GetInterface(IKsPin*) failed, hr = 0x%x"), hr));
}
}
else if (riid == IID_IKsPropertySet)
{
DbgLog((LOG_TRACE, 4, TEXT("CVBIVideoPort::NonDelegatingQueryInterface(IID_IKsPropertySet)")));
hr = GetInterface( static_cast<IKsPropertySet*>( this ), ppv);
if (FAILED(hr)) {
DbgLog((LOG_ERROR, 0, TEXT("GetInterface(IKsPropertySet*) failed, hr = 0x%x"), hr));
}
}
else
{
DbgLog((LOG_TRACE, 4, TEXT("CVBIVideoPort::NonDelegatingQueryInterface(Other)")));
hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);
if (FAILED(hr)) {
DbgLog((LOG_ERROR, 0, TEXT("CUnknown::NonDelegatingQueryInterface failed, hr = 0x%x"), hr));
}
}
return hr;
}
//==========================================================================
// sets the pointer to directdraw
STDMETHODIMP CVBIVideoPort::SetDirectDraw(LPDIRECTDRAW7 pDirectDraw)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::SetDirectDraw")));
HRESULT hr = NOERROR;
m_pDirectDraw = pDirectDraw;
return hr;
}
//==========================================================================
// sets the pointer to the lock, which would be used to synchronize calls to the object
STDMETHODIMP CVBIVideoPort::SetObjectLock(CCritSec* pMainObjLock)
{
AMTRACE((TEXT("CVBIVideoPort::SetObjectLock")));
HRESULT hr = NOERROR;
if (!pMainObjLock)
{
DbgLog((LOG_ERROR, 0, TEXT("pMainObjLock is NULL")));
hr = E_INVALIDARG;
goto CleanUp;
}
m_pMainObjLock = pMainObjLock;
CleanUp:
return hr;
}
//==========================================================================
// check that the mediatype is acceptable
STDMETHODIMP CVBIVideoPort::CheckMediaType(const CMediaType* pmt)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::CheckMediaType")));
HRESULT hr = NOERROR;
if ((pmt->majortype != MEDIATYPE_Video) || (pmt->subtype != MEDIASUBTYPE_VPVBI))
{
hr = VFW_E_TYPE_NOT_ACCEPTED;
goto CleanUp;
}
CleanUp:
return hr;
}
//==========================================================================
HRESULT CVBIVideoPort::GetMediaType(int iPosition, CMediaType* pmt)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::GetMediaType")));
HRESULT hr = S_OK;
if (iPosition == 0)
{
pmt->SetType(&MEDIATYPE_Video);
pmt->SetSubtype(&MEDIASUBTYPE_VPVBI);
pmt->SetFormatType(&FORMAT_None);
pmt->SetSampleSize(1);
pmt->SetTemporalCompression(FALSE);
}
else if (iPosition > 0) {
hr = VFW_S_NO_MORE_ITEMS;
} else { // iPosition < 0
hr = E_INVALIDARG;
}
return hr;
}
//==========================================================================
//
STDMETHODIMP CVBIVideoPort::CheckConnect(IPin* pReceivePin)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::CheckConnect")));
HRESULT hr = NOERROR;
PKSMULTIPLE_ITEM pMediumList = NULL;
IKsPin* pIKsPin = NULL;
PKSPIN_MEDIUM pMedium = NULL;
hr = pReceivePin->QueryInterface(IID_IKsPin, (void** )&pIKsPin);
if (FAILED(hr))
goto CleanUp;
ASSERT(pIKsPin);
hr = pIKsPin->KsQueryMediums(&pMediumList);
if (FAILED(hr))
goto CleanUp;
ASSERT(pMediumList);
pMedium = (KSPIN_MEDIUM* )(pMediumList+1);
SetKsMedium((const KSPIN_MEDIUM* )pMedium);
CleanUp:
RELEASE (pIKsPin);
if (pMediumList)
{
CoTaskMemFree((void*)pMediumList);
pMediumList = NULL;
}
return hr;
}
//==========================================================================
// supposed to be called when the host connects with the decoder
STDMETHODIMP CVBIVideoPort::CompleteConnect(IPin* pReceivePin)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::CompleteConnect")));
ASSERT(!m_bConnected);
HRESULT hr = NOERROR;
// re-initialize variables
m_dwPixelsPerSecond = 0;
ZeroStruct( m_vpConnectInfo );
ZeroStruct( m_capVPDataInfo );
if (!m_pDirectDraw)
{
DbgLog((LOG_ERROR, 0, TEXT("m_pDirectDraw is NULL")));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
ASSERT(m_pIVPConfig == NULL);
RELEASE( m_pIVPConfig );
hr = pReceivePin->QueryInterface(IID_IVPVBIConfig, (void** )&m_pIVPConfig);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0, TEXT("QueryInterface(IID_IVPVBIConfig) failed, hr = 0x%x"), hr));
hr = VFW_E_NO_TRANSPORT;
goto CleanUp;
}
ASSERT(m_pIVPConfig);
// create the VP container
ASSERT(m_pDDVPContainer == NULL);
hr = m_pDirectDraw->QueryInterface( IID_IDDVideoPortContainer, (LPVOID* )&m_pDDVPContainer);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0, TEXT("m_pDirectDraw->QueryInterface(IID_IDDVideoPortContainer) failed, hr = 0x%x"), hr));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
// negotiate the connection parameters
// get/set connection info happens here
hr = NegotiateConnectionParameters();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("NegotiateConnectionParameters failed, hr = 0x%x"), hr));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
// get the decoder data parameters
hr = GetDecoderVPDataInfo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("GetDecoderVPDataInfo failed, hr = 0x%x"), hr));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
hr = SetupVideoPort();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("SetupVideoPort failed, hr = 0x%x"), hr));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
// Success!
m_bConnected = TRUE;
CleanUp:
if (FAILED(hr))
BreakConnect();
return hr;
}
//==========================================================================
STDMETHODIMP CVBIVideoPort::BreakConnect()
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::BreakConnect")));
ASSERT(!m_bFilterRunning);
HRESULT hr = NOERROR;
if( m_bConnected ) {
if (m_VPState == VP_STATE_RUNNING)
{
DbgLog((LOG_ERROR, 0, TEXT("BreakConnect called while videoport running")));
StopVideo();
}
if (m_VPState == VP_STATE_STOPPED)
{
hr = TearDownVideoPort();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("TearDownVideoPort failed, hr = 0x%x"), hr));
return hr;
}
}
// release the videoport container
RELEASE (m_pDDVPContainer);
// release the IVPConfig interface
RELEASE (m_pIVPConfig);
// delete the output Video pixelformat
ZeroStruct( m_ddVPOutputVideoFormat);
// delete the input Video pixelformat
ZeroStruct( m_ddVPInputVideoFormat);
m_bConnected = FALSE;
}
return hr;
}
//==========================================================================
// transition from Stop to Pause.
// We do not need to to anything
STDMETHODIMP CVBIVideoPort::Active()
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::Active")));
ASSERT(m_bConnected);
ASSERT(!m_bFilterRunning);
ASSERT(m_VPState != VP_STATE_RUNNING);
HRESULT hr = NOERROR;
if (!m_bConnected)
{
hr = VFW_E_NOT_CONNECTED;
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::Active - not connected")));
goto CleanUp;
}
CleanUp:
return hr;
}
//==========================================================================
// transition (from Pause or Run) to Stop
STDMETHODIMP CVBIVideoPort::Inactive()
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::Inactive")));
ASSERT(m_bConnected);
HRESULT hr = NOERROR;
if (!m_bConnected)
{
hr = VFW_E_NOT_CONNECTED;
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::Inactive - not connected")));
goto CleanUp;
}
// Inactive is also called when going from pause to stop, in which case the
// VideoPort would have already been stopped in the function RunToPause.
// Also, we may have been temporarily disconnected from the videoport by
// a full screen DOS box or a DirectX game, in which case m_VPState would be
// VP_STATE_RETRYING
if (m_VPState == VP_STATE_RUNNING)
{
ASSERT(m_bFilterRunning);
// stop the VideoPort
hr = StopVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("StopVideo failed, hr = 0x%x"), hr));
goto CleanUp;
}
}
m_bFilterRunning = FALSE;
CleanUp:
return hr;
}
//==========================================================================
// transition from Pause to Run. We just start the VideoPort.
STDMETHODIMP CVBIVideoPort::Run(REFERENCE_TIME tStart)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::Run")));
UNREFERENCED_PARAMETER(tStart);
ASSERT(m_bConnected);
ASSERT(!m_bFilterRunning);
ASSERT(m_VPState != VP_STATE_RUNNING);
HRESULT hr = NOERROR;
if (!m_bConnected)
{
hr = VFW_E_NOT_CONNECTED;
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::Run - not connected")));
goto CleanUp;
}
if (m_VPState == VP_STATE_NO_VP)
{
hr = SetupVideoPort();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::Run - SetupVideoPort failed, hr = 0x%x"), hr));
goto CleanUp;
}
}
hr = StartVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("StartVideo failed, hr = 0x%x"), hr));
ASSERT( SUCCEEDED(hr));
goto CleanUp;
}
m_bFilterRunning = TRUE;
CleanUp:
return hr;
}
//==========================================================================
// transition from Run to Pause. We just stop the VideoPort
// Note that transition from Run to Stop is caught by Inactive
STDMETHODIMP CVBIVideoPort::RunToPause()
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::RunToPause")));
ASSERT(m_bConnected);
ASSERT(m_bFilterRunning);
HRESULT hr = NOERROR;
if (!m_bConnected)
{
hr = VFW_E_NOT_CONNECTED;
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::RunToPause - not connected")));
goto CleanUp;
}
// We may have been temporarily disconnected from the videoport by
// a full screen DOS box or a DirectX game, in which case m_VPState would be
// VP_STATE_RETRYING
if (m_VPState == VP_STATE_RUNNING)
{
// stop the VideoPort
hr = StopVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("StopVideo failed, hr = 0x%x"), hr));
goto CleanUp;
}
}
m_bFilterRunning = FALSE;
CleanUp:
return hr;
}
//==========================================================================
STDMETHODIMP CVBIVideoPort::GetVPDataInfo(AMVPDATAINFO* pAMVPDataInfo)
{
CAutoLock cObjectLock(m_pMainObjLock);
AMTRACE((TEXT("CVBIVideoPort::GetVPDataInfo")));
HRESULT hr = NOERROR;
if (!m_bConnected)
{
hr = VFW_E_NOT_CONNECTED;
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::GetVPDataInfo - not connected")));
goto CleanUp;
}
if (!pAMVPDataInfo)
{
hr = E_INVALIDARG;
DbgLog((LOG_ERROR, 2, TEXT("pAMVPDataInfo is NULL")));
goto CleanUp;
}
*pAMVPDataInfo = m_capVPDataInfo;
CleanUp:
return hr;
}
//==========================================================================
// this function is used to redo the whole videoport connect process, while the graph
// maybe be running.
STDMETHODIMP CVBIVideoPort::RenegotiateVPParameters()
{
CAutoLock cObjectLock(m_pMainObjLock);
DbgLog((LOG_TRACE, 1, TEXT("Entering CVBIVideoPort::RenegotiateVPParameters")));
ASSERT(m_bConnected);
HRESULT hr = NOERROR;
if (!m_bConnected)
{
hr = VFW_E_NOT_CONNECTED;
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::RenegotiateVPParameters - not connected")));
goto CleanUp;
}
if (m_VPState == VP_STATE_RUNNING)
StopVideo();
if (m_VPState == VP_STATE_STOPPED)
TearDownVideoPort();
hr = SetupVideoPort(); // always want_vp_setup (if connected)
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0, TEXT("SetupVideoPort failed in RenegotiateVPParameters, hr = 0x%x"), hr));
goto CleanUp;
}
if (m_bFilterRunning)
{
hr = StartVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0, TEXT("StartVideo failed in RenegotiateVPParameters, hr = 0x%x"), hr));
goto CleanUp;
}
}
CleanUp:
DbgLog((LOG_TRACE, 1, TEXT("Leaving CVBIVideoPort::RenegotiateVPParameters, hr = 0x%x"), hr));
return hr;
}
//==========================================================================
// IKsPin::Get implementation
STDMETHODIMP CVBIVideoPort::Get(REFGUID guidPropSet, DWORD dwPropID, LPVOID pInstanceData,
DWORD cbInstanceData, LPVOID pPropData, DWORD cbPropData, DWORD* pcbReturned)
{
HRESULT hr = S_OK;
AMTRACE((TEXT("CVBIVideoPort::Get")));
if (guidPropSet != AMPROPSETID_Pin)
{
hr = E_PROP_SET_UNSUPPORTED;
goto CleanUp;
}
if ((pPropData == NULL) && (pcbReturned == NULL))
{
hr = E_POINTER;
goto CleanUp;
}
if (dwPropID == KSPROPERTY_PIN_CATEGORY)
{
if (pcbReturned)
* pcbReturned = sizeof(GUID);
if (pPropData != NULL)
{
if (cbPropData < sizeof(GUID))
{
hr = E_UNEXPECTED;
goto CleanUp;
}
* (GUID* )pPropData = m_CategoryGUID;
}
}
else if (dwPropID == KSPROPERTY_PIN_MEDIUMS)
{
if (pcbReturned)
* pcbReturned = sizeof (KSPIN_MEDIUM);
if (pPropData != NULL)
{
if (cbPropData < sizeof(KSPIN_MEDIUM))
{
hr = E_UNEXPECTED;
goto CleanUp;
}
* (KSPIN_MEDIUM* )pPropData = m_Medium;
}
}
else
hr = E_PROP_ID_UNSUPPORTED;
CleanUp:
return hr;
}
//==========================================================================
//
STDMETHODIMP CVBIVideoPort::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD* pTypeSupport)
{
HRESULT hr = S_OK;
AMTRACE((TEXT("CVBIVideoPort::QuerySupported")));
if (guidPropSet != AMPROPSETID_Pin)
{
hr = E_PROP_SET_UNSUPPORTED;
goto CleanUp;
}
if ((dwPropID != KSPROPERTY_PIN_CATEGORY) && (dwPropID != KSPROPERTY_PIN_MEDIUMS))
{
hr = E_PROP_ID_UNSUPPORTED;
goto CleanUp;
}
if (pTypeSupport)
* pTypeSupport = KSPROPERTY_SUPPORT_GET;
CleanUp:
return hr;
}
//==========================================================================
//
STDMETHODIMP CVBIVideoPort::KsQueryMediums(PKSMULTIPLE_ITEM* pMediumList)
{
// The following special return code notifies the proxy that this pin is
// not available as a kernel mode connection
HRESULT hr = S_FALSE;
AMTRACE((TEXT("CVBIVideoPort::KsQueryMediums")));
* pMediumList = reinterpret_cast<PKSMULTIPLE_ITEM>(CoTaskMemAlloc(sizeof(**pMediumList)));
if (!*pMediumList)
{
hr = E_OUTOFMEMORY;
goto CleanUp;
}
(*pMediumList)->Count = 0;
(*pMediumList)->Size = sizeof(**pMediumList);
CleanUp:
return hr;
}
//==========================================================================
//
STDMETHODIMP CVBIVideoPort::KsQueryInterfaces(PKSMULTIPLE_ITEM* pInterfaceList)
{
PKSPIN_INTERFACE pInterface;
HRESULT hr = NOERROR;
AMTRACE((TEXT("CVBIVideoPort::KsQueryInterfaces")));
* pInterfaceList = reinterpret_cast<PKSMULTIPLE_ITEM>(CoTaskMemAlloc(sizeof(**pInterfaceList) + sizeof(*pInterface)));
if (!*pInterfaceList)
{
hr = E_OUTOFMEMORY;
goto CleanUp;
}
(*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;
CleanUp:
return hr;
}
//==========================================================================
//
STDMETHODIMP CVBIVideoPort::KsGetCurrentCommunication(KSPIN_COMMUNICATION* pCommunication,
KSPIN_INTERFACE* pInterface, KSPIN_MEDIUM* pMedium)
{
AMTRACE((TEXT("CVBIVideoPort::KsGetCurrentCommunication")));
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 NOERROR;
}
//==========================================================================
// Called every second or two by the thread in CSurfaceWatcher m_SurfaceWatcher,
// this function checks if we have lost our DDraw surface to a full-screen DOS box
// or a DirectX game. If we have (on this call or a previous one or on a call
// to RenegotiateVPParameters), attempt to get it back.
HRESULT CVBIVideoPort::CheckSurfaces()
{
CAutoLock cObjectLock(m_pMainObjLock);
//AMTRACE((TEXT("CVBIVideoPort::CheckSurfaces")));
HRESULT hr = NOERROR;
if (!m_bConnected)
{
//DbgLog((LOG_TRACE, 2, TEXT("CVBIVideoPort::CheckSurfaces - not connected")));
goto CleanUp;
}
// First, see if we think we have surfaces but have really lost them.
if (m_VPState != VP_STATE_NO_VP)
{
//DbgLog((LOG_TRACE, 1, TEXT("CVBIVideoPort::CheckSurfaces - checking surfaces")));
if (m_pOffscreenSurf)
{
hr = m_pOffscreenSurf->IsLost();
if (hr == DDERR_SURFACELOST)
{
DbgLog((LOG_TRACE, 1, TEXT("CVBIVideoPort::CheckSurfaces - Surface Lost!")));
if (m_VPState == VP_STATE_RUNNING)
{
hr = StopVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::CheckSurfaces - StopVideo failed (1), hr = 0x%x"), hr));
goto CleanUp;
}
}
TearDownVideoPort();
}
}
else
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::CheckSurfaces - no surface!")));
if (m_VPState == VP_STATE_RUNNING)
{
hr = StopVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::CheckSurfaces - StopVideo failed (2), hr = 0x%x"), hr));
goto CleanUp;
}
}
TearDownVideoPort();
}
}
// Next, check if our state is what we need. May have been changed above, or on a previous
// call, or on a call to RenegotiateVPParameters.
if (m_VPState == VP_STATE_NO_VP)
{
DbgLog((LOG_TRACE, 1, TEXT("CVBIVideoPort::CheckSurfaces - trying to re-setup videoport")));
hr = SetupVideoPort();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::CheckSurfaces - SetupVideoPort failed, hr = 0x%x"), hr));
goto CleanUp;
}
}
if ((m_VPState == VP_STATE_STOPPED) && m_bFilterRunning)
{
hr = StartVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::CheckSurfaces - StartVideo failed, hr = 0x%x"), hr));
goto CleanUp;
}
}
CleanUp:
return NOERROR;
}
//==========================================================================
// this functions negotiates the connection parameters with
// the decoder.
// Since this function might be called during renegotiation, the
// existing connection parameters are passed in as input and if
// possible, we try to use the same parameters.
HRESULT CVBIVideoPort::GetVideoPortCaps()
{
AMTRACE((TEXT("CVBIVideoPort::GetVideoPortCaps")));
HRESULT hr = NOERROR;
// vpCaps is scratch memory, results stored in this->m_vpCaps
ZeroStruct( m_vpCaps );
// DDVIDEOPORTCAPS vpCaps;
// INITDDSTRUCT( vpCaps );
hr = VPMUtil::FindVideoPortCaps( m_pDDVPContainer, &m_vpCaps, m_dwVideoPortId );
if (FAILED(hr) || S_FALSE == hr )
{
DbgLog((LOG_ERROR,0, TEXT("m_pDDVPContainer->EnumVideoPorts failed, hr = 0x%x"), hr));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
CleanUp:
return hr;
}
//==========================================================================
// This functions negotiates the connection parameters with the decoder.
HRESULT CVBIVideoPort::NegotiateConnectionParameters()
{
HRESULT hr = NOERROR;
LPDDVIDEOPORTCONNECT lpddCaptureConnect = NULL;
DWORD dwNumCaptureEntries = 0;
LPDDVIDEOPORTCONNECT lpddVideoPortConnect = NULL;
DWORD dwNumVideoPortEntries = 0;
BOOL bIntersectionFound = FALSE;
DWORD i, j;
AMTRACE((TEXT("CVBIVideoPort::NegotiateConnectionParameters")));
ASSERT(m_pIVPConfig);
ASSERT(m_pDDVPContainer);
// find the number of entries to be proposed by the decoder
hr = m_pIVPConfig->GetConnectInfo(&dwNumCaptureEntries, NULL);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pIVPConfig->GetConnectInfo failed, hr = 0x%x"), hr));
goto CleanUp;
}
ASSERT(dwNumCaptureEntries);
// allocate the necessary memory
lpddCaptureConnect = (LPDDVIDEOPORTCONNECT) new BYTE [dwNumCaptureEntries*sizeof(DDVIDEOPORTCONNECT)];
if (lpddCaptureConnect == NULL)
{
DbgLog((LOG_ERROR,0,TEXT("NegotiateConnectionParameters : Out of Memory")));
hr = E_OUTOFMEMORY;
goto CleanUp;
}
// memset the allocated memory to zero
memset(lpddCaptureConnect, 0, dwNumCaptureEntries*sizeof(DDVIDEOPORTCONNECT));
// set the right size in each of the structs.
for (i = 0; i < dwNumCaptureEntries; i++)
{
lpddCaptureConnect[i].dwSize = sizeof(DDVIDEOPORTCONNECT);
}
// get the entries proposed by the decoder
hr = m_pIVPConfig->GetConnectInfo(&dwNumCaptureEntries, lpddCaptureConnect);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pIVPConfig->GetConnectInfo failed, hr = 0x%x"), hr));
goto CleanUp;
}
// find the number of entries supported by the videoport
hr = m_pDDVPContainer->GetVideoPortConnectInfo(m_dwVideoPortId, &dwNumVideoPortEntries, NULL);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pDDVPContainer->GetVideoPortConnectInfo failed, hr = 0x%x"), hr));
goto CleanUp;
}
ASSERT(dwNumVideoPortEntries);
// allocate the necessary memory
lpddVideoPortConnect = (LPDDVIDEOPORTCONNECT) new BYTE[dwNumVideoPortEntries*sizeof(DDVIDEOPORTCONNECT)];
if (lpddVideoPortConnect == NULL)
{
DbgLog((LOG_ERROR,0,TEXT("NegotiateConnectionParameters : Out of Memory")));
hr = E_OUTOFMEMORY;
goto CleanUp;
}
// memset the allocated memory to zero
memset(lpddVideoPortConnect, 0, dwNumVideoPortEntries*sizeof(DDVIDEOPORTCONNECT));
// set the right size in each of the structs.
for (i = 0; i < dwNumVideoPortEntries; i++)
{
lpddVideoPortConnect[i].dwSize = sizeof(DDVIDEOPORTCONNECT);
}
// get the entries supported by the videoport
hr = m_pDDVPContainer->GetVideoPortConnectInfo(0, &dwNumVideoPortEntries, lpddVideoPortConnect);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pDDVPContainer->GetVideoPortConnectInfo failed, hr = 0x%x"), hr));
hr = E_FAIL;
goto CleanUp;
}
#ifdef DEBUG
for (i = 0; i < dwNumCaptureEntries; i++)
DbgLog((LOG_TRACE, 3, TEXT("lpddCaptureConnect[%d].dwFlags = 0x%x"), i, lpddCaptureConnect[i].dwFlags));
for (j = 0; j < dwNumVideoPortEntries; j++)
DbgLog((LOG_TRACE,3,TEXT("lpddVideoPortConnect[%d].dwFlags = 0x%x"), j, lpddVideoPortConnect[j].dwFlags));
#endif
// take the first element of the intersection of the two lists and
// set that value on the decoder
for (i = 0; i < dwNumCaptureEntries && !bIntersectionFound; i++)
{
for (j = 0; j < dwNumVideoPortEntries && !bIntersectionFound; j++)
{
if (lpddCaptureConnect[i].dwPortWidth == lpddVideoPortConnect[j].dwPortWidth &&
IsEqualIID(lpddCaptureConnect[i].guidTypeID, lpddVideoPortConnect[j].guidTypeID))
{
// make sure we save the right one (the one from the video port, not the one
// from the capture driver)
memcpy(&m_vpConnectInfo, (lpddVideoPortConnect+j), sizeof(DDVIDEOPORTCONNECT));
hr = m_pIVPConfig->SetConnectInfo(i);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pIVPConfig->SetConnectInfo failed, hr = 0x%x"), hr));
goto CleanUp;
}
bIntersectionFound = TRUE;
}
}
}
if (!bIntersectionFound)
{
hr = E_FAIL;
goto CleanUp;
}
// cleanup
CleanUp:
delete [] lpddCaptureConnect;
delete [] lpddVideoPortConnect;
return hr;
}
//==========================================================================
// This functions gets various data parameters from the decoder
// parameters include dimensions, double-clock, vact etc.
// Also maximum pixel rate the decoder will output.
// This happens after the connnection parameters have been set-up
HRESULT CVBIVideoPort::GetDecoderVPDataInfo()
{
HRESULT hr = NOERROR;
DWORD dwMaxPixelsPerSecond = 0;
AMVPSIZE amvpSize;
AMTRACE((TEXT("CVBIVideoPort::GetDecoderVPDataInfo")));
// set the size of the struct
m_capVPDataInfo.dwSize = sizeof(AMVPDATAINFO);
// get the VideoPort data information
hr = m_pIVPConfig->GetVPDataInfo(&m_capVPDataInfo);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pIVPConfig->GetVPDataInfo failed, hr = 0x%x"), hr));
goto CleanUp;
}
amvpSize.dwWidth = m_capVPDataInfo.amvpDimInfo.dwVBIWidth;
amvpSize.dwHeight = m_capVPDataInfo.amvpDimInfo.dwVBIHeight;
// get the maximum pixel rate the decoder will output
hr = m_pIVPConfig->GetMaxPixelRate(&amvpSize, &dwMaxPixelsPerSecond);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("m_pIVPConfig->GetMaxPixelRate failed, hr = 0x%x"), hr));
goto CleanUp;
}
m_dwPixelsPerSecond = dwMaxPixelsPerSecond;
CleanUp:
return hr;
}
//==========================================================================
// Calls DDRAW to actually create the video port.
HRESULT CVBIVideoPort::CreateVideoPort()
{
HRESULT hr = NOERROR;
DDVIDEOPORTDESC svpDesc;
AMTRACE((TEXT("CVBIVideoPort::CreateVideoPort")));
INITDDSTRUCT( svpDesc );
// fill up the fields of the description struct
svpDesc.dwVBIWidth = m_capVPDataInfo.amvpDimInfo.dwVBIWidth;
svpDesc.dwFieldHeight = m_capVPDataInfo.amvpDimInfo.dwFieldHeight;
svpDesc.dwFieldWidth = m_capVPDataInfo.amvpDimInfo.dwFieldWidth;
svpDesc.dwMicrosecondsPerField = m_capVPDataInfo.dwMicrosecondsPerField;
svpDesc.dwMaxPixelsPerSecond = m_dwPixelsPerSecond;
svpDesc.dwVideoPortID = m_dwVideoPortId;
//DAG_TODO: need to use QueryVideoPortStatus
svpDesc.VideoPortType.dwSize = sizeof(DDVIDEOPORTCONNECT);
svpDesc.VideoPortType.dwPortWidth = m_vpConnectInfo.dwPortWidth;
memcpy(&(svpDesc.VideoPortType.guidTypeID), &(m_vpConnectInfo.guidTypeID), sizeof(GUID));
svpDesc.VideoPortType.dwFlags = 0;
// if the decoder can send double clocked data and the videoport
// supports it, then set that property. This field is only valid
// with an external signal.
if (m_capVPDataInfo.bEnableDoubleClock &&
m_vpConnectInfo.dwFlags & DDVPCONNECT_DOUBLECLOCK)
{
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_DOUBLECLOCK;
}
// if the decoder can give an external activation signal and the
// videoport supports it, then set that property. This field is
// only valid with an external signal.
if (m_capVPDataInfo.bEnableVACT &&
m_vpConnectInfo.dwFlags & DDVPCONNECT_VACT)
{
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_VACT;
}
// if the decoder can send interlaced data and the videoport
// supports it, then set that property.
// !!!SJF_TODO - should we fail if the decoder can't send interlaced data?
if (m_capVPDataInfo.bDataIsInterlaced)
{
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_INTERLACED;
}
if (m_bHalfLineFix)
{
//!!!SJF_TODO - flip polarity back to normal on decoder?
ASSERT(!m_capVPDataInfo.bFieldPolarityInverted);
//!!!SJF_TODO - fail if videoport doesn't handle inverted polarity?
ASSERT(m_vpConnectInfo.dwFlags & DDVPCONNECT_INVERTPOLARITY);
DbgLog((LOG_TRACE, 3, TEXT("INVERTPOLARITY & HALFLINE")));
svpDesc.VideoPortType.dwFlags |=
(DDVPCONNECT_INVERTPOLARITY | DDVPCONNECT_HALFLINE);
}
#if 0 // def DEBUG
DbgLog((LOG_TRACE, 3, TEXT("CreateVideoPort - DDVIDEOPORTDESC")));
DbgLog((LOG_TRACE, 3, TEXT("dwSize: %d"),svpDesc.dwSize));
DbgLog((LOG_TRACE, 3, TEXT("dwFieldWidth: %d"),svpDesc.dwFieldWidth));
DbgLog((LOG_TRACE, 3, TEXT("dwVBIWidth: %d"),svpDesc.dwVBIWidth));
DbgLog((LOG_TRACE, 3, TEXT("dwFieldHeight: %d"),svpDesc.dwFieldHeight));
DbgLog((LOG_TRACE, 3, TEXT("dwMicroseconds: %d"),svpDesc.dwMicrosecondsPerField));
DbgLog((LOG_TRACE, 3, TEXT("dwMaxPixels: %d"),svpDesc.dwMaxPixelsPerSecond));
DbgLog((LOG_TRACE, 3, TEXT("dwVideoPortID: %d"),svpDesc.dwVideoPortID));
DbgLog((LOG_TRACE, 3, TEXT("dwReserved1: %d"),svpDesc.dwReserved1));
DbgLog((LOG_TRACE, 3, TEXT("dwReserved2: %d"),svpDesc.dwReserved2));
DbgLog((LOG_TRACE, 3, TEXT("dwReserved3: %d"),svpDesc.dwReserved3));
DbgLog((LOG_TRACE, 3, TEXT("DDVIDEOPORTCONNECT")));
DbgLog((LOG_TRACE, 3, TEXT("dwSize: %d"),svpDesc.VideoPortType.dwSize));
DbgLog((LOG_TRACE, 3, TEXT("dwPortWidth: %d"),svpDesc.VideoPortType.dwPortWidth));
DbgLog((LOG_TRACE, 3, TEXT("dwFlags: 0x%x"),svpDesc.VideoPortType.dwFlags));
DbgLog((LOG_TRACE, 3, TEXT("GUID: 0x%x"),*((DWORD* )&svpDesc.VideoPortType.guidTypeID)));
DbgLog((LOG_TRACE, 3, TEXT("dwReserved1: %d"),svpDesc.VideoPortType.dwReserved1));
#endif // DEBUG
// create the videoport. The first parameter is dwFlags, reserved for
// future use by ddraw. The last parameter is pUnkOuter, again must be
// NULL.
hr = m_pDDVPContainer->CreateVideoPort(DDVPCREATE_VBIONLY, &svpDesc, &m_pVideoPort, NULL );
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("Unable to create the video port, hr = 0x%x"), hr));
goto CleanUp;
}
CleanUp:
return hr;
}
//==========================================================================
// this function is used to allocate an offscreen surface to attach to the
// videoport.
// The allocation order it tries is just in decreasing amount of memory
// required.
// (3 buffers, single height)
// (2 buffers, single height)
// (1 buffer , single height).
HRESULT CVBIVideoPort::CreateVPSurface(void)
{
DWORD dwMaxBuffers;
HRESULT hr = NOERROR;
DWORD dwCurHeight = 0, dwCurBuffers = 0;
AMTRACE((TEXT("CVBIVideoPort::CreateVPSurface")));
ASSERT(m_pDirectDraw);
// we will try to allocate up to 3 buffers (unless the
// hardware can handle less than 3)
dwMaxBuffers = 3;
if (m_vpCaps.dwNumVBIAutoFlipSurfaces < dwMaxBuffers)
dwMaxBuffers = m_vpCaps.dwNumVBIAutoFlipSurfaces;
// initialize the fields of ddsdDesc
DDSURFACEDESC2 ddsdDesc;
INITDDSTRUCT( ddsdDesc );
ddsdDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
ddsdDesc.ddpfPixelFormat = m_ddVPOutputVideoFormat;
ddsdDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_VIDEOPORT;
// if we're bob interleaving, the VBI surface seems to need to be doubled too, so always
// double it (the VBI surface memory is relatively small anyways)
ddsdDesc.dwHeight = m_dwSurfaceHeight * 2;
ddsdDesc.dwWidth = m_dwSurfacePitch;
DbgLog((LOG_TRACE, 3, TEXT("Surface height %d, width %d, max buffers %d"),
ddsdDesc.dwHeight, ddsdDesc.dwWidth, dwMaxBuffers));
// we will only try to allocate more than one buffer if the videoport
// is cabable of autoflipping
if ((m_vpCaps.dwFlags & DDVPD_CAPS) && (m_vpCaps.dwCaps & DDVPCAPS_AUTOFLIP) && dwMaxBuffers > 1)
{
ddsdDesc.dwFlags |= DDSD_BACKBUFFERCOUNT;
ddsdDesc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
for (dwCurBuffers = dwMaxBuffers; !m_pOffscreenSurf && dwCurBuffers >= 2; dwCurBuffers--)
{
ddsdDesc.dwBackBufferCount = dwCurBuffers-1;
hr = m_pDirectDraw->CreateSurface(&ddsdDesc, &m_pOffscreenSurf, NULL);
if (SUCCEEDED(hr))
{
hr = m_pOffscreenSurf->QueryInterface( IID_IDirectDrawSurface, (VOID **)&m_pOffscreenSurf1 );
if( SUCCEEDED( hr )) {
DbgLog((LOG_TRACE, 3, TEXT("allocated %d backbuffers"), ddsdDesc.dwBackBufferCount));
goto CleanUp;
} else {
// should never fail, but just in case try again
ASSERT( !"VBI Surface doesn't support DDraw1" );
RELEASE( m_pOffscreenSurf );
}
}
else
{
DbgLog((LOG_ERROR, 0, TEXT("failed to allocate %d backbuffers, hr = 0x%x"),
ddsdDesc.dwBackBufferCount, hr));
}
}
}
// we should only reach this point when attempt to allocate multiple
// buffers failed or no autoflip available
DbgLog((LOG_ERROR, 0, TEXT("Warning: unable to allocate backbuffers")));
ddsdDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT;
ddsdDesc.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP);
m_svpInfo.dwVPFlags &= ~DDVP_AUTOFLIP;
hr = m_pDirectDraw->CreateSurface(&ddsdDesc, &m_pOffscreenSurf, NULL);
if (SUCCEEDED(hr))
{
hr = m_pOffscreenSurf->QueryInterface( IID_IDirectDrawSurface, (VOID **)&m_pOffscreenSurf1 );
if( SUCCEEDED( hr )) {
goto CleanUp;
} else {
// should never fail, but just in case try again
ASSERT( !"VBI Surface doesn't support DDraw1" );
RELEASE( m_pOffscreenSurf );
}
}
ASSERT(!m_pOffscreenSurf);
DbgLog((LOG_ERROR,0, TEXT("Unable to create offscreen surface")));
CleanUp:
return hr;
}
//==========================================================================
// this function is used to inform the decoder of the various ddraw kernel handle
// using IVPConfig interface
HRESULT CVBIVideoPort::SetDDrawKernelHandles()
{
HRESULT hr = NOERROR;
IDirectDrawKernel* pDDK = NULL;
IDirectDrawSurfaceKernel* pDDSK = NULL;
ULONG_PTR* rgKernelHandles = NULL;
DWORD dwCount = 0;
ULONG_PTR ddKernelHandle = 0;
AMTRACE((TEXT("CVBIVideoPort::SetDDrawKernelHandles")));
// get the IDirectDrawKernel interface
ASSERT(m_pDirectDraw);
hr = m_pDirectDraw->QueryInterface(IID_IDirectDrawKernel, (LPVOID* )&pDDK);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("QueryInterface for IDirectDrawKernel failed, hr = 0x%x"), hr));
goto CleanUp;
}
// get the kernel handle
ASSERT(pDDK);
hr = pDDK->GetKernelHandle(&ddKernelHandle);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("GetKernelHandle from IDirectDrawKernel failed, hr = 0x%x"), hr));
goto CleanUp;
}
// set the kernel handle to directdraw using IVPConfig
ASSERT(m_pIVPConfig);
ASSERT(ddKernelHandle);
hr = m_pIVPConfig->SetDirectDrawKernelHandle(ddKernelHandle);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("IVPConfig::SetDirectDrawKernelHandle failed, hr = 0x%x"), hr));
goto CleanUp;
}
// set the VidceoPort Id using IVPConfig
ASSERT(m_pIVPConfig);
hr = m_pIVPConfig->SetVideoPortID(m_dwVideoPortId);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("IVPConfig::SetVideoPortID failed, hr = 0x%x"), hr));
goto CleanUp;
}
{
// should not be NULL
ASSERT( m_pOffscreenSurf1 );
KernelHandleArray pArray( m_pOffscreenSurf, hr );
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("GetKernelHandles failed, hr = 0x%x"), hr));
goto CleanUp;
}
// set the kernel handle to the offscreen surface using IVPConfig
ASSERT(m_pIVPConfig);
hr = m_pIVPConfig->SetDDSurfaceKernelHandles( pArray.GetCount(), pArray.GetHandles() );
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("IVPConfig::SetDDSurfaceKernelHandles failed, hr = 0x%x"), hr));
goto CleanUp;
}
// call SetSurfaceParameters interface on IVPConfig
ASSERT(m_pIVPConfig);
DbgLog((LOG_TRACE, 3, TEXT("SetSurfaceParams(%d,%d,%d)"),
m_dwSurfacePitch, m_dwSurfaceOriginX, m_dwSurfaceOriginY));
hr = m_pIVPConfig->SetSurfaceParameters(m_dwSurfacePitch,
m_dwSurfaceOriginX,m_dwSurfaceOriginY);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,TEXT("IVPConfig::SetSurfaceParameters failed, hr = 0x%x"), hr));
goto CleanUp;
}
}
CleanUp:
// release the kernel ddraw handle
RELEASE (pDDK);
return hr;
}
/*****************************Private*Routine******************************\
* CVideoPortObj::NegotiatePixelFormat
*
* this function is used to negotiate the pixelformat with the decoder.
* It asks the decoder fot a list of input formats, intersects that list
* with the one the deocoder supports (while maintaining the order) and
* then calls "GetBestFormat" on that list to get the "best" input and
* output format. After that it calls "SetPixelFormat" on the decoder in
* order to inform the decoder of the decision.
*
*
* History:
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
*
\**************************************************************************/
HRESULT CVBIVideoPort::GetInputPixelFormats( PixelFormatList* pList )
{
AMTRACE((TEXT("CVideoPortObj::NegotiatePixelFormat")));
CAutoLock cObjectLock(m_pMainObjLock);
HRESULT hr = NOERROR;
// find the number of entries to be proposed
DWORD dwNumProposedEntries = 0;
hr = m_pIVPConfig->GetVideoFormats(&dwNumProposedEntries, NULL);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pIVPConfig->GetVideoFormats failed, hr = 0x%x"), hr));
return hr;
}
ASSERT(dwNumProposedEntries);
// find the number of entries supported by the videoport
DWORD dwNumVPInputEntries = 0;
hr = m_pVideoPort->GetInputFormats(&dwNumVPInputEntries, NULL, DDVPFORMAT_VBI);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pVideoPort->GetInputFormats failed, hr = 0x%x"), hr));
return hr;
}
ASSERT(dwNumVPInputEntries);
// allocate the necessary memory
PixelFormatList lpddProposedFormats(dwNumProposedEntries);
if (lpddProposedFormats.GetEntries() == NULL)
{
DbgLog((LOG_ERROR,0,TEXT("NegotiatePixelFormat : Out of Memory")));
hr = E_OUTOFMEMORY;
return hr;
}
// get the entries proposed
hr = m_pIVPConfig->GetVideoFormats(&dwNumProposedEntries, lpddProposedFormats.GetEntries() );
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pIVPConfig->GetVideoFormats failed, hr = 0x%x"), hr));
return hr;
}
// allocate the necessary memory
PixelFormatList lpddVPInputFormats(dwNumVPInputEntries);
if (lpddVPInputFormats.GetEntries() == NULL)
{
DbgLog((LOG_ERROR,0,TEXT("NegotiatePixelFormat : Out of Memory")));
hr = E_OUTOFMEMORY;
return hr;
}
// get the entries supported by the videoport
hr = m_pVideoPort->GetInputFormats(&dwNumVPInputEntries,
lpddVPInputFormats.GetEntries(), DDVPFORMAT_VBI);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pVideoPort->GetInputFormats failed, hr = 0x%x"), hr));
hr = E_FAIL;
return hr;
}
*pList = lpddVPInputFormats.IntersectWith( lpddProposedFormats );
// the number of entries in the intersection is zero!!
// Return failure.
if (pList->GetCount() == 0)
{
hr = E_FAIL;
return hr;
}
// call GetBestFormat with whatever search criterion you want
// DWORD dwBestEntry;
// hr = GetBestFormat(lpddIntersectionFormats.GetCount(),
// lpddIntersectionFormats.GetEntries(), TRUE, &dwBestEntry,
// &m_ddVPOutputVideoFormat);
// if (FAILED(hr))
// {
// DbgLog((LOG_ERROR,0,TEXT("GetBestFormat failed, hr = 0x%x"), hr));
// } else {
// hr = SetVPInputPixelFormat( lpddIntersectionFormats[dwBestEntry] )
// }
return hr;
}
HRESULT
CVBIVideoPort::GetOutputPixelFormats(
const PixelFormatList& ddInputFormats,
PixelFormatList* pddOutputFormats )
{
HRESULT hr = S_OK;
AMTRACE((TEXT("CVideoPortObj::GetOutputFormats")));
CAutoLock cObjectLock(m_pMainObjLock);
for (DWORD i = 0; i < ddInputFormats.GetCount(); i++)
{
// For each input format, figure out the output formats
DDPIXELFORMAT* pInputFormat = const_cast<DDPIXELFORMAT*>(&ddInputFormats[i]);
DWORD dwNumOutputFormats;
hr = m_pVideoPort->GetOutputFormats(pInputFormat,
&dwNumOutputFormats,
NULL, DDVPFORMAT_VBI);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pVideoPort->GetOutputFormats failed, hr = 0x%x"),
hr));
break;
}
ASSERT(dwNumOutputFormats);
// allocate the necessary memory
pddOutputFormats[i].Reset( dwNumOutputFormats );
if (pddOutputFormats[i].GetEntries() == NULL)
{
DbgLog((LOG_ERROR, 0,
TEXT("new failed, failed to allocate memnory for ")
TEXT("lpddOutputFormats in NegotiatePixelFormat")));
hr = E_OUTOFMEMORY;
break;
}
// get the entries supported by the videoport
hr = m_pVideoPort->GetOutputFormats(pInputFormat,
&dwNumOutputFormats,
pddOutputFormats[i].GetEntries(),
DDVPFORMAT_VBI);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pVideoPort->GetOutputFormats failed, hr = 0x%x"),
hr));
break;
}
} // end of outer for loop
return hr;
}
HRESULT CVBIVideoPort::SetInputPixelFormat( DDPIXELFORMAT& ddFormat )
{
HRESULT hr = NOERROR;
// find the number of entries to be proposed
DWORD dwNumProposedEntries = 0;
hr = m_pIVPConfig->GetVideoFormats(&dwNumProposedEntries, NULL);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pIVPConfig->GetVideoFormats failed, hr = 0x%x"), hr));
return hr;
}
ASSERT(dwNumProposedEntries);
PixelFormatList lpddProposedFormats(dwNumProposedEntries);
if (lpddProposedFormats.GetEntries() == NULL)
{
DbgLog((LOG_ERROR,0,TEXT("NegotiatePixelFormat : Out of Memory")));
hr = E_OUTOFMEMORY;
return hr;
}
// get the entries proposed
hr = m_pIVPConfig->GetVideoFormats(&dwNumProposedEntries, lpddProposedFormats.GetEntries() );
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pIVPConfig->GetVideoFormats failed, hr = 0x%x"), hr));
return hr;
}
// set the format the decoder is supposed to be using
for (DWORD i = 0; i < dwNumProposedEntries; i++)
{
if (VPMUtil::EqualPixelFormats(lpddProposedFormats[i], ddFormat ))
{
hr = m_pIVPConfig->SetVideoFormat(i);
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0,
TEXT("m_pIVPConfig->SetVideoFormat failed, hr = 0x%x"),
hr));
return hr;
}
// cache the input format
m_ddVPInputVideoFormat = ddFormat;
break;
}
}
return hr;
}
//==========================================================================
HRESULT CVBIVideoPort::InitializeVideoPortInfo()
{
HRESULT hr = NOERROR;
RECT rcVPCrop;
AMTRACE((TEXT("CVBIVideoPort::InitializeVideoPortInfo")));
m_dwSurfacePitch = m_capVPDataInfo.amvpDimInfo.dwVBIWidth;
m_dwSurfaceHeight = m_capVPDataInfo.amvpDimInfo.dwVBIHeight;
m_dwSurfaceOriginX = m_capVPDataInfo.amvpDimInfo.rcValidRegion.left;
m_dwSurfaceOriginY = m_capVPDataInfo.amvpDimInfo.rcValidRegion.top;
m_bHalfLineFix = FALSE;
// If we ask the videoport to do cropping, the bottom of the cropping
// region MUST touch but not overlap the top of the cropping region
// for video set by OVMIXER due to h/w limitations.
// So, the bottom of our crop region is always dwVBIHeight (or
// possibly dwVBIHeight+1 if certain halfline fixes are in effect,
// see below) even if the capture driver hasn't set ValidRegion to
// include that many lines.
rcVPCrop.top = 0;
rcVPCrop.left = 0;
rcVPCrop.bottom = m_capVPDataInfo.amvpDimInfo.dwVBIHeight;
rcVPCrop.right = m_capVPDataInfo.amvpDimInfo.dwVBIWidth;
// Adjust for half-lines
// Some video decoders send halflines in even or odd field.
// Some video ports capture halflines, some don't.
// See Video Line Numbering using VPE by smac
if (m_vpConnectInfo.dwFlags & DDVPCONNECT_HALFLINE) // e.g. ATI videoport
{
if ((m_capVPDataInfo.lHalfLinesOdd == 0) &&
(m_capVPDataInfo.lHalfLinesEven == 1)) // e.g. Brooktree decoder
{
// ATI All In Wonder (AIW) board
// halfline problem
DbgLog((LOG_TRACE, 3, TEXT("Setting up for AIW h/w")));
m_dwSurfaceHeight++;
rcVPCrop.bottom += 1;
m_bHalfLineFix = TRUE;
}
else if (((m_capVPDataInfo.lHalfLinesOdd == -1) && (m_capVPDataInfo.lHalfLinesEven == 0)) || // e.g. Philips decoder
((m_capVPDataInfo.lHalfLinesOdd == 0) && (m_capVPDataInfo.lHalfLinesEven == -1)) || // e.g. ? decoder
((m_capVPDataInfo.lHalfLinesOdd == 0) && (m_capVPDataInfo.lHalfLinesEven == 0))) // e.g. ? decoder
{
// no halfline problem, do nothing
}
else
{
// YIKES! We have no solution for these cases (if they even exist)!
DbgLog((LOG_ERROR, 0,TEXT("CVBIVideoPort::InitializeVideoPortInfo: unfixable halfline problem!")));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
}
else // videoport that doesn't capture halflines
{
if ((m_capVPDataInfo.lHalfLinesOdd == -1) &&
(m_capVPDataInfo.lHalfLinesEven == 0)) // e.g. Philips decoder
{
// halfline problem
m_dwSurfaceHeight++;
rcVPCrop.top -= 1;
m_bHalfLineFix = TRUE;
}
else if (((m_capVPDataInfo.lHalfLinesOdd == 0) && (m_capVPDataInfo.lHalfLinesEven == 1)) || // e.g. BT829 decoder
((m_capVPDataInfo.lHalfLinesOdd == 1) && (m_capVPDataInfo.lHalfLinesEven == 0)) || // e.g. ? decoder
((m_capVPDataInfo.lHalfLinesOdd == 0) && (m_capVPDataInfo.lHalfLinesEven == 0))) // e.g. ? decoder
{
// no halfline problem, do nothing
}
else
{
// YIKES! We have no solution for these cases (if they even exist)!
DbgLog((LOG_ERROR, 0,TEXT("CVBIVideoPort::InitializeVideoPortInfo: unfixable halfline problem!")));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
}
// Adjust if video discards lines during the VREF period
if (m_vpConnectInfo.dwFlags & DDVPCONNECT_DISCARDSVREFDATA)
{
DbgLog((LOG_TRACE, 3, TEXT("VideoPort discards %d VREF lines"),
m_capVPDataInfo.dwNumLinesInVREF));
ASSERT(m_dwSurfaceOriginY >= m_capVPDataInfo.dwNumLinesInVREF);
m_dwSurfaceOriginY -= m_capVPDataInfo.dwNumLinesInVREF;
m_dwSurfaceHeight -= m_capVPDataInfo.dwNumLinesInVREF;
rcVPCrop.bottom -= m_capVPDataInfo.dwNumLinesInVREF;
}
// initialize the DDVIDEOPORTINFO struct to be passed to pVideoport->StartVideo
INITDDSTRUCT( m_svpInfo );
m_svpInfo.dwVBIHeight = m_dwSurfaceHeight;
// Assume we're going to be able to autoflip
m_svpInfo.dwVPFlags = DDVP_AUTOFLIP;
// pixelformats get filled in in NegotiatePixelFormats
#if 0 // !!!SJF_TODO - ATI says that cropping for VBI is not supported.
// We always set h/w cropping in the Y direction if we can.
// For VBI, we don't need to do cropping in the X direction.
// Can the videoport crop in the Y direction?
if ((m_vpCaps.dwFlags & DDVPD_FX) && (m_vpCaps.dwFX & DDVPFX_CROPY))
{
rcVPCrop.top = m_dwSurfaceOriginY;
m_dwSurfaceHeight -= m_dwSurfaceOriginY;
m_dwSurfaceOriginY = 0;
m_svpInfo.rCrop = rcVPCrop;
m_svpInfo.dwVPFlags |= DDVP_CROP;
DbgLog((LOG_TRACE, 3, TEXT("Cropping left top: (%d,%d)"),
m_svpInfo.rCrop.left, m_svpInfo.rCrop.top));
DbgLog((LOG_TRACE, 3, TEXT("Cropping bottom right: (%d,%d)"),
m_svpInfo.rCrop.right, m_svpInfo.rCrop.bottom));
}
else
{
if (m_bHalfLineFix)
{
DbgLog((LOG_ERROR, 0,TEXT("CVBIVideoPort::InitializeVideoPortInfo: can't crop to fix halfline problem!")));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
}
#endif // 0
if (m_bHalfLineFix)
{
if (!(m_vpConnectInfo.dwFlags & DDVPCONNECT_INVERTPOLARITY))
{
DbgLog((LOG_ERROR, 0, TEXT("CVBIVideoPort::InitializeVideoPortInfo: can't invert polarity to fix halfline problem!")));
hr = VFW_E_VP_NEGOTIATION_FAILED;
goto CleanUp;
}
}
#if 0 // def DEBUG
DbgLog((LOG_TRACE, 3, TEXT("m_dwSurfaceHeight: %d"),m_dwSurfaceHeight));
DbgLog((LOG_TRACE, 3, TEXT("m_dwSurfacePitch: %d"),m_dwSurfacePitch));
DbgLog((LOG_TRACE, 3, TEXT("m_dwSurfaceOriginX: %d"),m_dwSurfaceOriginX));
DbgLog((LOG_TRACE, 3, TEXT("m_dwSurfaceOriginY: %d"),m_dwSurfaceOriginY));
#endif // DEBUG
CleanUp:
return hr;
}
//==========================================================================
//
HRESULT CVBIVideoPort::SetupVideoPort()
{
AMTRACE((TEXT("CVBIVideoPort::SetupVideoPort")));
ASSERT(m_VPState == VP_STATE_NO_VP);
HRESULT hr = NOERROR;
// initialize variables
ZeroStruct( m_svpInfo );
ZeroStruct( m_vpCaps );
// Get the Video Port caps
hr = GetVideoPortCaps();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("GetVideoPortCaps failed, hr = 0x%x"), hr));
goto CleanUp;
}
// initalize the DDVideoPortInfo structure
hr = InitializeVideoPortInfo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("InitializeVideoPortInfo FAILED, hr = 0x%x"), hr));
goto CleanUp;
}
// create the video port
hr = CreateVideoPort();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CreateVideoPort failed, hr = 0x%x"), hr));
goto CleanUp;
}
// negotiate the pixel format
hr = NegotiatePixelFormat();
if ( FAILED( hr ))
{
DbgLog((LOG_ERROR, 0, TEXT("NegotiatePixelFormat Failed, hr = 0x%x"), hr));
goto CleanUp;
}
// Update the m_svpInfo structure which was mostly filled in in
// InitializeVideoPortInfo
ASSERT(VPMUtil::EqualPixelFormats(m_ddVPInputVideoFormat, m_ddVPOutputVideoFormat));
m_svpInfo.lpddpfVBIInputFormat = &m_ddVPInputVideoFormat;
m_svpInfo.lpddpfVBIOutputFormat = &m_ddVPOutputVideoFormat;
// create the offscreen surface
hr = CreateVPSurface();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("CreateVPSurface FAILED, hr = 0x%x"), hr));
hr = VFW_E_OUT_OF_VIDEO_MEMORY;
goto CleanUp;
}
// attach the offscreen surface to the videoport
hr = m_pVideoPort->SetTargetSurface(m_pOffscreenSurf1, DDVPTARGET_VBI);
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("m_pVideoPort->SetTargetSurface failed, hr = 0x%x"), hr));
goto CleanUp;
}
// inform the decoder of the ddraw kernel handle, videoport id and surface kernel
// handle
hr = SetDDrawKernelHandles();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("SetDDrawKernelHandles failed, hr = 0x%x"), hr));
goto CleanUp;
}
m_VPState = VP_STATE_STOPPED;
CleanUp:
if (FAILED(hr))
TearDownVideoPort();
return hr;
}
HRESULT CVBIVideoPort::NegotiatePixelFormat()
{
PixelFormatList ddInputVideoFormats;
HRESULT hr = GetInputPixelFormats( &ddInputVideoFormats );
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0,
TEXT("NegotiatePixelFormat Failed, hr = 0x%x"), hr));
} else {
PixelFormatList* pddOutputVideoFormats = NULL;
if( ddInputVideoFormats.GetCount() ) {
pddOutputVideoFormats = new PixelFormatList[ ddInputVideoFormats.GetCount() ];
if( !pddOutputVideoFormats ) {
hr = E_OUTOFMEMORY;
goto CleanUp;
}
hr = GetOutputPixelFormats( ddInputVideoFormats, pddOutputVideoFormats );
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0,
TEXT("NegotiatePixelFormat Failed, hr = 0x%x"), hr));
goto CleanUp;
}
// for every input format, figure out a table of every possible output format
// Then we can offer a list of possible output formats. When we need one of them, search
// the input lists to locate it (and possibly select the conversion with the lowest bandwidth)
PixelFormatList ddAllOutputVideoFormats = PixelFormatList::Union( pddOutputVideoFormats, ddInputVideoFormats.GetCount() );
if( ddAllOutputVideoFormats.GetCount() > 0 ) {
m_ddVPOutputVideoFormat = ddAllOutputVideoFormats[ m_dwDefaultOutputFormat ];
DWORD dwInput = PixelFormatList::FindListContaining(
m_ddVPOutputVideoFormat, pddOutputVideoFormats, ddInputVideoFormats.GetCount() );
if( dwInput < ddInputVideoFormats.GetCount() ) {
hr = SetInputPixelFormat( ddInputVideoFormats[dwInput] );
} else {
// can't happen
hr = E_FAIL;
goto CleanUp;
}
}
}
}
CleanUp:
return hr;
}
//==========================================================================
//
HRESULT CVBIVideoPort::TearDownVideoPort()
{
AMTRACE((TEXT("CVBIVideoPort::TearDownVideoPort")));
// Release the DirectDraw surface
RELEASE (m_pOffscreenSurf);
RELEASE (m_pOffscreenSurf1);
// release the videoport
RELEASE (m_pVideoPort);
m_VPState = VP_STATE_NO_VP;
return NOERROR;
}
//==========================================================================
//
HRESULT CVBIVideoPort::StartVideo()
{
AMTRACE((TEXT("CVBIVideoPort::StartVideo")));
ASSERT(m_VPState == VP_STATE_STOPPED);
HRESULT hr = NOERROR;
DWORD dwSignalStatus;
hr = m_pVideoPort->StartVideo(&m_svpInfo);
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("StartVideo failed, hr = 0x%x"), hr));
goto CleanUp;
}
m_VPState = VP_STATE_RUNNING;
DbgLog((LOG_TRACE, 2, TEXT("STARTVIDEO DONE!")));
// check if the videoport is receiving a signal.
hr = m_pVideoPort->GetVideoSignalStatus(&dwSignalStatus);
if (hr != E_NOTIMPL)
{
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 0, TEXT("GetVideoSignalStatus() failed, hr = 0x%x"), hr));
goto CleanUp;
}
else if (dwSignalStatus == DDVPSQ_NOSIGNAL)
{
DbgLog((LOG_ERROR, 0, TEXT("GetVideoSignalStatus() returned DDVPSQ_NOSIGNAL, hr = 0x%x"), hr));
//goto CleanUp; // SJF_TODO - ignore error for now
}
}
//m_pVideoPort->WaitForSync(DDVPWAIT_END, 0, 0);
CleanUp:
return hr;
}
HRESULT CVBIVideoPort::StopVideo()
{
AMTRACE((TEXT("CVBIVideoPort::StopVideo")));
ASSERT(m_VPState == VP_STATE_RUNNING);
HRESULT hr = NOERROR;
hr = m_pVideoPort->StopVideo();
if (FAILED(hr))
{
DbgLog((LOG_ERROR,0, TEXT("m_pVideoPort->StopVideo failed, hr = 0x%x"), hr));
//goto CleanUp;
hr = NOERROR;
}
m_VPState = VP_STATE_STOPPED;
//CleanUp:
return hr;
}
/******************************Public*Routine******************************\
* CVideoPortObj::SetVideoPortID
*
*
*
* History:
* Thu 09/09/1999 - GlennE - Added this comment and cleaned up the code
*
\**************************************************************************/
STDMETHODIMP CVBIVideoPort::SetVideoPortID( DWORD dwVideoPortId )
{
AMTRACE((TEXT("CVideoPortObj::SetVideoPortID")));
CAutoLock cObjectLock(m_pMainObjLock);
HRESULT hr = S_OK;
if ( m_dwVideoPortId != dwVideoPortId ) {
// we can't switch ports when running
if( m_VPState != VPInfoState_STOPPED ) {
hr = VFW_E_WRONG_STATE;
} else {
if( m_pDDVPContainer ) {
hr = VPMUtil::FindVideoPortCaps( m_pDDVPContainer, NULL, m_dwVideoPortId );
} else {
hr = VPMUtil::FindVideoPortCaps( m_pDirectDraw, NULL, m_dwVideoPortId );
}
if( hr == S_OK) {
m_dwVideoPortId = dwVideoPortId;
} else if( hr == S_FALSE ) {
return E_INVALIDARG;
}// else fail
}
}
return hr;
}