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