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.
3885 lines
122 KiB
3885 lines
122 KiB
// Copyright (c) 1998 - 1999 Microsoft Corporation. All Rights Reserved.
|
|
#include <streams.h>
|
|
#include <ddraw.h>
|
|
#include <VPObj.h>
|
|
#include <VPMUtil.h>
|
|
#include <dvp.h>
|
|
#include <ddkernel.h>
|
|
|
|
// VIDEOINFOHEADER2
|
|
#include <dvdmedia.h>
|
|
|
|
#include <FormatList.h>
|
|
#include <KHandleArray.h>
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj
|
|
*
|
|
* constructor
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
CVideoPortObj::CVideoPortObj( LPUNKNOWN pUnk, HRESULT *phr, IVideoPortControl* pVPControl )
|
|
: CUnknown(NAME("VP Object"), pUnk)
|
|
, m_bConnected( FALSE )
|
|
, m_pIVPConfig( NULL )
|
|
, m_bVPSyncMaster( FALSE )
|
|
, m_pMainObjLock( NULL )
|
|
, m_pIVideoPortControl( pVPControl )
|
|
, m_pddOutputVideoFormats( NULL )
|
|
, m_dwDefaultOutputFormat( 0 )
|
|
, m_dwVideoPortId( 0 )
|
|
, m_pDVP( NULL )
|
|
, m_pVideoPort( NULL )
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::Constructor")));
|
|
InitVariables();
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* ~CVideoPortObj
|
|
*
|
|
* destructor
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
CVideoPortObj::~CVideoPortObj()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::Destructor")));
|
|
|
|
if (m_bConnected)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("Destructor called without calling breakconnect")));
|
|
BreakConnect();
|
|
}
|
|
|
|
m_pIVideoPortControl = NULL;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::NonDelegatingQueryInterface
|
|
*
|
|
* overridden to expose IVPNotify and IVPObject
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::NonDelegatingQueryInterface(REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::NonDelegatingQueryInterface")));
|
|
|
|
if (riid == IID_IVPNotify) {
|
|
hr = GetInterface(static_cast<IVPNotify*>(this), ppv);
|
|
} else if (riid == IID_IVPNotify2) {
|
|
hr = GetInterface(static_cast<IVPNotify2*>(this), ppv);
|
|
} else {
|
|
hr = CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::InitVariables
|
|
*
|
|
* this function only initializes those variables which are supposed to be reset
|
|
* on RecreateVideoport
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
void CVideoPortObj::InitVariables()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::InitVariables")));
|
|
|
|
delete [] m_pddOutputVideoFormats;
|
|
m_pddOutputVideoFormats = NULL;
|
|
|
|
m_ddInputVideoFormats.Reset(0);
|
|
|
|
ZeroStruct( m_rcDest );
|
|
ZeroStruct( m_rcSource );
|
|
|
|
// image dimensions
|
|
m_lImageWidth = 0;
|
|
m_lImageHeight = 0;
|
|
m_lDecoderImageHeight = 0;
|
|
m_lDecoderImageWidth = 0;
|
|
|
|
// Capturing information
|
|
m_fCapturing = FALSE;
|
|
m_fCaptureInterleaved = FALSE;
|
|
m_cxCapture = 0;
|
|
m_cyCapture = 0;
|
|
|
|
// overlay surface related stuff
|
|
m_pOutputSurface = NULL; // DirectDraw overlay surface
|
|
m_pOutputSurface1 = NULL;
|
|
|
|
m_pChain = NULL;
|
|
m_dwBackBufferCount = 0;
|
|
m_dwOutputSurfaceWidth = 0;
|
|
m_dwOutputSurfaceHeight = 0;
|
|
// m_dwOverlayFlags = 0;
|
|
|
|
// vp variables to store flags, current state etc
|
|
m_bStart = FALSE;
|
|
m_VPState = VPInfoState_STOPPED; // current state: running, stopped
|
|
m_CurrentMode = AMVP_MODE_WEAVE;
|
|
// m_StoredMode = m_CurrentMode;
|
|
m_CropState = VPInfoCropState_None;
|
|
m_dwPixelsPerSecond = 0;
|
|
m_bVSInterlaced = FALSE;
|
|
m_fGarbageLine = false;
|
|
m_fHalfHeightVideo = false;
|
|
|
|
// vp data structures
|
|
ASSERT( m_pDVP == NULL );
|
|
RELEASE( m_pDVP );
|
|
|
|
ASSERT( m_pVideoPort == NULL );
|
|
RELEASE( m_pVideoPort );
|
|
|
|
ZeroStruct( m_svpInfo );
|
|
ZeroStruct( m_sBandwidth );
|
|
ZeroStruct( m_vpCaps );
|
|
ZeroStruct( m_ddConnectInfo );
|
|
ZeroStruct( m_VPDataInfo );
|
|
|
|
// All the pixel formats (Video/VBI)
|
|
ZeroStruct( m_ddVPInputVideoFormat );
|
|
ZeroStruct( m_ddVPOutputVideoFormat );
|
|
|
|
// can we support the different modes
|
|
m_bCanWeave = FALSE;
|
|
m_bCanBobInterleaved = FALSE;
|
|
m_bCanBobNonInterleaved = FALSE;
|
|
m_bCanSkipOdd = FALSE;
|
|
m_bCanSkipEven = FALSE;
|
|
m_bCantInterleaveHalfline = FALSE;
|
|
|
|
// decimation parameters
|
|
m_ulDeciStepX = 0;
|
|
m_dwDeciNumX = m_dwDeciDenX = 1000;
|
|
m_ulDeciStepY = 0;
|
|
m_dwDeciNumY = m_dwDeciDenY = 1000;
|
|
m_DecimationModeX = DECIMATE_NONE;
|
|
m_DecimationModeY = DECIMATE_NONE;
|
|
|
|
m_bVPDecimating = FALSE;
|
|
m_bDecimating = FALSE;
|
|
m_lWidth = 0;
|
|
m_lHeight = 0;
|
|
|
|
// variables to store the current aspect ratio
|
|
m_dwPictAspectRatioX = 1;
|
|
m_dwPictAspectRatioY = 1;
|
|
|
|
// misc
|
|
m_CropState = VPInfoCropState_None;
|
|
m_bStoredWinInfoSet = FALSE;
|
|
ZeroStruct( m_StoredWinInfo );
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::GetDirectDrawVideoPort
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Mon 10/16/2000 - NWilt -
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP
|
|
CVideoPortObj::GetDirectDrawVideoPort(LPDIRECTDRAWVIDEOPORT *ppDirectDrawVideoPort)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::GetDirectDrawVideoPort")));
|
|
HRESULT hr = NOERROR;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
if (!ppDirectDrawVideoPort ) {
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("value of ppDirectDrawVideoPort is invalid,")
|
|
TEXT(" ppDirectDrawVideoPort = NULL")));
|
|
return E_INVALIDARG;
|
|
}
|
|
// remove annoying double indirection since we now asserted its not null
|
|
LPDIRECTDRAWVIDEOPORT& pDirectDrawVideoPort = *ppDirectDrawVideoPort;
|
|
if(!m_bConnected)
|
|
{
|
|
// not connected, this function does not make much sense since the
|
|
// surface wouldn't even have been allocated as yet
|
|
DbgLog((LOG_ERROR, 1, TEXT("not connected, exiting")));
|
|
hr = VFW_E_NOT_CONNECTED;
|
|
} else {
|
|
pDirectDrawVideoPort = m_pVideoPort;
|
|
if(! pDirectDrawVideoPort ) {
|
|
hr = E_FAIL;
|
|
} else {
|
|
pDirectDrawVideoPort->AddRef();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::SetObjectLock
|
|
*
|
|
* sets the pointer to the lock, which would be used to synchronize calls
|
|
* to the object. It is the callee's responsiblility to synchronize this call
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::SetObjectLock(CCritSec *pMainObjLock)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::SetObjectLock")));
|
|
|
|
if (!pMainObjLock)
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("pMainObjLock is NULL")));
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else {
|
|
m_pMainObjLock = pMainObjLock;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::SetMediaType
|
|
*
|
|
* check that the mediatype is acceptable
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::SetMediaType(const CMediaType* pmt)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::SetMediaType")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
HRESULT hr = CheckMediaType(pmt);
|
|
|
|
#if defined(DEBUG)
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 1, TEXT("CheckMediaType failed, hr = 0x%x"), hr));
|
|
}
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::CheckMediaType
|
|
*
|
|
* check that the mediatype is acceptable. No lock is taken here.
|
|
* It is the callee's responsibility to maintain integrity!
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::CheckMediaType(const CMediaType* pmt)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::CheckMediaType")));
|
|
|
|
HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
|
|
if ((pmt->majortype == MEDIATYPE_Video) &&
|
|
(pmt->subtype == MEDIASUBTYPE_VPVideo) &&
|
|
(pmt->formattype == FORMAT_None))
|
|
{
|
|
// get the hardware caps
|
|
const DDCAPS* pDirectCaps = m_pIVideoPortControl->GetHardwareCaps();
|
|
if( pDirectCaps ) {
|
|
hr = NOERROR;
|
|
} else {
|
|
// ASSERT( !"Warning: No VPE Support detected on video card" );
|
|
DbgLog((LOG_ERROR, 2,
|
|
TEXT("no VPE support in hardware,")
|
|
TEXT("so not accepting this mediatype")));
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CVideoPortObj::NegotiatePixelFormat()
|
|
{
|
|
HRESULT hr = GetInputPixelFormats( &m_ddInputVideoFormats );
|
|
delete [] m_pddOutputVideoFormats;
|
|
m_pddOutputVideoFormats = NULL;
|
|
if( m_ddInputVideoFormats.GetCount() ) {
|
|
m_pddOutputVideoFormats = new PixelFormatList[ m_ddInputVideoFormats.GetCount() ];
|
|
if( !m_pddOutputVideoFormats ) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
hr = GetOutputPixelFormats( m_ddInputVideoFormats, m_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)
|
|
m_ddAllOutputVideoFormats = PixelFormatList::Union( m_pddOutputVideoFormats, m_ddInputVideoFormats.GetCount() );
|
|
|
|
// for the input pin connection we need a 'default' format
|
|
// We'll use reconnect after we know what we're connected to.
|
|
//
|
|
// Typically the VPE only supports one format so all of this is really
|
|
// overkill ...
|
|
if( m_ddAllOutputVideoFormats.GetCount() > 0 ) {
|
|
m_ddVPOutputVideoFormat = m_ddAllOutputVideoFormats[ m_dwDefaultOutputFormat ];
|
|
|
|
DWORD dwInput = PixelFormatList::FindListContaining(
|
|
m_ddVPOutputVideoFormat, m_pddOutputVideoFormats, m_ddInputVideoFormats.GetCount() );
|
|
if( dwInput < m_ddInputVideoFormats.GetCount() ) {
|
|
hr = SetInputPixelFormat( m_ddInputVideoFormats[dwInput] );
|
|
} else {
|
|
// can't happen
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::RecreateVideoPort
|
|
\**************************************************************************/
|
|
|
|
HRESULT CVideoPortObj::SetupVideoPort()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::SetupVideoPort")));
|
|
HRESULT hr = NOERROR;
|
|
HRESULT hrFailure = VFW_E_VP_NEGOTIATION_FAILED;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
InitVariables();
|
|
|
|
LPDIRECTDRAW7 pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
ASSERT(pDirectDraw);
|
|
|
|
const DDCAPS* pDirectCaps = m_pIVideoPortControl->GetHardwareCaps();
|
|
ASSERT(pDirectCaps);
|
|
|
|
ASSERT(m_pIVPConfig);
|
|
|
|
// create the VP container
|
|
ASSERT(m_pDVP == NULL);
|
|
ASSERT(pDirectDraw);
|
|
|
|
hr = pDirectDraw->QueryInterface(IID_IDDVideoPortContainer, (LPVOID *)&m_pDVP);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("pDirectDraw->QueryInterface(IID_IDDVideoPortContainer)")
|
|
TEXT(" failed, hr = 0x%x"), hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
// Get the Video Port caps
|
|
// DDVIDEOPORTCAPS vpCaps;
|
|
// INITDDSTRUCT(vpCaps);
|
|
hr = VPMUtil::FindVideoPortCaps( m_pDVP, &m_vpCaps, m_dwVideoPortId );
|
|
|
|
if (FAILED(hr) || S_FALSE == hr )
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pDVP->EnumVideoPorts failed, hr = 0x%x"), hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// negotiate the connection parameters
|
|
// get/set connection info happens here
|
|
hr = NegotiateConnectionParamaters();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("NegotiateConnectionParamaters failed, hr = 0x%x"), hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// Determine if we are capturing and if we are what the intended
|
|
// capture image size is, first determine if the video port
|
|
// supports interleaving interlaced fields in memory
|
|
//
|
|
|
|
BOOL fInterleave;
|
|
if (m_vpCaps.dwFX & DDVPFX_INTERLEAVE) {
|
|
fInterleave = TRUE;
|
|
}
|
|
else {
|
|
fInterleave = FALSE;
|
|
}
|
|
|
|
m_pIVideoPortControl->GetCaptureInfo(&m_fCapturing, &m_cxCapture,
|
|
&m_cyCapture, &fInterleave);
|
|
m_fCaptureInterleaved = fInterleave;
|
|
|
|
#if defined(DEBUG)
|
|
if (m_fCapturing) {
|
|
|
|
ASSERT(m_cxCapture > 0);
|
|
ASSERT(m_cyCapture > 0);
|
|
DbgLog((LOG_TRACE, 1,
|
|
TEXT("We are CAPTURING, intended size (%d, %d)"),
|
|
m_cxCapture, m_cyCapture));
|
|
}
|
|
#endif
|
|
|
|
|
|
for (DWORD i = 0; i < 2; i++)
|
|
{
|
|
AMVPSIZE amvpSize;
|
|
DWORD dwNewWidth = 0;
|
|
|
|
ZeroStruct( amvpSize );
|
|
|
|
// get the rest of the data parameters
|
|
hr = GetDataParameters();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("GetDataParameters failed, hr = 0x%x"), hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// create the video port
|
|
hr = CreateVideoPort();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("CreateVideoPort failed, hr = 0x%x"), hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// check if we need to crop at videoport or overlay or neither
|
|
hr = DetermineCroppingRestrictions();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("DetermineCroppingRestrictions FAILED, hr = 0x%x"),
|
|
hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
m_lImageWidth = WIDTH(&m_VPDataInfo.amvpDimInfo.rcValidRegion);
|
|
m_lImageHeight = HEIGHT(&m_VPDataInfo.amvpDimInfo.rcValidRegion);
|
|
|
|
m_lDecoderImageWidth = m_lImageWidth;
|
|
m_lDecoderImageHeight = m_lImageHeight;
|
|
|
|
if (m_fCapturing) {
|
|
|
|
if (m_lImageWidth != m_cxCapture ||
|
|
m_lImageHeight != m_cyCapture) {
|
|
|
|
DbgLog((LOG_TRACE, 1,
|
|
TEXT("Adjust Decoder Image size to CaptureSize")));
|
|
}
|
|
|
|
m_lImageWidth = m_cxCapture;
|
|
m_lImageHeight = m_cyCapture;
|
|
}
|
|
|
|
m_dwPictAspectRatioX = m_VPDataInfo.dwPictAspectRatioX;
|
|
m_dwPictAspectRatioY = m_VPDataInfo.dwPictAspectRatioY;
|
|
|
|
|
|
// negotiate the pixel format
|
|
hr = NegotiatePixelFormat();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("NegotiatePixelFormat Failed, hr = 0x%x"), hr));
|
|
hr = hrFailure;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// check the vp caps
|
|
hr = CheckDDrawVPCaps();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("CheckDDrawVPCaps FAILED, hr = 0x%x"), hr));
|
|
// CheckDDrawVPCaps already returns a "proper" error code
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
|
|
dwNewWidth = m_VPDataInfo.amvpDimInfo.dwFieldWidth;
|
|
if (m_sBandwidth.dwCaps == DDVPBCAPS_SOURCE &&
|
|
m_sBandwidth.dwYInterpAndColorkey < 900)
|
|
{
|
|
dwNewWidth = MulDiv(dwNewWidth,
|
|
m_sBandwidth.dwYInterpAndColorkey, 1000);
|
|
}
|
|
else if (m_sBandwidth.dwCaps == DDVPBCAPS_DESTINATION &&
|
|
m_sBandwidth.dwYInterpAndColorkey > 1100)
|
|
{
|
|
dwNewWidth = MulDiv(dwNewWidth, 1000,
|
|
m_sBandwidth.dwYInterpAndColorkey);
|
|
}
|
|
|
|
// VGA can't handle the bandwidth, ask decoder to down-scale
|
|
if (dwNewWidth != m_VPDataInfo.amvpDimInfo.dwFieldWidth)
|
|
{
|
|
amvpSize.dwWidth = dwNewWidth;
|
|
amvpSize.dwHeight = m_VPDataInfo.amvpDimInfo.dwFieldHeight;
|
|
|
|
DbgLog((LOG_TRACE,1,
|
|
TEXT("SetScalingFactors to (%d, %d)"),
|
|
amvpSize.dwWidth, amvpSize.dwHeight));
|
|
|
|
hr = m_pIVPConfig->SetScalingFactors(&amvpSize);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->SetScalingFactors")
|
|
TEXT("failed, hr = 0x%x"), hr));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// release the videoport
|
|
ASSERT(m_pVideoPort);
|
|
ReleaseVideoPort();
|
|
|
|
// initialize relevant structs
|
|
ZeroStruct( m_sBandwidth );
|
|
ZeroStruct( m_VPDataInfo );
|
|
ZeroStruct( m_ddVPInputVideoFormat );
|
|
ZeroStruct( m_ddVPOutputVideoFormat );
|
|
|
|
// initialize decimation parameters
|
|
m_ulDeciStepX = 0;
|
|
m_dwDeciNumX = m_dwDeciDenX = 1000;
|
|
m_DecimationModeX = DECIMATE_NONE;
|
|
|
|
m_ulDeciStepY = 0;
|
|
m_dwDeciNumY = m_dwDeciDenY = 1000;
|
|
m_DecimationModeY = DECIMATE_NONE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DbgLog((LOG_ERROR,0,TEXT("no need to scale at the decoder")));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// iniitalize the DDVideoPortInfo structure
|
|
hr = InitializeVideoPortInfo();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("InitializeVideoPortInfo FAILED, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
#if 0
|
|
// quickly open & close to make sure that we have a notification
|
|
// The rapid switch doesn't work with most drivers though
|
|
static HRESULT CheckVPNotifiyValid( LPDIRECTDRAWVIDEOPORT pVP )
|
|
{
|
|
HANDLE hevSampleAvailable;
|
|
DDVIDEOPORTNOTIFY vpNotify;
|
|
LPDIRECTDRAWVIDEOPORTNOTIFY pNotify;
|
|
|
|
HRESULT hr = pVP->QueryInterface( IID_IDirectDrawVideoPortNotify, (LPVOID *) &pNotify );
|
|
if( SUCCEEDED( hr )) {
|
|
hr = pNotify->AcquireNotification( &hevSampleAvailable, &vpNotify );
|
|
vpNotify.lDone = 1;
|
|
pNotify->ReleaseNotification( hevSampleAvailable );
|
|
RELEASE( pNotify );
|
|
}
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
HRESULT CVideoPortObj::AttachVideoPortToSurface()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
LPDIRECTDRAW7 pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
ASSERT(pDirectDraw);
|
|
|
|
const DDCAPS* pDirectCaps = m_pIVideoPortControl->GetHardwareCaps();
|
|
ASSERT(pDirectCaps);
|
|
|
|
#ifdef DEBUG
|
|
#define DBGFLAG( f ) DbgLog((LOG_ERROR, 1, TEXT("%s = %s"), TEXT(#f), f ? TEXT("TRUE") : TEXT("FALSE") ))
|
|
|
|
DBGFLAG (m_bVSInterlaced);
|
|
DBGFLAG( m_vpCaps.dwCaps & DDVPCAPS_AUTOFLIP );
|
|
DBGFLAG (m_vpCaps.dwFX & DDVPFX_INTERLEAVE);
|
|
DBGFLAG (m_bCantInterleaveHalfline);
|
|
DBGFLAG (pDirectCaps->dwCaps2 & DDCAPS2_CANBOBINTERLEAVED);
|
|
DBGFLAG (pDirectCaps->dwCaps2 & DDCAPS2_CANBOBNONINTERLEAVED);
|
|
#undef DBGFLAG
|
|
#endif
|
|
|
|
BOOL bCanWeave = FALSE;
|
|
BOOL bCanBobInterleaved = FALSE;
|
|
BOOL bCanBobNonInterleaved = FALSE;
|
|
BOOL bTryDoubleHeight = FALSE, bPreferBuffers = FALSE;
|
|
DWORD dwMaxOverlayBuffers;
|
|
|
|
// can Weave only if content is non-interlaced (cause of motion
|
|
// artifacts otherwise) and if videoport is capable of flipping and
|
|
// supports interleaved data and if certain halfline scenarios do not
|
|
// preclude interleaving
|
|
//
|
|
if ((!m_bVSInterlaced) &&
|
|
(m_vpCaps.dwCaps & DDVPCAPS_AUTOFLIP) &&
|
|
(m_vpCaps.dwFX & DDVPFX_INTERLEAVE) &&
|
|
(!m_bCantInterleaveHalfline))
|
|
{
|
|
bCanWeave = TRUE;
|
|
}
|
|
|
|
// can BobNonInterleaved only if content is interlaced and if videoport is
|
|
// capable of flipping, is capable of bobing interleaved data and supports
|
|
// interleaved data and if certain halfline scenarios do not preclude
|
|
// interleaving
|
|
//
|
|
if ((m_bVSInterlaced) &&
|
|
(m_vpCaps.dwCaps & DDVPCAPS_AUTOFLIP) &&
|
|
(pDirectCaps->dwCaps2 & DDCAPS2_CANBOBINTERLEAVED) &&
|
|
(m_vpCaps.dwFX & DDVPFX_INTERLEAVE) &&
|
|
(!m_bCantInterleaveHalfline))
|
|
{
|
|
bCanBobInterleaved = TRUE;
|
|
}
|
|
|
|
// can BobInterleaved only if content is interlaced and if videoport is
|
|
// capable of flipping and is capable of bobing non-interleaved data.
|
|
//
|
|
if ((m_bVSInterlaced) &&
|
|
(m_vpCaps.dwCaps & DDVPCAPS_AUTOFLIP) &&
|
|
(pDirectCaps->dwCaps2 & DDCAPS2_CANBOBNONINTERLEAVED))
|
|
{
|
|
bCanBobNonInterleaved = TRUE;
|
|
}
|
|
|
|
// this just means that we would perfer higher number of
|
|
// buffers instead of more height in the event of a conflict
|
|
// (in cases like 2buffer, 1height versus 1buffer, 2height)
|
|
//
|
|
bPreferBuffers = TRUE;
|
|
|
|
// we will try to allocate surface of double the field height only if
|
|
// either mode weave or bob-interleaved are possible
|
|
//
|
|
bTryDoubleHeight = bCanWeave || bCanBobInterleaved;
|
|
|
|
// 3 buffers prevents any waiting
|
|
dwMaxOverlayBuffers = 3;
|
|
|
|
// we will try to allocate multiple buffers only if either mode weave or
|
|
// bob-interleaved or bob-non-interleaved are possible
|
|
//
|
|
if (bCanWeave || bCanBobInterleaved || bCanBobNonInterleaved)
|
|
{
|
|
//try to allocate min(m_vpCaps.dwNumAutoFlipSurfaces,
|
|
// m_vpCaps.dwNumPreferredAutoflip) buffers
|
|
//
|
|
ASSERT(m_vpCaps.dwFlags & DDVPD_AUTOFLIP);
|
|
if (m_vpCaps.dwFlags & DDVPD_PREFERREDAUTOFLIP)
|
|
{
|
|
dwMaxOverlayBuffers = min(m_vpCaps.dwNumAutoFlipSurfaces,
|
|
m_vpCaps.dwNumPreferredAutoflip);
|
|
}
|
|
else
|
|
{
|
|
dwMaxOverlayBuffers = min(m_vpCaps.dwNumAutoFlipSurfaces, 3);
|
|
}
|
|
}
|
|
|
|
// create the overlay surface
|
|
hr = CreateSourceSurface(bTryDoubleHeight, dwMaxOverlayBuffers, bPreferBuffers);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("CreateVPOverlay FAILED, hr = 0x%x"), hr));
|
|
hr = VFW_E_OUT_OF_VIDEO_MEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// tell the upstream filter the valid data location on the ddraw surface
|
|
hr = SetSurfaceParameters();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetSurfaceParameters FAILED, hr = 0x%x"), hr));
|
|
hr = VFW_E_OUT_OF_VIDEO_MEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// attach the overlay surface to the videoport
|
|
hr = ReconnectVideoPortToSurface();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pVideoPort->SetTargetSurface failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!(VPMUtil::EqualPixelFormats( m_ddVPInputVideoFormat, m_ddVPOutputVideoFormat)))
|
|
{
|
|
m_svpInfo.dwVPFlags |= DDVP_CONVERT;
|
|
}
|
|
else
|
|
{
|
|
m_svpInfo.dwVPFlags &= ~DDVP_CONVERT;
|
|
}
|
|
|
|
// determine which modes are possible now
|
|
// depends upon the height, number of back buffers etc
|
|
hr = DetermineModeRestrictions();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("DetermineModeRestrictions FAILED, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
//
|
|
// try the various modes, this can be folded into DetermineModeRestrictions
|
|
// instead of mindlessly pounding at cases (put into format negotiation code in VPMOutputPin ?)
|
|
//
|
|
hr = SetUpMode( m_CurrentMode );
|
|
if( FAILED( hr )) {
|
|
// switch modes
|
|
AMVP_MODE modes[5]={AMVP_MODE_WEAVE,
|
|
AMVP_MODE_BOBINTERLEAVED, AMVP_MODE_BOBNONINTERLEAVED,
|
|
AMVP_MODE_SKIPODD, AMVP_MODE_SKIPEVEN
|
|
};
|
|
|
|
for( DWORD dwModeIndex = 0; dwModeIndex < NUMELMS( modes ); dwModeIndex++ ) {
|
|
if( modes[dwModeIndex] != m_CurrentMode ) {
|
|
hr = SetUpMode( modes[dwModeIndex] );
|
|
if( SUCCEEDED(hr )) {
|
|
m_CurrentMode = modes[dwModeIndex];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVideoPortObj::SignalNewVP()
|
|
{
|
|
// finally notify the capture thread of the new surface
|
|
ASSERT( m_pVideoPort );
|
|
ASSERT( m_pChain );
|
|
ASSERT( m_pChain[0].pDDSurf );
|
|
HRESULT hRes = m_pIVideoPortControl->SignalNewVP( m_pVideoPort );
|
|
return hRes;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::CompleteConnect
|
|
*
|
|
* supposed to be called when the host connects with the decoder
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP
|
|
CVideoPortObj::CompleteConnect(IPin *pReceivePin, BOOL bRenegotiating)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::CompleteConnect")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
if (!bRenegotiating)
|
|
{
|
|
InitVariables();
|
|
|
|
ASSERT(m_pIVPConfig == NULL);
|
|
hr = pReceivePin->QueryInterface(IID_IVPConfig, (void **)&m_pIVPConfig);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0, TEXT("QueryInterface(IID_IVPConfig) failed, hr = 0x%x"), hr));
|
|
hr = VFW_E_NO_TRANSPORT;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
ASSERT(m_pIVPConfig);
|
|
|
|
hr = SetupVideoPort();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0, TEXT("SetupVideoPort failed, hr = 0x%x"), hr));
|
|
ASSERT(SUCCEEDED(hr));
|
|
goto CleanUp;
|
|
}
|
|
m_bConnected = TRUE;
|
|
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVideoPortObj::StopUsingVideoPort()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::StopUsingVideoPort")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// release the videoport
|
|
if (m_pVideoPort)
|
|
{
|
|
hr = m_pVideoPort->StopVideo();
|
|
ReleaseVideoPort();
|
|
}
|
|
|
|
// release the videoport container
|
|
RELEASE( m_pDVP );
|
|
|
|
// Release the DirectDraw overlay surface
|
|
// Must release VideoPort first so that the thread doesn't use this
|
|
hr = DestroyOutputSurfaces();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CVideoPortObj::DestroyOutputSurfaces()
|
|
{
|
|
// ref counts on m_pChain match primary m_pOutputSurface
|
|
delete [] m_pChain;
|
|
m_pChain = NULL;
|
|
|
|
RELEASE( m_pOutputSurface1 );
|
|
RELEASE( m_pOutputSurface );
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::BreakConnect
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP
|
|
CVideoPortObj::BreakConnect(BOOL bRenegotiating)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::BreakConnect")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
unsigned long ulCount;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
hr = StopUsingVideoPort();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0, TEXT("StopUsingVideoPort failed, hr = 0x%x"), hr));
|
|
}
|
|
if (!bRenegotiating)
|
|
{
|
|
// release the IVPConfig interface
|
|
RELEASE (m_pIVPConfig);
|
|
}
|
|
|
|
m_bConnected = FALSE;
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::Active()
|
|
*
|
|
*
|
|
* transition from Stop to Pause.
|
|
* We do not need to to anything unless this is the very first time we are
|
|
* showing the overlay
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::Active()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::Active")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
HRESULT hr = NOERROR;
|
|
|
|
ASSERT(m_bConnected);
|
|
ASSERT(m_VPState == VPInfoState_STOPPED);
|
|
|
|
if (!m_bConnected)
|
|
{
|
|
hr = VFW_E_NOT_CONNECTED;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// make sure that a frame is visible by making an update overlay call
|
|
m_bStart = TRUE;
|
|
|
|
// now stop the video, so the user will just see a still frame
|
|
hr = m_pVideoPort->StopVideo();
|
|
|
|
#if defined(DEBUG)
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pVideoPort->StopVideo failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
#endif
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::Inactive()
|
|
*
|
|
* transition (from Pause or Run) to Stop
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::Inactive()
|
|
{
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::Inactive")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
if (m_bConnected) {
|
|
|
|
// Inactive is also called when going from pause to stop, in which case the
|
|
// VideoPort would have already been stopped in the function RunToPause
|
|
|
|
if (m_VPState == VPInfoState_RUNNING) {
|
|
|
|
// stop the VideoPort
|
|
if( m_pVideoPort )
|
|
hr = m_pVideoPort->StopVideo();
|
|
if (SUCCEEDED(hr)) {
|
|
m_VPState = VPInfoState_STOPPED;
|
|
}
|
|
else {
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pVideoPort->StopVideo failed, hr = 0x%x"), hr));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
hr = VFW_E_NOT_CONNECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::Run
|
|
*
|
|
* transition from Pause to Run. We just start the VideoPort.
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::Run(REFERENCE_TIME /* tStart */)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::Run")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
ASSERT(m_bConnected);
|
|
ASSERT(m_VPState == VPInfoState_STOPPED);
|
|
HRESULT hr = S_OK;
|
|
|
|
if (m_bConnected)
|
|
{
|
|
// An UpdateOverlay is needed here. One example is, when we are
|
|
// clipping video in Stop/Pause state since we can't do scaling
|
|
// on the videoport. As soon as the user hits play, we should stop
|
|
// clipping the video.
|
|
|
|
m_bStart = TRUE;
|
|
|
|
m_VPState = VPInfoState_RUNNING;
|
|
// TBD: we need to kick a thread to start pumping frames from the videoport
|
|
// to the output pin
|
|
}
|
|
else {
|
|
hr = VFW_E_NOT_CONNECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::RunToPause()
|
|
*
|
|
* transition from Run to Pause. We just stop the VideoPort
|
|
* Note that transition from Run to Stop is caught by Inactive
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::RunToPause()
|
|
{
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::RunToPause")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
ASSERT(m_bConnected);
|
|
//ASSERT(m_VPState == VPInfoState_RUNNING);
|
|
|
|
HRESULT hr;
|
|
if (m_bConnected)
|
|
{
|
|
// stop the VideoPort
|
|
hr = m_pVideoPort->StopVideo();
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
m_VPState = VPInfoState_STOPPED;
|
|
}
|
|
else {
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pVideoPort->StopVideo failed, hr = 0x%x"), hr));
|
|
}
|
|
|
|
}
|
|
else {
|
|
hr = VFW_E_NOT_CONNECTED;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::CurrentMediaType
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::CurrentMediaType(AM_MEDIA_TYPE *pMediaType)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::CurrentMediaType")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
if (m_bConnected) {
|
|
if (pMediaType) {
|
|
VIDEOINFOHEADER2 *pVideoInfoHeader2 = VPMUtil::GetVideoInfoHeader2( (CMediaType *)pMediaType );
|
|
|
|
// tweak it if it isn't the correct type
|
|
if( !pVideoInfoHeader2 ) {
|
|
pVideoInfoHeader2 = VPMUtil::SetToVideoInfoHeader2( (CMediaType *)pMediaType );
|
|
}
|
|
|
|
if( pVideoInfoHeader2 ) {
|
|
VPMUtil::InitVideoInfoHeader2( pVideoInfoHeader2);
|
|
|
|
pVideoInfoHeader2->bmiHeader.biWidth = m_VPDataInfo.amvpDimInfo.rcValidRegion.right -
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.left;
|
|
pVideoInfoHeader2->bmiHeader.biHeight = m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom -
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top;
|
|
|
|
pVideoInfoHeader2->dwPictAspectRatioX = m_VPDataInfo.dwPictAspectRatioX;
|
|
pVideoInfoHeader2->dwPictAspectRatioY = m_VPDataInfo.dwPictAspectRatioY;
|
|
return S_OK;
|
|
} else {
|
|
DbgLog((LOG_ERROR, 2, TEXT("not videoheader2")));
|
|
return NOERROR;
|
|
}
|
|
} else {
|
|
DbgLog((LOG_ERROR, 2, TEXT("pMediaType is NULL")));
|
|
return E_INVALIDARG;
|
|
}
|
|
} else {
|
|
return VFW_E_NOT_CONNECTED;
|
|
}
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::GetRectangles
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::GetRectangles(RECT *prcSource, RECT *prcDest)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::GetRectangles")));
|
|
|
|
if (prcSource && prcDest) {
|
|
|
|
// adjust the source to be bigger to take into account the decimation
|
|
// that's happening
|
|
//
|
|
prcSource->left = MulDiv(m_rcSource.left, m_dwDeciDenX, m_dwDeciNumX);
|
|
prcSource->right = MulDiv(m_rcSource.right, m_dwDeciDenX, m_dwDeciNumX);
|
|
prcSource->top = MulDiv(m_rcSource.top, m_dwDeciDenY, m_dwDeciNumY);
|
|
prcSource->bottom = MulDiv(m_rcSource.bottom,m_dwDeciDenY, m_dwDeciNumY);
|
|
|
|
*prcDest = m_rcDest;
|
|
}
|
|
else {
|
|
hr = E_INVALIDARG;
|
|
DbgLog((LOG_ERROR, 2, TEXT("prcSource or prcDest is NULL")));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CVideoPortObj::GetCropState(VPInfoCropState *pCropState)
|
|
{
|
|
*pCropState = m_CropState;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetPixelsPerSecond(DWORD* pPixelPerSec)
|
|
{
|
|
*pPixelPerSec = m_dwPixelsPerSecond;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetVPDataInfo(AMVPDATAINFO* pVPDataInfo)
|
|
{
|
|
*pVPDataInfo = m_VPDataInfo;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetVPInfo(DDVIDEOPORTINFO* pVPInfo)
|
|
{
|
|
*pVPInfo = m_svpInfo;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetVPBandwidth(DDVIDEOPORTBANDWIDTH* pVPBandwidth)
|
|
{
|
|
*pVPBandwidth = m_sBandwidth;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetVPCaps(DDVIDEOPORTCAPS* pVPCaps)
|
|
{
|
|
*pVPCaps = m_vpCaps;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetVPInputFormat(LPDDPIXELFORMAT pVPFormat)
|
|
{
|
|
*pVPFormat = m_ddVPInputVideoFormat;
|
|
return NOERROR;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetVPOutputFormat(LPDDPIXELFORMAT pVPFormat)
|
|
{
|
|
*pVPFormat = m_ddVPOutputVideoFormat;
|
|
return NOERROR;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::StartVideo
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::ReconnectVideoPortToSurface()
|
|
{
|
|
// not a high frequency event, so always try to reattach
|
|
// if surfaces were lost for some reason, must be reattached
|
|
HRESULT hResult;
|
|
|
|
// we need a video port, StartWithRetry will create another videoport
|
|
if( !m_pVideoPort ) {
|
|
return E_FAIL;
|
|
}
|
|
if( !m_pOutputSurface || FAILED( hResult = m_pOutputSurface->Restore() )) { // == DDERR_WRONGMODE with Rage128
|
|
// ASSERT( !"VPM: Can't restore surface, recreating" );
|
|
hResult = AttachVideoPortToSurface();
|
|
if( SUCCEEDED( hResult )) {
|
|
hResult = SignalNewVP();
|
|
}
|
|
} else {
|
|
hResult = m_pVideoPort->SetTargetSurface(m_pOutputSurface1, DDVPTARGET_VIDEO);
|
|
|
|
// hack for bug where a running video port caused DDraw to stop it, discards the VPInfo, then tries
|
|
// to start it, but returns E_INVALIDARG since the VPInfo is NULL!!
|
|
if( FAILED( hResult )) {
|
|
hResult = m_pVideoPort->SetTargetSurface(m_pOutputSurface1, DDVPTARGET_VIDEO);
|
|
}
|
|
// ASSERT( SUCCEEDED(hResult)); <- can fail if the videoport was lost during a res mode change (G400)
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
HRESULT CVideoPortObj::StartVideoWithRetry()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
// Can be NULL if we call StartWithRetry twice and the VP failed
|
|
if( m_pVideoPort ) {
|
|
hr = m_pVideoPort->StartVideo(&m_svpInfo);
|
|
}
|
|
|
|
// This case SUCCEEDS on the G400
|
|
|
|
// Try again with ReconnectToSurf first
|
|
if (FAILED(hr))
|
|
{
|
|
// ASSERT( !"StartWithRetry entering salvage mode" );
|
|
hr = ReconnectVideoPortToSurface();
|
|
if( SUCCEEDED( hr )) {
|
|
hr = m_pVideoPort->StartVideo(&m_svpInfo);
|
|
}
|
|
}
|
|
// Try again with CreateVP then ReconnectToSurf (first case FAILs on the Rage128)
|
|
if( FAILED(hr)) {
|
|
// ASSERT( !"Recreating videoport" );
|
|
// try replacing the video port
|
|
hr = CreateVideoPort();
|
|
if( SUCCEEDED( hr )) {
|
|
hr = ReconnectVideoPortToSurface();
|
|
}
|
|
if( SUCCEEDED( hr )) {
|
|
hr = m_pVideoPort->StartVideo(&m_svpInfo);
|
|
}
|
|
}
|
|
#if 0
|
|
// Try again with SetupVP (CreateVP), ReconnectToSurf first
|
|
// This implies the video port enumerator isn't valid any more. I don't think
|
|
// this should happen in practice, but just in case.
|
|
if( FAILED(hr)) {
|
|
ASSERT( !"Rebuilding videoport" );
|
|
// really corrupt, start from the beginning
|
|
hr = SetupVideoPort();
|
|
if( SUCCEEDED( hr )) {
|
|
hr = ReconnectVideoPortToSurface();
|
|
}
|
|
if( SUCCEEDED( hr )) {
|
|
hr = m_pVideoPort->StartVideo(&m_svpInfo);
|
|
}
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::StartVideo(const VPWININFO* pWinInfo )
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::StartVideo")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
if ( m_bStart) {
|
|
VPWININFO CopyWinInfo;
|
|
AMVP_MODE tryMode;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// no point making any videoport calls, if the video is stopped
|
|
if (m_VPState == VPInfoState_RUNNING || m_bStart)
|
|
{
|
|
if (m_bStart)
|
|
{
|
|
DWORD dwSignalStatus;
|
|
hr = StartVideoWithRetry();
|
|
if( FAILED( hr )) {
|
|
hr = StartVideoWithRetry();
|
|
if( FAILED( hr )) {
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
DbgLog((LOG_ERROR,0, TEXT("StartVideo DONE!!!")));
|
|
|
|
// check if the videoport is receiving a signal.
|
|
hr = m_pVideoPort->GetVideoSignalStatus(&dwSignalStatus);
|
|
if ((SUCCEEDED(hr)) && (dwSignalStatus == DDVPSQ_SIGNALOK))
|
|
{
|
|
m_pVideoPort->WaitForSync(DDVPWAIT_END, 0, 0);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pVideoPort->GetVideoSignalStatus() failed,")
|
|
TEXT(" hr = 0x%x"), hr));
|
|
hr = NOERROR;
|
|
}
|
|
|
|
|
|
m_bStart = FALSE;
|
|
}
|
|
}
|
|
}
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
static bool AreEqual( const DDVIDEOPORTCONNECT& proposed, const DDVIDEOPORTCONNECT& videoport )
|
|
{
|
|
return (proposed.dwPortWidth == videoport.dwPortWidth) &&
|
|
IsEqualIID(proposed.guidTypeID, videoport.guidTypeID);
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::NegotiateConnectionParamaters
|
|
*
|
|
* 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.
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::NegotiateConnectionParamaters()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::NegotiateConnectionParamaters")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
LPDDVIDEOPORTCONNECT lpddProposedConnect = NULL;
|
|
DWORD dwNumProposedEntries = 0;
|
|
DDVIDEOPORTSTATUS ddVPStatus = { sizeof(DDVIDEOPORTSTATUS)};
|
|
LPDDVIDEOPORTCONNECT lpddVideoPortConnect = NULL;
|
|
DWORD dwNumVideoPortEntries = 0;
|
|
BOOL bIntersectionFound = FALSE;
|
|
DWORD i, j;
|
|
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
ASSERT(m_pIVPConfig);
|
|
ASSERT(m_pDVP);
|
|
|
|
// find the number of entries to be proposed
|
|
hr = m_pIVPConfig->GetConnectInfo(&dwNumProposedEntries, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->GetConnectInfo failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
ASSERT(dwNumProposedEntries);
|
|
|
|
// allocate the necessary memory
|
|
lpddProposedConnect = new DDVIDEOPORTCONNECT[dwNumProposedEntries];
|
|
if (lpddProposedConnect == NULL)
|
|
{
|
|
DbgLog((LOG_ERROR,0,TEXT("NegotiateConnectionParamaters : Out of Memory")));
|
|
hr = E_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// memset the allocated memory to zero
|
|
ZeroArray(lpddProposedConnect, dwNumProposedEntries );
|
|
|
|
// set the right size in each of the structs.
|
|
for (i = 0; i < dwNumProposedEntries; i++)
|
|
{
|
|
lpddProposedConnect[i].dwSize = sizeof(DDVIDEOPORTCONNECT);
|
|
}
|
|
|
|
// get the entries proposed
|
|
hr = m_pIVPConfig->GetConnectInfo(&dwNumProposedEntries, lpddProposedConnect);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->GetConnectInfo failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
// get the status of the video port
|
|
hr = m_pDVP->QueryVideoPortStatus(m_dwVideoPortId, &ddVPStatus);
|
|
if (FAILED(hr))
|
|
{
|
|
// Some cards don't implement this so just crash on
|
|
ddVPStatus.bInUse = FALSE;
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pDVP->QueryVideoPortStatus failed, hr = 0x%x"), hr));
|
|
// goto CleanUp;
|
|
}
|
|
|
|
// find the number of entries supported by the videoport
|
|
hr = m_pDVP->GetVideoPortConnectInfo(m_dwVideoPortId, &dwNumVideoPortEntries, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pDVP->GetVideoPortConnectInfo failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
ASSERT(dwNumVideoPortEntries);
|
|
|
|
// allocate the necessary memory
|
|
lpddVideoPortConnect = new DDVIDEOPORTCONNECT[dwNumVideoPortEntries];
|
|
if (lpddVideoPortConnect == NULL)
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("NegotiateConnectionParamaters : Out of Memory")));
|
|
hr = E_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// memset the allocated memory to zero
|
|
ZeroMemory(lpddVideoPortConnect,
|
|
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_pDVP->GetVideoPortConnectInfo(m_dwVideoPortId, &dwNumVideoPortEntries,
|
|
lpddVideoPortConnect);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pDVP->GetVideoPortConnectInfo failed, hr = 0x%x"), hr));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
// check if the video port is not already in use
|
|
if (!ddVPStatus.bInUse)
|
|
{
|
|
|
|
// take the first element of the intersection of the two lists and
|
|
// set that value on the decoder
|
|
for (i = 0; i < dwNumProposedEntries && !bIntersectionFound; i++)
|
|
{
|
|
for (j = 0; j < dwNumVideoPortEntries && !bIntersectionFound; j++)
|
|
{
|
|
if ( AreEqual( lpddProposedConnect[i], lpddVideoPortConnect[j]) )
|
|
{
|
|
m_ddConnectInfo = lpddVideoPortConnect[j];
|
|
hr = m_pIVPConfig->SetConnectInfo(i);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->SetConnectInfo")
|
|
TEXT(" failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
bIntersectionFound = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// take the first element of the list matching the current status
|
|
for (i = 0; i < dwNumProposedEntries && !bIntersectionFound; i++)
|
|
{
|
|
if ( AreEqual(lpddProposedConnect[i], ddVPStatus.VideoPortType) )
|
|
{
|
|
for (j = 0; j < dwNumVideoPortEntries && !bIntersectionFound; j++)
|
|
{
|
|
if ( AreEqual(lpddProposedConnect[i], lpddVideoPortConnect[j]) )
|
|
{
|
|
m_ddConnectInfo = lpddVideoPortConnect[j];
|
|
bIntersectionFound = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bIntersectionFound)
|
|
{
|
|
hr = E_FAIL;
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
// cleanup
|
|
CleanUp:
|
|
delete [] lpddProposedConnect;
|
|
delete [] lpddVideoPortConnect;
|
|
return hr;
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::GetDataParameters
|
|
*
|
|
*
|
|
* 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
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::GetDataParameters()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::GetDataParameters")));
|
|
|
|
HRESULT hr = NOERROR;
|
|
DWORD dwMaxPixelsPerSecond = 0;
|
|
AMVPSIZE amvpSize;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
|
|
// set the size of the struct
|
|
m_VPDataInfo.dwSize = sizeof(AMVPDATAINFO);
|
|
|
|
// get the VideoPort data information
|
|
hr = m_pIVPConfig->GetVPDataInfo(&m_VPDataInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->GetVPDataInfo failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
/*
|
|
if (m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom > m_VPDataInfo.amvpDimInfo.dwFieldHeight)
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom = m_VPDataInfo.amvpDimInfo.dwFieldHeight;
|
|
*/
|
|
|
|
// if decoder says data is not interlaced
|
|
if (!(m_VPDataInfo.bDataIsInterlaced))
|
|
{
|
|
// this flag does not mean anything
|
|
if (m_VPDataInfo.bFieldPolarityInverted)
|
|
{
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// these don't mean anything either
|
|
if ((m_VPDataInfo.lHalfLinesOdd != 0) ||
|
|
(m_VPDataInfo.lHalfLinesEven != 0))
|
|
{
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
amvpSize.dwWidth = m_VPDataInfo.amvpDimInfo.dwFieldWidth;
|
|
amvpSize.dwHeight = m_VPDataInfo.amvpDimInfo.dwFieldHeight;
|
|
|
|
// 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:
|
|
DbgLog((LOG_TRACE, 5,TEXT("Leaving CVideoPortObj::GetDataParameters")));
|
|
return hr;
|
|
}
|
|
|
|
static BOOL CanCreateSurface( LPDIRECTDRAW7 pDirectDraw, const DDPIXELFORMAT& ddFormat )
|
|
{
|
|
|
|
// check if output format is suitable for a DDraw output device
|
|
|
|
DDSURFACEDESC2 ddsdDesc;
|
|
ddsdDesc.dwSize = sizeof(DDSURFACEDESC);
|
|
ddsdDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT |
|
|
DDSD_WIDTH | DDSD_PIXELFORMAT;
|
|
|
|
ddsdDesc.ddpfPixelFormat = ddFormat;
|
|
|
|
ddsdDesc.ddsCaps.dwCaps = // DDSCAPS_OVERLAY |
|
|
DDSCAPS_VIDEOMEMORY |
|
|
DDSCAPS_VIDEOPORT;
|
|
|
|
// the actual overlay surface created might be of different
|
|
// dimensions, however we are just testing the pixel format
|
|
ddsdDesc.dwWidth = 64;
|
|
ddsdDesc.dwHeight = 64;
|
|
|
|
ASSERT(pDirectDraw);
|
|
LPDIRECTDRAWSURFACE7 pSurf;
|
|
HRESULT hr = pDirectDraw->CreateSurface(&ddsdDesc, &pSurf, NULL);
|
|
if( SUCCEEDED( hr )) {
|
|
pSurf->Release();
|
|
}
|
|
return SUCCEEDED( hr );
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::GetBestFormat
|
|
*
|
|
* this function takes a list of inputformats and returns the
|
|
* "best" input and output format according to some criterion.
|
|
* It also checks if the output formats is suitable by trying
|
|
* to allocate a small surface and checking to see if the call
|
|
* succeeds. Since this is before the overlay surface has been
|
|
* created, that should be a ok. Right now the criterion just
|
|
* includes bestbendwidth, or if not that then just the first
|
|
* suitable one in the list.
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT
|
|
CVideoPortObj::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_VIDEO);
|
|
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_VIDEO);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pVideoPort->GetOutputFormats failed, hr = 0x%x"),
|
|
hr));
|
|
break;
|
|
}
|
|
} // end of outer for loop
|
|
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 CVideoPortObj::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_VIDEO);
|
|
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_VIDEO);
|
|
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 CVideoPortObj::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 CVideoPortObj::ReleaseVideoPort()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
// tell the filter we've yanked the VP so that it doesn't hold onto refs to the VP
|
|
if( m_pIVideoPortControl ) {
|
|
hr = m_pIVideoPortControl->SignalNewVP( NULL );
|
|
}
|
|
// release it ourselves
|
|
RELEASE( m_pVideoPort );
|
|
return hr;
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::CreateVideoPort
|
|
*
|
|
* Displays the Create Video Port dialog and calls DDRAW to actually
|
|
* create the port.
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::CreateVideoPort()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
DDVIDEOPORTDESC svpDesc;
|
|
DWORD dwTemp = 0, dwOldVal = 0;
|
|
DWORD lHalfLinesOdd = 0, lHalfLinesEven = 0;
|
|
AMTRACE((TEXT("CVideoPortObj::CreateVideoPort")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
INITDDSTRUCT(svpDesc);
|
|
|
|
// 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_VPDataInfo.bEnableDoubleClock &&
|
|
m_ddConnectInfo.dwFlags & DDVPCONNECT_DOUBLECLOCK)
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_DOUBLECLOCK;
|
|
}
|
|
else
|
|
{
|
|
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_VPDataInfo.bEnableVACT &&
|
|
m_ddConnectInfo.dwFlags & DDVPCONNECT_VACT)
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_VACT;
|
|
}
|
|
else
|
|
{
|
|
svpDesc.VideoPortType.dwFlags &= ~DDVPCONNECT_VACT;
|
|
}
|
|
|
|
// if the decoder can send interlaced data and the videoport
|
|
// supports it, then set that property.
|
|
if (m_VPDataInfo.bDataIsInterlaced)
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_INTERLACED;
|
|
m_bVSInterlaced = TRUE;
|
|
}
|
|
else
|
|
{
|
|
svpDesc.VideoPortType.dwFlags &= ~DDVPCONNECT_INTERLACED;
|
|
m_bVSInterlaced = FALSE;
|
|
}
|
|
|
|
// handle the VREF stuff here
|
|
if (m_ddConnectInfo.dwFlags & DDVPCONNECT_DISCARDSVREFDATA)
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top -=
|
|
m_VPDataInfo.dwNumLinesInVREF;
|
|
|
|
if (m_VPDataInfo.amvpDimInfo.rcValidRegion.top < 0)
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top = 0;
|
|
}
|
|
|
|
// handle the halfline stuff here
|
|
lHalfLinesOdd = m_VPDataInfo.lHalfLinesOdd;
|
|
lHalfLinesEven = m_VPDataInfo.lHalfLinesEven;
|
|
|
|
// reset both the halfline and the invert polarity bits
|
|
svpDesc.VideoPortType.dwFlags &= ~DDVPCONNECT_HALFLINE;
|
|
svpDesc.VideoPortType.dwFlags &= ~DDVPCONNECT_INVERTPOLARITY;
|
|
|
|
// if halflines are being reported assert that the data is interlaced
|
|
if (lHalfLinesOdd != 0 || lHalfLinesEven != 0)
|
|
{
|
|
ASSERT(m_VPDataInfo.bDataIsInterlaced);
|
|
}
|
|
|
|
// whenever halflines exist, make sure to set the tell the hal
|
|
if (((lHalfLinesOdd == 1 || lHalfLinesEven == 1) && (m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE)) ||
|
|
((lHalfLinesOdd == -1 || lHalfLinesEven == -1) && (!(m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE))))
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_HALFLINE;
|
|
}
|
|
|
|
// In this case, the video is forced to move down one line
|
|
// case 2 in scott's document
|
|
if ((lHalfLinesOdd == 0) &&
|
|
(lHalfLinesEven == 1) &&
|
|
(m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE))
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top += 1;
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom += 2;
|
|
m_fGarbageLine = true;
|
|
|
|
// if the deocder is already not inverting fields and if the VGA supports
|
|
// inverting polarities, then ask the VGA to invert polarities othwise ask
|
|
// decoder to invert polarities.
|
|
if (m_ddConnectInfo.dwFlags & DDVPCONNECT_INVERTPOLARITY)
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_INVERTPOLARITY;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pIVPConfig->SetInvertPolarity();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->SetInvertPolarity failed, hr = 0x%x"),
|
|
hr));
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
// case 3 and 5 in scott's document
|
|
else if ((lHalfLinesOdd == 1) &&
|
|
(lHalfLinesEven == 0))
|
|
{
|
|
// case 5 (just shift by one, do not reverse polarities
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top += 1;
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom += 2;
|
|
m_fGarbageLine = true;
|
|
m_bCantInterleaveHalfline = TRUE;
|
|
|
|
|
|
// case 3 (shift by one and reverse polarities)
|
|
if (!(m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE))
|
|
{
|
|
// if the deocder is already not inverting fields and if the
|
|
// VGA supports inverting polarities, then ask the VGA to invert
|
|
// polarities othwise ask decoder to invert polarities.
|
|
//
|
|
if (m_ddConnectInfo.dwFlags & DDVPCONNECT_INVERTPOLARITY)
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_INVERTPOLARITY;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pIVPConfig->SetInvertPolarity();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->SetInvertPolarity failed,")
|
|
TEXT(" hr = 0x%x"),
|
|
hr));
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// case 4 in scott's document
|
|
else if ((lHalfLinesOdd == 0) &&
|
|
(lHalfLinesEven == -1) &&
|
|
(!(m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE)))
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top += 0;
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom += 1;
|
|
m_fGarbageLine = true;
|
|
}
|
|
else if (((lHalfLinesOdd == 0) && (lHalfLinesEven == 0)) ||
|
|
((lHalfLinesOdd == -1) && (lHalfLinesEven == 0) && (m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE)) ||
|
|
((lHalfLinesOdd == 0) && (lHalfLinesEven == -1) && (m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE)) || // opposite of case 4
|
|
((lHalfLinesOdd == 0) && (lHalfLinesEven == 1) && (!(m_ddConnectInfo.dwFlags & DDVPCONNECT_HALFLINE)))) // opposite of case 2
|
|
{
|
|
// if the deocder is already inverting fields and if the VGA supports
|
|
// inverting polarities, then ask the VGA to invert polarities
|
|
// othwise ask decoder to invert polarities.
|
|
if (m_VPDataInfo.bFieldPolarityInverted)
|
|
{
|
|
if (m_ddConnectInfo.dwFlags & DDVPCONNECT_INVERTPOLARITY)
|
|
{
|
|
svpDesc.VideoPortType.dwFlags |= DDVPCONNECT_INVERTPOLARITY;
|
|
}
|
|
else
|
|
{
|
|
hr = m_pIVPConfig->SetInvertPolarity();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("m_pIVPConfig->SetInvertPolarity failed,")
|
|
TEXT(" hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Potential bug : workaround for current BPC driver
|
|
// hr = E_FAIL; // we can't handle these cases, FAIL
|
|
// goto CleanUp;
|
|
}
|
|
|
|
if (m_VPDataInfo.amvpDimInfo.dwFieldHeight <
|
|
(DWORD)m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom)
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.dwFieldHeight =
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom;
|
|
}
|
|
|
|
if ((m_vpCaps.dwFlags & DDVPD_WIDTH) &&
|
|
(m_VPDataInfo.amvpDimInfo.dwFieldWidth > m_vpCaps.dwMaxWidth))
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.dwFieldWidth = m_vpCaps.dwMaxWidth;
|
|
}
|
|
|
|
if ((m_vpCaps.dwFlags & DDVPD_WIDTH) &&
|
|
(m_VPDataInfo.amvpDimInfo.dwVBIWidth > m_vpCaps.dwMaxVBIWidth))
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.dwVBIWidth = m_vpCaps.dwMaxVBIWidth;
|
|
}
|
|
|
|
if ((m_vpCaps.dwFlags & DDVPD_HEIGHT) &&
|
|
(m_VPDataInfo.amvpDimInfo.dwFieldHeight > m_vpCaps.dwMaxHeight))
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.dwFieldHeight = m_vpCaps.dwMaxHeight;
|
|
}
|
|
|
|
if (m_VPDataInfo.amvpDimInfo.rcValidRegion.right >
|
|
(LONG)m_VPDataInfo.amvpDimInfo.dwFieldWidth)
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.right =
|
|
(LONG)m_VPDataInfo.amvpDimInfo.dwFieldWidth;
|
|
}
|
|
|
|
if (m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom >
|
|
(LONG)m_VPDataInfo.amvpDimInfo.dwFieldHeight)
|
|
{
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom =
|
|
(LONG)m_VPDataInfo.amvpDimInfo.dwFieldHeight;
|
|
}
|
|
|
|
// fill up the fields of the description struct
|
|
svpDesc.dwFieldWidth = m_VPDataInfo.amvpDimInfo.dwFieldWidth;
|
|
svpDesc.dwVBIWidth = m_VPDataInfo.amvpDimInfo.dwVBIWidth;
|
|
svpDesc.dwFieldHeight = m_VPDataInfo.amvpDimInfo.dwFieldHeight;
|
|
|
|
svpDesc.dwMicrosecondsPerField = m_VPDataInfo.dwMicrosecondsPerField;
|
|
svpDesc.dwMaxPixelsPerSecond = m_dwPixelsPerSecond;
|
|
svpDesc.dwVideoPortID = m_dwVideoPortId;
|
|
svpDesc.VideoPortType.dwSize = sizeof(DDVIDEOPORTCONNECT);
|
|
svpDesc.VideoPortType.dwPortWidth = m_ddConnectInfo.dwPortWidth;
|
|
memcpy(&svpDesc.VideoPortType.guidTypeID, &m_ddConnectInfo.guidTypeID, sizeof(GUID));
|
|
|
|
DbgLog((LOG_TRACE, 3, TEXT("svpDesc")));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwFieldWidth = %u"), svpDesc.dwFieldWidth));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwVBIWidth = %u"), svpDesc.dwVBIWidth));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwFieldHeight= %u"), svpDesc.dwFieldHeight));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwMicrosecondsPerField= %u"), svpDesc.dwMicrosecondsPerField));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwMaxPixelsPerSecond= %u"), svpDesc.dwMaxPixelsPerSecond));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwVideoPortID= %u"), svpDesc.dwVideoPortID));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwSize= %u"), svpDesc.VideoPortType.dwSize));
|
|
DbgLog((LOG_TRACE, 3, TEXT("dwPortWidth= %u"), svpDesc.VideoPortType.dwPortWidth));
|
|
|
|
// create the videoport. The first parameter is dwFlags, reserved for
|
|
// future use by ddraw. The last parameter is pUnkOuter, again must be
|
|
// NULL.
|
|
//
|
|
// use the DDVPCREATE_VIDEOONLY flag only if the hal is capable of
|
|
// streaming VBI on a seperate surface
|
|
//
|
|
|
|
ReleaseVideoPort();
|
|
|
|
if (m_vpCaps.dwCaps & DDVPCAPS_VBIANDVIDEOINDEPENDENT)
|
|
{
|
|
hr = m_pDVP->CreateVideoPort(DDVPCREATE_VIDEOONLY, &svpDesc,
|
|
&m_pVideoPort, NULL);
|
|
ASSERT( hr != DDERR_OUTOFCAPS ); // means videoport is in use, I.E. the VPM has leaked a ref count to the videoport
|
|
// usually we forgot a RELEASE
|
|
// ASSERT( SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pDVP->CreateVideoPort(DDVPCREATE_VIDEOONLY)")
|
|
TEXT(" failed, hr = 0x%x"), hr));
|
|
}
|
|
} else {
|
|
hr = m_pDVP->CreateVideoPort(0, &svpDesc, &m_pVideoPort, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pDVP->CreateVideoPort(0) failed, hr = 0x%x"), hr));
|
|
}
|
|
}
|
|
// tell the filter about the new VP in ReconnectVideoPortToSurface after we have a new surface
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::DetermineCroppingRestrictions
|
|
*
|
|
*
|
|
* this function is used to check the cropping restrictions at the
|
|
* videoport and at the overlay. This function also decides where
|
|
* the cropping should be done (at videoport or at overlay).
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::DetermineCroppingRestrictions()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::DetermineCroppingRestrictions")));
|
|
HRESULT hr = NOERROR;
|
|
|
|
BOOL bVideoPortCanCrop = TRUE, bOverlayCanCrop = TRUE;
|
|
DWORD dwTemp = 0, dwOldVal = 0;
|
|
DWORD dwCropOriginX = 0, dwCropOriginY = 0;
|
|
DWORD dwCropWidth = 0, dwCropHeight=0;
|
|
const DDCAPS* pDirectCaps = NULL;
|
|
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
pDirectCaps = m_pIVideoPortControl->GetHardwareCaps();
|
|
ASSERT(pDirectCaps);
|
|
|
|
// cache the cropping paramters
|
|
dwCropOriginX = m_VPDataInfo.amvpDimInfo.rcValidRegion.left;
|
|
dwCropOriginY = m_VPDataInfo.amvpDimInfo.rcValidRegion.top;
|
|
dwCropWidth = (DWORD)(m_VPDataInfo.amvpDimInfo.rcValidRegion.right -
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.left);
|
|
dwCropHeight = (DWORD)(m_VPDataInfo.amvpDimInfo.rcValidRegion.bottom -
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.top);
|
|
|
|
|
|
// Align the left boundary
|
|
if (bVideoPortCanCrop && (m_vpCaps.dwFlags & DDVPD_ALIGN))
|
|
{
|
|
dwTemp = dwCropOriginX & (m_vpCaps.dwAlignVideoPortCropBoundary-1);
|
|
if (dwTemp != 0)
|
|
{
|
|
dwOldVal = dwCropOriginX;
|
|
dwCropOriginX = dwCropOriginX +
|
|
m_vpCaps.dwAlignVideoPortCropBoundary - dwTemp;
|
|
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.left = dwCropOriginX;
|
|
dwCropWidth = (DWORD)(m_VPDataInfo.amvpDimInfo.rcValidRegion.right -
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.left);
|
|
DbgLog((LOG_TRACE,2,
|
|
TEXT("Alligning the left cropping boundary from %d to %d"),
|
|
dwOldVal, dwCropOriginX));
|
|
}
|
|
}
|
|
|
|
// Align the width
|
|
if (bVideoPortCanCrop && (m_vpCaps.dwFlags & DDVPD_ALIGN))
|
|
{
|
|
dwTemp = dwCropWidth & (m_vpCaps.dwAlignVideoPortCropWidth-1);
|
|
if (dwTemp != 0)
|
|
{
|
|
dwOldVal = dwCropOriginX;
|
|
dwCropWidth = dwCropWidth - dwTemp;
|
|
m_VPDataInfo.amvpDimInfo.rcValidRegion.right =
|
|
dwCropWidth + (DWORD)(m_VPDataInfo.amvpDimInfo.rcValidRegion.left);
|
|
DbgLog((LOG_TRACE,2,
|
|
TEXT("Alligning the width of cropping rect from %d to %d"),
|
|
dwOldVal, dwCropWidth));
|
|
}
|
|
}
|
|
|
|
// determine if we can do without any cropping at all
|
|
if (dwCropOriginX == 0 && dwCropOriginY == 0 &&
|
|
dwCropWidth == m_VPDataInfo.amvpDimInfo.dwFieldWidth &&
|
|
dwCropHeight == m_VPDataInfo.amvpDimInfo.dwFieldHeight)
|
|
{
|
|
// hurray we are home free!!!
|
|
DbgLog((LOG_TRACE,1, TEXT("No cropping necessary")));
|
|
m_CropState = VPInfoCropState_None;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// determine if the videoport can do the cropping for us
|
|
|
|
// Can the videoport crop in the X direction
|
|
if (bVideoPortCanCrop && (m_vpCaps.dwFlags & DDVPD_FX))
|
|
{
|
|
if (dwCropWidth != m_VPDataInfo.amvpDimInfo.dwFieldWidth &&
|
|
(m_vpCaps.dwFX & DDVPFX_CROPX) == 0)
|
|
{
|
|
DbgLog((LOG_ERROR,1, TEXT("VideoPort can't crop, DDVPFX_CROPX == 0")));
|
|
bVideoPortCanCrop = FALSE;
|
|
}
|
|
}
|
|
|
|
// Can the videoport crop in the Y direction
|
|
if (bVideoPortCanCrop && (m_vpCaps.dwFlags & DDVPD_FX))
|
|
{
|
|
if (dwCropHeight != m_VPDataInfo.amvpDimInfo.dwFieldHeight &&
|
|
(m_vpCaps.dwFX & DDVPFX_CROPY) == 0 &&
|
|
(m_vpCaps.dwFX & DDVPFX_CROPTOPDATA) == 0)
|
|
{
|
|
DbgLog((LOG_ERROR,1, TEXT("VideoPort can't crop, DDVPFX_CROPY == 0")));
|
|
bVideoPortCanCrop = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// ok, so the videoport can crop for us. So no need to crop at the
|
|
// overlay surface.
|
|
if (bVideoPortCanCrop)
|
|
{
|
|
DbgLog((LOG_TRACE,2, TEXT("Cropping would be done at the videoport")));
|
|
m_CropState = VPInfoCropState_AtVideoPort;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// determine if the overlay can do the cropping for us
|
|
ASSERT( !"Cropping must be at overlay ... not supported" );
|
|
// Is left boundary alligned
|
|
if (bOverlayCanCrop && (pDirectCaps->dwCaps & DDCAPS_ALIGNBOUNDARYDEST))
|
|
{
|
|
dwTemp = dwCropOriginX & (pDirectCaps->dwAlignBoundaryDest-1);
|
|
if (dwTemp != 0)
|
|
{
|
|
DbgLog((LOG_ERROR,1,
|
|
TEXT("Overlay can't crop, Align = %d, Crop.left = %d"),
|
|
dwTemp, dwCropOriginX));
|
|
bOverlayCanCrop = FALSE;
|
|
}
|
|
}
|
|
if (bOverlayCanCrop && (pDirectCaps->dwCaps & DDCAPS_ALIGNBOUNDARYSRC))
|
|
{
|
|
dwTemp = dwCropOriginX & (pDirectCaps->dwAlignBoundarySrc-1);
|
|
if (dwTemp != 0)
|
|
{
|
|
DbgLog((LOG_ERROR,1,
|
|
TEXT("Overlay can't crop, Align = %d, Crop.left = %d"),
|
|
dwTemp, dwCropOriginX));
|
|
bOverlayCanCrop = FALSE;
|
|
}
|
|
}
|
|
|
|
// Is Width alligned
|
|
if (bOverlayCanCrop && (pDirectCaps->dwCaps & DDCAPS_ALIGNSIZEDEST))
|
|
{
|
|
dwTemp = dwCropWidth & (pDirectCaps->dwAlignSizeDest -1);
|
|
if (dwTemp != 0)
|
|
{
|
|
DbgLog((LOG_ERROR,1,
|
|
TEXT("Overlay can't crop, Align = %d, Crop.Width = %d"),
|
|
dwTemp, dwCropWidth));
|
|
bOverlayCanCrop = FALSE;
|
|
}
|
|
}
|
|
if (bOverlayCanCrop && (pDirectCaps->dwCaps & DDCAPS_ALIGNSIZESRC))
|
|
{
|
|
dwTemp = dwCropWidth & (pDirectCaps->dwAlignSizeSrc -1);
|
|
if (dwTemp != 0)
|
|
{
|
|
DbgLog((LOG_ERROR,1,
|
|
TEXT("Overlay can't crop, Align = %d, Crop.Width = %d"),
|
|
dwTemp, dwCropWidth));
|
|
bOverlayCanCrop = FALSE;
|
|
}
|
|
}
|
|
|
|
// ok, the videoport was unsuitable but the overlay came through
|
|
// this means more pain for me, no!!!
|
|
if (bOverlayCanCrop)
|
|
{
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVideoPortObj::RecreateSourceSurfaceChain()
|
|
{
|
|
DWORD dwcSurfaces = m_dwBackBufferCount + 1;
|
|
|
|
if( !m_pOutputSurface ) {
|
|
return E_POINTER;
|
|
}
|
|
RELEASE( m_pOutputSurface1 );
|
|
|
|
// required for SetTargetSurface for videoport
|
|
HRESULT hResult = m_pOutputSurface->QueryInterface( IID_IDirectDrawSurface, (VOID **)&m_pOutputSurface1 );
|
|
if( FAILED( hResult )) {
|
|
return hResult;
|
|
}
|
|
|
|
// otherwise we're leaking surface counts
|
|
delete [] m_pChain;
|
|
|
|
m_pChain = new Chain[dwcSurfaces];
|
|
if ( ! m_pChain )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
m_pChain[0].pDDSurf = m_pOutputSurface;
|
|
m_pChain[0].dwCount =0;
|
|
if ( m_dwBackBufferCount )
|
|
{
|
|
LPDIRECTDRAWSURFACE7 pDDS = m_pOutputSurface;
|
|
LPDIRECTDRAWSURFACE7 pDDSBack;
|
|
for ( UINT i = 1; i < dwcSurfaces; i++ )
|
|
{
|
|
DDSCAPS2 caps = {0};
|
|
m_pChain[i].pDDSurf = NULL;
|
|
m_pChain[i].dwCount =0;
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
DDSURFACEDESC2 ddSurfaceDesc;
|
|
// get the surface description
|
|
INITDDSTRUCT(ddSurfaceDesc);
|
|
pDDS->GetSurfaceDesc(&ddSurfaceDesc);
|
|
}
|
|
#endif
|
|
|
|
if( i==1 ) {
|
|
// for first attached get the back buffer
|
|
caps.dwCaps = DDSCAPS_BACKBUFFER;
|
|
} else {
|
|
// for the rest get the complex surfaces
|
|
// (since only the first has DDSCAPS_BACKBUFFER set)
|
|
caps.dwCaps = DDSCAPS_COMPLEX;
|
|
}
|
|
if ( SUCCEEDED( pDDS->GetAttachedSurface( &caps, &pDDSBack ) ) )
|
|
{
|
|
m_pChain[i].pDDSurf = pDDSBack;
|
|
pDDS = pDDSBack;
|
|
} else {
|
|
ASSERT( !"Fatal problem ... can't get attached surface (bug in video driver)" );
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
DbgLog((LOG_TRACE, 1,
|
|
TEXT("Created an offscreen Surface of Width=%d,")
|
|
TEXT(" Height=%d, Total-No-of-Buffers=%d"),
|
|
m_dwOutputSurfaceWidth, m_dwOutputSurfaceHeight,
|
|
dwcSurfaces ));
|
|
return S_OK;
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::CreateVPOverlay
|
|
*
|
|
* this function is used to allocate an overlay surface to attach to the
|
|
* videoport.
|
|
* The allocation order it tries is just in decreasing amount of memory
|
|
* required. Theres is one ambiguity, which is resolved by bPreferBuffers
|
|
* (3 buffers, double height)
|
|
* (2 buffers, double height)
|
|
* (3 buffers, single height)
|
|
* (2 buffers, single height) OR (1 buffer , double height) (depends upon bPreferBuffers)
|
|
* (1 buffer , single height).
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT
|
|
CVideoPortObj::CreateSourceSurface(
|
|
BOOL bTryDoubleHeight,
|
|
DWORD dwMaxBuffers,
|
|
BOOL bPreferBuffers)
|
|
{
|
|
DDSURFACEDESC2 ddsdDesc;
|
|
HRESULT hr = NOERROR;
|
|
DWORD dwMaxHeight = 0, dwMinHeight = 0, dwCurHeight = 0, dwCurBuffers = 0;
|
|
LPDIRECTDRAW7 pDirectDraw = NULL;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::CreateVPOverlay")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
ASSERT(pDirectDraw);
|
|
|
|
// initialize the fields of 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;
|
|
ddsdDesc.dwWidth = m_lImageWidth;
|
|
|
|
|
|
dwMaxHeight = dwMinHeight = m_lImageHeight;
|
|
|
|
// make sure we don't leak the old surface
|
|
DestroyOutputSurfaces();
|
|
|
|
// we will try to allocate double height surface, only if the decoder is
|
|
// sending interlaced data, and the videoport supports interlaced data
|
|
// and can interleave interlaced data in memory and bTryDoubleHeight is true
|
|
if (bTryDoubleHeight)
|
|
{
|
|
dwMaxHeight = 2 * m_lImageHeight;
|
|
}
|
|
else
|
|
{
|
|
// make sure that bPreferBuffers is TRUE here, since it is a single
|
|
// height case making it FALSE would not make any sense
|
|
bPreferBuffers = TRUE;
|
|
}
|
|
|
|
// we will only try to allocate more than one buffer, if the videoport
|
|
// is cabable of autoflipping
|
|
if (dwMaxBuffers > 1)
|
|
{
|
|
ddsdDesc.dwFlags |= DDSD_BACKBUFFERCOUNT;
|
|
ddsdDesc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
|
|
|
|
for (dwCurHeight = dwMaxHeight;
|
|
!m_pOutputSurface && dwCurHeight >= dwMinHeight; dwCurHeight /= 2)
|
|
{
|
|
for (dwCurBuffers = dwMaxBuffers;
|
|
!m_pOutputSurface && dwCurBuffers >= 2; dwCurBuffers--)
|
|
{
|
|
|
|
// if the case is (2 buffers, single height) but we prefer
|
|
// more height rather than more buffers, then postpone this
|
|
// case. We will come to it eventually, if the other cases fail.
|
|
if (!bPreferBuffers &&
|
|
dwCurBuffers == 2 &&
|
|
dwCurHeight == m_lImageHeight)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ddsdDesc.dwHeight = dwCurHeight;
|
|
ddsdDesc.dwBackBufferCount = dwCurBuffers-1;
|
|
|
|
hr = pDirectDraw->CreateSurface(&ddsdDesc, &m_pOutputSurface, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dwBackBufferCount = dwCurBuffers-1;
|
|
m_dwOutputSurfaceHeight = ddsdDesc.dwHeight;
|
|
m_dwOutputSurfaceWidth = ddsdDesc.dwWidth;
|
|
hr = RecreateSourceSurfaceChain();
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// we should only reach this point when attempt to allocate multiple buffers
|
|
// failed or no autoflip available or bPreferBuffers is FALSE
|
|
|
|
|
|
// case (1 buffer, double height)
|
|
if (dwMaxHeight == 2*m_lImageHeight)
|
|
{
|
|
ddsdDesc.dwHeight = 2*m_lImageHeight;
|
|
ddsdDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT;
|
|
ddsdDesc.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP);
|
|
ddsdDesc.dwBackBufferCount = 0;
|
|
|
|
hr = pDirectDraw->CreateSurface(&ddsdDesc, &m_pOutputSurface, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dwBackBufferCount = 0;
|
|
m_dwOutputSurfaceHeight = ddsdDesc.dwHeight;
|
|
m_dwOutputSurfaceWidth = ddsdDesc.dwWidth;
|
|
hr = RecreateSourceSurfaceChain();
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
// case (2 buffer, single height) only if you prefer height to buffers
|
|
if (bPreferBuffers && (dwMaxBuffers > 1) &&
|
|
(m_vpCaps.dwCaps & DDVPCAPS_AUTOFLIP))
|
|
{
|
|
ddsdDesc.dwFlags |= DDSD_BACKBUFFERCOUNT;
|
|
ddsdDesc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX | DDSCAPS_FLIP;
|
|
|
|
ddsdDesc.dwHeight = 2*m_lImageHeight;
|
|
ddsdDesc.dwBackBufferCount = 1;
|
|
hr = pDirectDraw->CreateSurface(&ddsdDesc, &m_pOutputSurface, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dwBackBufferCount = 1;
|
|
m_dwOutputSurfaceHeight = ddsdDesc.dwHeight;
|
|
m_dwOutputSurfaceWidth = ddsdDesc.dwWidth;
|
|
hr = RecreateSourceSurfaceChain();
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
// case (1 buffer, single height)
|
|
{
|
|
ddsdDesc.dwHeight = m_lImageHeight;
|
|
ddsdDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT;
|
|
ddsdDesc.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP);
|
|
ddsdDesc.dwBackBufferCount = 0;
|
|
hr = pDirectDraw->CreateSurface(&ddsdDesc, &m_pOutputSurface, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_dwBackBufferCount = 0;
|
|
m_dwOutputSurfaceHeight = ddsdDesc.dwHeight;
|
|
m_dwOutputSurfaceWidth = ddsdDesc.dwWidth;
|
|
hr = RecreateSourceSurfaceChain();
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
// ASSERT( m_pOutputSurface );
|
|
DbgLog((LOG_TRACE, 1, TEXT("Unable to create offset output surface")));
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
static DWORD GetPitch( const DDSURFACEDESC2& ddSurf )
|
|
{
|
|
const DDPIXELFORMAT& ddFormat = ddSurf.ddpfPixelFormat;
|
|
|
|
if( ddSurf.dwFlags & DDSD_PITCH ) {
|
|
return ddSurf.lPitch;
|
|
} else {
|
|
if( ddFormat.dwFlags & DDPF_FOURCC) {
|
|
if( ddFormat.dwFourCC == mmioFOURCC('U','Y','V','Y') ) {
|
|
return 2* ddSurf.dwWidth;
|
|
}
|
|
}
|
|
return ddSurf.dwWidth;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::SetSurfaceParameters
|
|
*
|
|
* SetSurfaceParameters used to tell the decoder where the
|
|
* valid data is on the surface
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::SetSurfaceParameters()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
DWORD dwPitch = 0;
|
|
DDSURFACEDESC2 ddSurfaceDesc;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::SetSurfaceParameters")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// get the surface description
|
|
INITDDSTRUCT(ddSurfaceDesc);
|
|
hr = m_pOutputSurface->GetSurfaceDesc(&ddSurfaceDesc);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 1,
|
|
TEXT("m_pOutputSurface->GetSurfaceDesc failed, hr = 0x%x"),
|
|
hr));
|
|
}
|
|
else
|
|
{
|
|
ASSERT(ddSurfaceDesc.dwFlags & DDSD_PITCH);
|
|
dwPitch = GetPitch(ddSurfaceDesc);
|
|
}
|
|
|
|
hr = m_pIVPConfig->SetSurfaceParameters(dwPitch, 0, 0);
|
|
|
|
// right now the proxy maps ERROR_SET_NOT_FOUND to an HRESULT and
|
|
// returns that failure code if the driver does not implement a function
|
|
//
|
|
if (hr == E_NOTIMPL || hr == (HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND)))
|
|
{
|
|
hr = NOERROR;
|
|
DbgLog((LOG_TRACE, 5,TEXT("SetSurfaceParamters not implemented")));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_TRACE, 5,TEXT("SetSurfaceParamters failed, hr = 0x%x"), hr));
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::InitializeVideoPortInfo
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::InitializeVideoPortInfo()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::InitializeVideoPortInfo")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// initialize the DDVIDEOPORTINFO struct to be passed to start-video
|
|
INITDDSTRUCT(m_svpInfo);
|
|
m_svpInfo.lpddpfInputFormat = &m_ddVPInputVideoFormat;
|
|
m_svpInfo.dwVPFlags = DDVP_AUTOFLIP;
|
|
|
|
if (m_CropState == VPInfoCropState_AtVideoPort)
|
|
{
|
|
m_svpInfo.rCrop = m_VPDataInfo.amvpDimInfo.rcValidRegion;
|
|
m_svpInfo.dwVPFlags |= DDVP_CROP;
|
|
|
|
// use the VBI height only if the hal is capable of streaming
|
|
// VBI on a seperate surface
|
|
if (m_vpCaps.dwCaps & DDVPCAPS_VBIANDVIDEOINDEPENDENT)
|
|
{
|
|
m_svpInfo.dwVBIHeight = m_VPDataInfo.amvpDimInfo.rcValidRegion.top;
|
|
}
|
|
} else {
|
|
m_svpInfo.dwVPFlags &= ~DDVP_CROP;
|
|
}
|
|
|
|
if (m_bVPSyncMaster) {
|
|
m_svpInfo.dwVPFlags |= DDVP_SYNCMASTER;
|
|
} else {
|
|
m_svpInfo.dwVPFlags &= ~DDVP_SYNCMASTER;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::CheckDDrawVPCaps
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::CheckDDrawVPCaps()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
BOOL bAlwaysColorkey;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::CheckDDrawVPCaps")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// Determine if we should always colorkey, or only when we need to.
|
|
// At issue is the fact that some overlays cannot colorkey and Y
|
|
// interpolate at the same time. If not, we will only colorkey when
|
|
// we have to.
|
|
m_sBandwidth.dwSize = sizeof(DDVIDEOPORTBANDWIDTH);
|
|
hr = m_pVideoPort->GetBandwidthInfo(&m_ddVPOutputVideoFormat,
|
|
m_lImageWidth, m_lImageHeight,
|
|
DDVPB_TYPE, &m_sBandwidth);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pVideoPort->GetBandwidthInfo FAILED, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (m_sBandwidth.dwCaps == DDVPBCAPS_SOURCE)
|
|
{
|
|
hr = m_pVideoPort->GetBandwidthInfo(&m_ddVPOutputVideoFormat,
|
|
m_lImageWidth, m_lImageHeight,
|
|
DDVPB_OVERLAY, &m_sBandwidth);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("m_pVideoPort->GetBandwidthInfo FAILED, hr = 0x%x"),
|
|
hr));
|
|
goto CleanUp;
|
|
}
|
|
// store the caps info in this struct itself
|
|
m_sBandwidth.dwCaps = DDVPBCAPS_SOURCE;
|
|
if (m_sBandwidth.dwYInterpAndColorkey < m_sBandwidth.dwYInterpolate &&
|
|
m_sBandwidth.dwYInterpAndColorkey < m_sBandwidth.dwColorkey)
|
|
{
|
|
bAlwaysColorkey = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bAlwaysColorkey = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_sBandwidth.dwCaps == DDVPBCAPS_DESTINATION);
|
|
|
|
|
|
DWORD dwImageHeight = m_lImageHeight;
|
|
if (m_fCaptureInterleaved) {
|
|
dwImageHeight /= 2;
|
|
}
|
|
|
|
hr = m_pVideoPort->GetBandwidthInfo(&m_ddVPOutputVideoFormat,
|
|
m_lImageWidth, dwImageHeight,
|
|
DDVPB_VIDEOPORT, &m_sBandwidth);
|
|
if (hr != DD_OK)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("GetBandwidthInfo FAILED, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
// store the caps info in this struct itself
|
|
m_sBandwidth.dwCaps = DDVPBCAPS_DESTINATION;
|
|
if (m_sBandwidth.dwYInterpAndColorkey > m_sBandwidth.dwYInterpolate &&
|
|
m_sBandwidth.dwYInterpAndColorkey > m_sBandwidth.dwColorkey)
|
|
{
|
|
bAlwaysColorkey = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bAlwaysColorkey = TRUE;
|
|
}
|
|
}
|
|
|
|
// determine the decimation properties in the x direction
|
|
|
|
// Data can be arbitrarily shrunk
|
|
if (m_vpCaps.dwFX & DDVPFX_PRESHRINKX) {
|
|
m_DecimationModeX = DECIMATE_ARB;
|
|
}
|
|
|
|
// Data can be shrunk in increments of 1/x in the X direction
|
|
// (where x is specified in the DDVIDEOPORTCAPS.dwPreshrinkXStep
|
|
else if (m_vpCaps.dwFX & DDVPFX_PRESHRINKXS) {
|
|
|
|
m_DecimationModeX = DECIMATE_INC;
|
|
m_ulDeciStepX = m_vpCaps.dwPreshrinkXStep;
|
|
|
|
DbgLog((LOG_TRACE, 1,
|
|
TEXT("preshrink X increment %d"), m_vpCaps.dwPreshrinkXStep));
|
|
}
|
|
|
|
// Data can be binary shrunk (1/2, 1/4, 1/8, etc.)
|
|
else if (m_vpCaps.dwFX & DDVPFX_PRESHRINKXB) {
|
|
|
|
m_DecimationModeX = DECIMATE_BIN;
|
|
}
|
|
|
|
// no scaling at all supported !!
|
|
else {
|
|
|
|
m_DecimationModeX = DECIMATE_NONE;
|
|
}
|
|
|
|
// determine the decimation properties in the y direction
|
|
|
|
// Data can be arbitrarily shrunk
|
|
if (m_vpCaps.dwFX & DDVPFX_PRESHRINKY)
|
|
{
|
|
m_DecimationModeY = DECIMATE_ARB;
|
|
}
|
|
|
|
// Data can be shrunk in increments of 1/x in the Y direction
|
|
// (where x is specified in the DDVIDEOPORTCAPS.dwPreshrinkYStep
|
|
else if (m_vpCaps.dwFX & DDVPFX_PRESHRINKYS)
|
|
{
|
|
m_DecimationModeY = DECIMATE_INC;
|
|
m_ulDeciStepX = m_vpCaps.dwPreshrinkYStep;
|
|
}
|
|
|
|
// Data can be binary shrunk (1/2, 1/4, 1/8, etc.)
|
|
else if (m_vpCaps.dwFX & DDVPFX_PRESHRINKYB)
|
|
{
|
|
m_DecimationModeY = DECIMATE_BIN;
|
|
}
|
|
|
|
else {
|
|
m_DecimationModeY = DECIMATE_NONE;
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::DetermineModeRestrictions
|
|
*
|
|
* Determine if we can bob(interleaved/non), weave, or skip fields
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::DetermineModeRestrictions()
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::DetermineModeRestrictions")));
|
|
HRESULT hr = NOERROR;
|
|
const DDCAPS* pDirectCaps = NULL;
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
pDirectCaps = m_pIVideoPortControl->GetHardwareCaps();
|
|
ASSERT(pDirectCaps);
|
|
|
|
m_bCanWeave = FALSE;
|
|
m_bCanBobInterleaved = FALSE;
|
|
m_bCanBobNonInterleaved = FALSE;
|
|
m_bCanSkipOdd = FALSE;
|
|
m_bCanSkipEven = FALSE;
|
|
|
|
// this is just a policy. Don't weave interlaced content cause of
|
|
// motion artifacts
|
|
if ((!m_bVSInterlaced) &&
|
|
m_dwOutputSurfaceHeight >= m_lImageHeight * 2 &&
|
|
m_dwBackBufferCount > 0)
|
|
{
|
|
m_bCanWeave = TRUE;
|
|
DbgLog((LOG_TRACE, 1, TEXT("Can Weave")));
|
|
}
|
|
|
|
if (m_bVSInterlaced &&
|
|
m_dwOutputSurfaceHeight >= m_lImageHeight * 2 &&
|
|
pDirectCaps->dwCaps2 & DDCAPS2_CANBOBINTERLEAVED)
|
|
{
|
|
m_bCanBobInterleaved = TRUE;
|
|
DbgLog((LOG_TRACE, 1, TEXT("Can Bob Interleaved")));
|
|
}
|
|
|
|
if (m_bVSInterlaced &&
|
|
m_dwBackBufferCount > 0 &&
|
|
pDirectCaps->dwCaps2 & DDCAPS2_CANBOBNONINTERLEAVED)
|
|
{
|
|
m_bCanBobNonInterleaved = TRUE;
|
|
DbgLog((LOG_TRACE, 1, TEXT("Can Bob NonInterleaved")));
|
|
}
|
|
|
|
if (m_vpCaps.dwCaps & DDVPCAPS_SKIPODDFIELDS)
|
|
{
|
|
m_bCanSkipOdd = TRUE;
|
|
DbgLog((LOG_TRACE, 1, TEXT("Can Skip Odd")));
|
|
}
|
|
|
|
if (m_vpCaps.dwCaps & DDVPCAPS_SKIPEVENFIELDS)
|
|
{
|
|
m_bCanSkipEven = TRUE;
|
|
DbgLog((LOG_TRACE, 1, TEXT("Can Skip Even")));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::SetDDrawKernelHandles
|
|
*
|
|
* this function is used to inform the decoder of the various ddraw
|
|
* kernel handle using IVPConfig interface
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::SetDDrawKernelHandles()
|
|
{
|
|
HRESULT hr = NOERROR, hrFailure = NOERROR;
|
|
IDirectDrawKernel *pDDK = NULL;
|
|
IDirectDrawSurfaceKernel *pDDSK = NULL;
|
|
DWORD *pdwKernelHandleCount = 0;
|
|
DWORD dwCount = 0;
|
|
ULONG_PTR dwDDKernelHandle = 0;
|
|
LPDIRECTDRAW7 pDirectDraw = NULL;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::SetDDrawKernelHandles")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
ASSERT(pDirectDraw);
|
|
|
|
// get the IDirectDrawKernel interface
|
|
hr = 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(&dwDDKernelHandle);
|
|
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(dwDDKernelHandle);
|
|
hr = m_pIVPConfig->SetDirectDrawKernelHandle(dwDDKernelHandle);
|
|
if (FAILED(hr))
|
|
{
|
|
hrFailure = 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;
|
|
}
|
|
{
|
|
KernelHandleArray pArray( m_pOutputSurface, hr );
|
|
|
|
if( SUCCEEDED( hr )) {
|
|
// set the kernel handle to the overlay surface using IVPConfig
|
|
ASSERT(m_pIVPConfig);
|
|
hr = m_pIVPConfig->SetDDSurfaceKernelHandles( pArray.GetCount(), pArray.GetHandles() );
|
|
if (FAILED(hr))
|
|
{
|
|
hrFailure = hr;
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("IVPConfig::SetDirectDrawKernelHandles failed,")
|
|
TEXT(" hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
}
|
|
CleanUp:
|
|
// release the kernel ddraw handle
|
|
RELEASE (pDDK);
|
|
return hrFailure;
|
|
}
|
|
|
|
/*****************************Private*Routine******************************\
|
|
* CVideoPortObj::SetUpMode
|
|
*
|
|
* This function is designed to be called everytime on an update-overlay call
|
|
* not just when the mode changes. This is basically to keep the code simple.
|
|
* Certain functions are supposed to be called in sequence,
|
|
* (SetUpMode, followedby AdjustSourceSize followedby SetDisplayRects).
|
|
* I just call them all everytime, eventhough it is possible to optimize on
|
|
* that. The logic is that since UpdateOverlay is so expensive, this is no
|
|
* performance hit.
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
HRESULT CVideoPortObj::SetUpMode( AMVP_MODE mode )
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::SetUpMode")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
switch( mode ) {
|
|
case AMVP_MODE_WEAVE:
|
|
case AMVP_MODE_BOBINTERLEAVED:
|
|
case AMVP_MODE_BOBNONINTERLEAVED:
|
|
case AMVP_MODE_SKIPODD:
|
|
case AMVP_MODE_SKIPEVEN:
|
|
break;
|
|
default:
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetUpMode failed, mode value not valid, mode = %d"),
|
|
mode));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (mode == AMVP_MODE_WEAVE && !m_bCanWeave)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetUpMode failed, Can't do mode AMVP_MODE_WEAVE")));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
if (mode == AMVP_MODE_BOBINTERLEAVED && !m_bCanBobInterleaved)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetUpMode failed, Can't do mode AMVP_MODE_BOBINTERLEAVED")));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
if (mode == AMVP_MODE_BOBNONINTERLEAVED && !m_bCanBobNonInterleaved)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetUpMode failed, Can't do mode AMVP_MODE_BOBNONINTERLEAVED")));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
if (mode == AMVP_MODE_SKIPODD && !m_bCanSkipOdd)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetUpMode failed, Can't do mode AMVP_MODE_SKIPODD")));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
if (mode == AMVP_MODE_SKIPEVEN && !m_bCanSkipEven)
|
|
{
|
|
DbgLog((LOG_ERROR, 0,
|
|
TEXT("SetUpMode failed, Can't do mode AMVP_MODE_SKIPEVEN")));
|
|
hr = E_FAIL;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// Determine if we should interleave this or not.
|
|
// If we are doing weave, we certainly need to interleave.
|
|
// Bob doesn't really care one way or the other (since it only
|
|
// displays one field at a time), but interleaved makes it much
|
|
// easier to switch from bob to weave.
|
|
if (mode == AMVP_MODE_BOBINTERLEAVED ||
|
|
mode == AMVP_MODE_WEAVE)
|
|
{
|
|
m_svpInfo.dwVPFlags |= DDVP_INTERLEAVE;
|
|
|
|
DbgLog((LOG_TRACE, 3, TEXT("Setting VPflag interleaved")));
|
|
m_fHalfHeightVideo = false;
|
|
}
|
|
else
|
|
{
|
|
m_svpInfo.dwVPFlags &= ~DDVP_INTERLEAVE;
|
|
m_fHalfHeightVideo = true;
|
|
// pWinInfo->SrcRect.top /= 2;
|
|
// pWinInfo->SrcRect.bottom /= 2;
|
|
}
|
|
|
|
// if there is a garbage line at the top, we must clip it.
|
|
// At this point the source rect is set up for a frame, so increment by 2
|
|
// since we incremented the cropping rect height by 1, decrement the bottom
|
|
// as well
|
|
if (m_fGarbageLine)
|
|
{
|
|
// Done in blit
|
|
//pWinInfo->SrcRect.top += 1;
|
|
//pWinInfo->SrcRect.bottom -= 1;
|
|
DbgLog((LOG_TRACE, 3,
|
|
TEXT("m_fGarbageLine is TRUE, incrementing SrcRect.top")));
|
|
}
|
|
|
|
if (mode == AMVP_MODE_SKIPODD)
|
|
{
|
|
m_svpInfo.dwVPFlags |= DDVP_SKIPODDFIELDS;
|
|
DbgLog((LOG_TRACE, 3, TEXT("Setting VPflag SkipOddFields")));
|
|
}
|
|
else
|
|
{
|
|
m_svpInfo.dwVPFlags &= ~DDVP_SKIPODDFIELDS;
|
|
}
|
|
|
|
if (mode == AMVP_MODE_SKIPEVEN)
|
|
{
|
|
m_svpInfo.dwVPFlags |= DDVP_SKIPEVENFIELDS;
|
|
DbgLog((LOG_TRACE, 3, TEXT("Setting VPflag SkipEvenFields")));
|
|
}
|
|
else
|
|
{
|
|
m_svpInfo.dwVPFlags &= ~DDVP_SKIPEVENFIELDS;
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::RenegotiateVPParameters
|
|
*
|
|
* this function is used to redo the whole videoport connect process,
|
|
* while the graph maybe be running.
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::RenegotiateVPParameters()
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
VPInfoState vpOldState;
|
|
|
|
AMTRACE((TEXT("CVideoPortObj::RenegotiateVPParameters")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// don't return an error code if not connected
|
|
if (!m_bConnected)
|
|
{
|
|
hr = NOERROR;
|
|
goto CleanUp;
|
|
}
|
|
|
|
LPDIRECTDRAW7 pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
if( pDirectDraw ) {
|
|
if( pDirectDraw->TestCooperativeLevel() != DD_OK ) {
|
|
// Don't alter the videoport while in exclusive mode, otherwise
|
|
// the DXG kernel layer drifts out of sync with DDraw
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// store the old state, we will need to restore it later
|
|
vpOldState = m_VPState;
|
|
|
|
if (m_VPState == VPInfoState_RUNNING)
|
|
{
|
|
m_VPState = VPInfoState_STOPPED;
|
|
}
|
|
|
|
// release everything except IVPConfig
|
|
hr = StopUsingVideoPort();
|
|
|
|
|
|
// redo the connection process
|
|
hr = SetupVideoPort();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0, TEXT("CompleteConnect failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
// also notifies VPMThread about new VP & surfaces
|
|
hr = AttachVideoPortToSurface();
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0, TEXT("AttachVideoPortToSurface failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
// if the video was previously running, make sure that a frame is
|
|
// visible by making an update overlay call
|
|
if (vpOldState == VPInfoState_RUNNING)
|
|
{
|
|
m_bStart = TRUE;
|
|
|
|
hr = m_pIVideoPortControl->StartVideo();
|
|
|
|
ASSERT( SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0,
|
|
TEXT("Start video failed failed, hr = 0x%x"), hr));
|
|
goto CleanUp;
|
|
}
|
|
|
|
#if 0 // hack to get the Rage128 playing video again, probably the software autoflipping
|
|
// is broken. After a aspect ratio change or res mode change, the autoflipping doesn't start up again
|
|
hr = m_pVideoPort->StopVideo();
|
|
|
|
// ATI seems to want another set of stop/start's to actually start
|
|
// autoflipping again...
|
|
m_bStart = TRUE;
|
|
hr = m_pIVideoPortControl->StartVideo();
|
|
#endif
|
|
|
|
m_VPState = VPInfoState_RUNNING;
|
|
}
|
|
// send a dynamic reconnect to the downstream filter
|
|
|
|
if( SUCCEEDED( hr )) {
|
|
hr = SignalNewVP();
|
|
}
|
|
|
|
CleanUp:
|
|
if (FAILED(hr))
|
|
{
|
|
hr = VFW_E_VP_NEGOTIATION_FAILED;
|
|
BreakConnect(TRUE);
|
|
|
|
m_pIVideoPortControl->EventNotify(EC_COMPLETE, S_OK, 0);
|
|
m_pIVideoPortControl->EventNotify(EC_ERRORABORT, hr, 0);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::SetDeinterlaceMode
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::SetDeinterlaceMode(AMVP_MODE mode)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::SetMode")));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::GetDeinterlaceMode
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::GetDeinterlaceMode(AMVP_MODE *pMode)
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::GetMode")));
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::SetVPSyncMaster
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::SetVPSyncMaster(BOOL bVPSyncMaster)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
AMTRACE((TEXT("CVideoPortObj::SetVPSyncMaster")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
// if value has not changed, no need to do anything
|
|
if (m_bVPSyncMaster != bVPSyncMaster)
|
|
{
|
|
// store the new value
|
|
m_bVPSyncMaster = bVPSyncMaster;
|
|
|
|
// if not connected, connection process will take care of updating the
|
|
// m_svpInfo struct
|
|
if (!m_bConnected)
|
|
goto CleanUp;
|
|
|
|
// update the m_svpInfo struct
|
|
if (m_bVPSyncMaster) {
|
|
m_svpInfo.dwVPFlags |= DDVP_SYNCMASTER;
|
|
}
|
|
else {
|
|
m_svpInfo.dwVPFlags &= ~DDVP_SYNCMASTER;
|
|
}
|
|
|
|
// if video is stopped currently, no need to do anything else
|
|
if (m_VPState == VPInfoState_STOPPED)
|
|
goto CleanUp;
|
|
|
|
// Call UpdateVideo to make sure the change is reflected immediately
|
|
ASSERT( m_svpInfo.dwVPFlags & DDVP_AUTOFLIP );
|
|
hr = m_pVideoPort->UpdateVideo(&m_svpInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR,0, TEXT("UpdateVideo failed, hr = 0x%x"), hr));
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
return hr;
|
|
}
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::GetVPSyncMaster
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - StEstrop - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::GetVPSyncMaster(BOOL *pbVPSyncMaster)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
AMTRACE((TEXT("CVideoPortObj::SetVPSyncMaster")));
|
|
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
if (pbVPSyncMaster) {
|
|
*pbVPSyncMaster = m_bVPSyncMaster;
|
|
}
|
|
else {
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::GetVPSyncMaster
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - GlennE - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::GetAllOutputFormats( const PixelFormatList** ppList )
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::GetAllOutputFormats")));
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
*ppList = &m_ddAllOutputVideoFormats;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CVideoPortObj::GetOutputFormat( DDPIXELFORMAT* pFormat )
|
|
{
|
|
AMTRACE((TEXT("CVideoPortObj::GetOutputFormat")));
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
|
|
*pFormat = m_ddVPOutputVideoFormat;
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* CVideoPortObj::SetVideoPortID
|
|
*
|
|
*
|
|
*
|
|
* History:
|
|
* Thu 09/09/1999 - GlennE - Added this comment and cleaned up the code
|
|
*
|
|
\**************************************************************************/
|
|
STDMETHODIMP CVideoPortObj::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_pDVP ) {
|
|
hr = VPMUtil::FindVideoPortCaps( m_pDVP, NULL, m_dwVideoPortId );
|
|
} else {
|
|
LPDIRECTDRAW7 pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
hr = VPMUtil::FindVideoPortCaps( pDirectDraw, NULL, m_dwVideoPortId );
|
|
}
|
|
if( hr == S_OK) {
|
|
m_dwVideoPortId = dwVideoPortId;
|
|
} else if( hr == S_FALSE ) {
|
|
return E_INVALIDARG;
|
|
}// else fail
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT GetRectFromImage( LPDIRECTDRAWSURFACE7 pSurf, RECT* pRect )
|
|
{
|
|
// assume entire dest for now ....
|
|
DDSURFACEDESC2 ddsd;
|
|
INITDDSTRUCT( ddsd );
|
|
HRESULT hr = pSurf->GetSurfaceDesc( &ddsd );
|
|
if ( SUCCEEDED(hr) ) {
|
|
pRect->left = 0;
|
|
pRect->top = 0;
|
|
pRect->right = ddsd.dwWidth;
|
|
pRect->bottom = ddsd.dwHeight;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// #define DEBUG_BLTS
|
|
#endif
|
|
|
|
#ifdef DEBUG_BLTS
|
|
static BYTE Clamp(float clr)
|
|
{
|
|
if (clr < 0.0f) {
|
|
return (BYTE)0;
|
|
} else if (clr > 255.0f) {
|
|
return (BYTE)255;
|
|
} else {
|
|
return (BYTE)clr;
|
|
}
|
|
}
|
|
|
|
static RGBQUAD
|
|
ConvertYCrCbToRGB(
|
|
int y,
|
|
int cr,
|
|
int cb
|
|
)
|
|
{
|
|
RGBQUAD rgbq;
|
|
|
|
float r = (1.1644f * (y-16)) + (1.5960f * (cr-128));
|
|
float g = (1.1644f * (y-16)) - (0.8150f * (cr-128)) - (0.3912f * (cb-128));
|
|
float b = (1.1644f * (y-16)) + (2.0140f * (cb-128));
|
|
|
|
|
|
rgbq.rgbBlue = Clamp(b);
|
|
rgbq.rgbGreen = Clamp(g);
|
|
rgbq.rgbRed = Clamp(r);
|
|
rgbq.rgbReserved = 0; // Alpha
|
|
|
|
return rgbq;
|
|
}
|
|
|
|
static void MyCopyBlt( const DDSURFACEDESC2& ddsdS, const RECT* pSrcRect,
|
|
const DDSURFACEDESC2& ddsdT, const RECT* pDestRect, const UINT pixelSize )
|
|
{
|
|
const LONG srcPitch = ddsdS.lPitch;
|
|
const LONG destPitch = ddsdT.lPitch;
|
|
|
|
const BYTE* pSrc = (BYTE *)ddsdS.lpSurface + pSrcRect->left * pixelSize;
|
|
BYTE* pDest = (BYTE *)ddsdT.lpSurface + pDestRect->left * pixelSize;
|
|
|
|
const UINT LineLength = (pSrcRect->right - pSrcRect->left) * pixelSize;
|
|
|
|
for( INT y=pSrcRect->top; y < pSrcRect->bottom; y++ ) {
|
|
CopyMemory( pDest + y * destPitch, pSrc + y * srcPitch, LineLength );
|
|
}
|
|
|
|
}
|
|
|
|
static void CopyYUY2LineToRGBA( BYTE* pDest, const BYTE* pSrc, UINT width )
|
|
{
|
|
while( width > 0 ) {
|
|
int y0 = (int)pSrc[0];
|
|
int cb = (int)pSrc[1];
|
|
int y1 = (int)pSrc[2];
|
|
int cr = (int)pSrc[3];
|
|
|
|
pSrc += 4;
|
|
|
|
RGBQUAD r = ConvertYCrCbToRGB(y0, cr, cb);
|
|
pDest[0] = r.rgbBlue;
|
|
pDest[1] = r.rgbGreen;
|
|
pDest[2] = r.rgbRed;
|
|
pDest[3] = 0; // Alpha
|
|
|
|
pDest +=4;
|
|
|
|
width--;
|
|
if( width > 0 ) {
|
|
pDest[0] = r.rgbBlue;
|
|
pDest[1] = r.rgbGreen;
|
|
pDest[2] = r.rgbRed;
|
|
pDest[3] = 0; // Alpha
|
|
|
|
pDest +=4;
|
|
width--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MyCopyYUY2ToRGBA( const DDSURFACEDESC2& ddsdS, const RECT* pSrcRect,
|
|
const DDSURFACEDESC2& ddsdT, const RECT* pDestRect )
|
|
{
|
|
const LONG srcPitch = ddsdS.lPitch;
|
|
const LONG destPitch = ddsdT.lPitch;
|
|
|
|
ASSERT( (pSrcRect->left & 1) == 0 ); // can only convert on even edges for now
|
|
ASSERT( (pSrcRect->right & 1) == 0 ); // can only convert on even edges for now
|
|
|
|
const BYTE* pSrc = (BYTE *)ddsdS.lpSurface + pSrcRect->left * 2;
|
|
BYTE* pDest = (BYTE *)ddsdT.lpSurface + pDestRect->left * 4;
|
|
|
|
const UINT LineWidth = (pSrcRect->right - pSrcRect->left);
|
|
|
|
for( INT y=pSrcRect->top; y < pSrcRect->bottom; y++ ) {
|
|
CopyYUY2LineToRGBA( pDest + y * destPitch, pSrc + y * srcPitch, LineWidth );
|
|
}
|
|
|
|
}
|
|
|
|
static void MyCopyYUY2Blt( const DDSURFACEDESC2& ddsdS, const RECT* pSrcRect,
|
|
const DDSURFACEDESC2& ddsdT, const RECT* pDestRect )
|
|
{
|
|
MyCopyBlt( ddsdS, pSrcRect, ddsdT, pDestRect,2 );
|
|
}
|
|
|
|
static void MyCopyUYVYBlt( const DDSURFACEDESC2& ddsdS, const RECT* pSrcRect,
|
|
const DDSURFACEDESC2& ddsdT, const RECT* pDestRect )
|
|
{
|
|
MyCopyBlt( ddsdS, pSrcRect, ddsdT, pDestRect,2 );
|
|
}
|
|
|
|
// handy debugging routine to test faulty UYVY blits
|
|
static HRESULT MyCopyUYVYSurf( LPDIRECTDRAWSURFACE7 pDestSurf, const RECT* pDestRect, LPDIRECTDRAWSURFACE7 pSrcSurf, const RECT* pSrcRect )
|
|
{
|
|
DDSURFACEDESC2 ddsdS = {sizeof(ddsdS)};
|
|
DDSURFACEDESC2 ddsdT = {sizeof(ddsdT)};
|
|
|
|
HRESULT hr = pSrcSurf->Lock(NULL, &ddsdS, DDLOCK_NOSYSLOCK, NULL);
|
|
ASSERT( SUCCEEDED( hr));
|
|
if (hr != DD_OK) {
|
|
return hr;
|
|
}
|
|
|
|
hr = pDestSurf->Lock(NULL, &ddsdT, DDLOCK_NOSYSLOCK, NULL);
|
|
ASSERT( SUCCEEDED( hr));
|
|
if (hr != DD_OK) {
|
|
pSrcSurf->Unlock(NULL);
|
|
return hr;
|
|
}
|
|
|
|
ASSERT( WIDTH( pSrcRect ) == WIDTH( pDestRect) );
|
|
ASSERT( HEIGHT( pSrcRect ) == HEIGHT( pDestRect) );
|
|
|
|
// we should not do conversions in the VPM, let the VMR do the work
|
|
ASSERT( ddsdS.ddpfPixelFormat.dwFourCC == ddsdT.ddpfPixelFormat.dwFourCC );
|
|
|
|
if( ddsdS.ddpfPixelFormat.dwFourCC == MAKEFOURCC('U', 'Y', 'V', 'Y' ) &&
|
|
ddsdT.ddpfPixelFormat.dwFourCC == MAKEFOURCC('U', 'Y', 'V', 'Y' ) ) {
|
|
MyCopyUYVYBlt( ddsdS, pSrcRect, ddsdT, pDestRect );
|
|
} else
|
|
if( ddsdS.ddpfPixelFormat.dwFourCC == MAKEFOURCC('Y', 'U', 'Y', '2' ) &&
|
|
ddsdT.ddpfPixelFormat.dwFourCC == MAKEFOURCC('Y', 'U', 'Y', '2' ) ) {
|
|
MyCopyYUY2Blt( ddsdS, pSrcRect, ddsdT, pDestRect );
|
|
} else {
|
|
// if( ddsdS.ddpfPixelFormat.dwFourCC == MAKEFOURCC('Y', 'U', 'Y', '2' ) &&
|
|
// ddsdT.ddpfPixelFormat.dwFourCC == 0 ) {
|
|
// MyCopyYUY2ToRGBA( ddsdS, pSrcRect, ddsdT, pDestRect );
|
|
// } else {
|
|
ASSERT( !"Can't handle MyBlt format" );
|
|
}
|
|
|
|
pSrcSurf->Unlock(NULL);
|
|
pDestSurf->Unlock(NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
static LPDIRECTDRAW7 GetDDrawFromSurface( LPDIRECTDRAWSURFACE7 pDestSurface )
|
|
{
|
|
IUnknown *pDDrawObjUnk ;
|
|
HRESULT hr = pDestSurface->GetDDInterface((LPVOID*)&pDDrawObjUnk) ;
|
|
if (SUCCEEDED(hr) ) {
|
|
LPDIRECTDRAW7 pDDObj;
|
|
hr = pDDrawObjUnk->QueryInterface(IID_IDirectDraw7, (LPVOID *) &pDDObj);
|
|
pDDrawObjUnk->Release();
|
|
if( SUCCEEDED( hr )) {
|
|
return pDDObj;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT CVideoPortObj::CallUpdateSurface( DWORD dwSourceIndex, LPDIRECTDRAWSURFACE7 pDestSurface )
|
|
{
|
|
if ( dwSourceIndex > m_dwBackBufferCount ) {
|
|
ASSERT( !"Invalid source index" );
|
|
return E_INVALIDARG;
|
|
}
|
|
//Debug: use the previous surface
|
|
// DWORD dwNumSurfaces= m_dwBackBufferCount+1;
|
|
// dwSourceIndex = (dwNumSurfaces+dwSourceIndex-1) % dwNumSurfaces;
|
|
|
|
ASSERT( m_pChain );
|
|
LPDIRECTDRAWSURFACE7 pSourceSurface = m_pChain[dwSourceIndex].pDDSurf;
|
|
|
|
// if we fail at this point, something is really wrong
|
|
ASSERT( pDestSurface );
|
|
ASSERT( pSourceSurface );
|
|
|
|
if( !pSourceSurface || !pDestSurface ) {
|
|
return E_FAIL;
|
|
}
|
|
// gather stats to verify distribution of surfaces
|
|
m_pChain[dwSourceIndex].dwCount++;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
RECT rSrc = m_VPDataInfo.amvpDimInfo.rcValidRegion;
|
|
|
|
if( m_CropState == VPInfoCropState_AtVideoPort ) {
|
|
// if cropping at the videoport, final image is translated back to (0,0)
|
|
rSrc.right = WIDTH( &rSrc );
|
|
rSrc.bottom = HEIGHT( &rSrc );
|
|
rSrc.left = 0;
|
|
rSrc.top = 0;
|
|
}
|
|
if( m_fGarbageLine ) {
|
|
// crop top line
|
|
rSrc.top ++;
|
|
rSrc.bottom --;
|
|
}
|
|
if( !m_fHalfHeightVideo ) {
|
|
// Bob interleaved or weave, so grab both fields (rcValidRegion is 0..240)
|
|
rSrc.top *=2;
|
|
rSrc.bottom *=2;
|
|
}
|
|
// Could watch the media type, however this is more reliable.
|
|
#ifdef DEBUG
|
|
// Make sure the source fits into the destination
|
|
{
|
|
RECT rDest;
|
|
hr = GetRectFromImage( pDestSurface, &rDest );
|
|
if( SUCCEEDED( hr )) {
|
|
ASSERT( rDest.bottom >= rSrc.bottom );
|
|
ASSERT( rDest.right >= rSrc.right );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
RECT rDest = rSrc;
|
|
|
|
|
|
#ifdef DEBUG_BLTS
|
|
// debugging to track down faulty BltFourCC blits
|
|
hr = MyCopyUYVYSurf( pDestSurface, &rDest, pSourceSurface, &rSrc );
|
|
#else
|
|
hr = pDestSurface->Blt(&rDest, pSourceSurface, &rSrc, DDBLT_WAIT, NULL);
|
|
#endif
|
|
// retry on lost surface
|
|
if ( DDERR_SURFACELOST == hr )
|
|
{
|
|
LPDIRECTDRAW7 pDirectDraw = m_pIVideoPortControl->GetDirectDraw();
|
|
if( pDirectDraw && pDirectDraw->TestCooperativeLevel() == DD_OK ) {
|
|
// otherwise the kernel dxg.sys is out of sync with DDraw
|
|
ASSERT( pDestSurface->IsLost() == DDERR_SURFACELOST || pSourceSurface->IsLost() == DDERR_SURFACELOST );
|
|
|
|
// check the destination. If we can't restore it, then we don't want to even both with the source
|
|
|
|
hr = pDestSurface->IsLost();
|
|
if( hr == DDERR_SURFACELOST ) {
|
|
// restore the DestSurface (passed to us, possibly a different DDrawObject)
|
|
// We can't just restore the surface since it could be an implicit surface that is part of a flipping
|
|
// chain, so we have to tell DDraw to restore everything on that thread
|
|
|
|
LPDIRECTDRAW7 pDestDirectDraw = GetDDrawFromSurface( pDestSurface );
|
|
if( pDestDirectDraw ) {
|
|
hr = pDestDirectDraw->RestoreAllSurfaces();
|
|
pDestDirectDraw->Release();
|
|
}
|
|
if( SUCCEEDED( hr )) {
|
|
hr = pDestSurface->IsLost();
|
|
}
|
|
}
|
|
|
|
if( hr != DDERR_SURFACELOST ) {
|
|
// valid destination, fix the source
|
|
hr = pSourceSurface->IsLost();
|
|
|
|
if( hr == DDERR_SURFACELOST ) {
|
|
hr = m_pOutputSurface->Restore();
|
|
if( FAILED( hr )) {
|
|
DbgLog((LOG_ERROR, 0, TEXT("CallUpdateSurface Blt() restore source failed, hr = %d"), hr & 0xffff));
|
|
} else {
|
|
// kick the videoport (G400 seems to stop playing)
|
|
|
|
// the surfaces are disconnected from the video port when they are lost, so reconnect them
|
|
hr = StartVideoWithRetry();
|
|
}
|
|
if( SUCCEEDED( hr )) {
|
|
// recompute the source image pointer incase StartVideoWithRetry recreated the surfaces
|
|
pSourceSurface = m_pChain[dwSourceIndex].pDDSurf;
|
|
}
|
|
}
|
|
if( SUCCEEDED( hr ) ) {
|
|
hr = pDestSurface->Blt(&rDest,
|
|
pSourceSurface, &rSrc,
|
|
DDBLT_WAIT, NULL);
|
|
}
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
// HRESULT coop= pDirectDraw ? pDirectDraw->TestCooperativeLevel() : E_FAIL;
|
|
// DbgLog((LOG_ERROR, 0, TEXT("TestCoopLevel failed, hr = %d"), coop & 0xffff));
|
|
#endif
|
|
}
|
|
} else {
|
|
ASSERT( SUCCEEDED( hr ));
|
|
}
|
|
// filter DERR_SURFACELOST since in DOS boxes, we'll continually fail the blit
|
|
if (DDERR_SURFACELOST != hr && FAILED(hr))
|
|
{
|
|
DbgLog((LOG_ERROR, 0, TEXT("CallUpdateSurface Blt() failed, hr = %d"), hr & 0xffff));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CVideoPortObj::GetMode( AMVP_MODE* pMode )
|
|
{
|
|
*pMode = m_CurrentMode;
|
|
return S_OK;
|
|
}
|
|
|
|
//==========================================================================
|
|
HRESULT CVideoPortObj::GetMediaType(int iPosition, CMediaType* pmt)
|
|
{
|
|
CAutoLock cObjectLock(m_pMainObjLock);
|
|
AMTRACE((TEXT("CVideoPortObj::GetMediaType")));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (iPosition == 0)
|
|
{
|
|
pmt->SetType(&MEDIATYPE_Video);
|
|
pmt->SetSubtype(&MEDIASUBTYPE_VPVideo);
|
|
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;
|
|
}
|