mirror of https://github.com/tongzx/nt5src
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
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;
|
|
}
|
|
|