// Copyright (c) 1998 - 1999 Microsoft Corporation. All Rights Reserved. #include #include #include #include #include #include // // Flipping surface implementation // // To allow decoders to hold on to surfaces for out of order decode // we flip directly to the surface pass on Receive rather than // use the default NULL target surface for Flip(). // // This works in the following way // // The COMPinputPin::m_pDirectDrawSurface points to the FRONT buffer // // When Receive is called we Flip() the front buffer and because we // do an explicit Flip() DirectDraw swaps the memory pointers for the // current Front buffer and the surface passed in which is then attached // to the front buffer. // // The received buffer is then put at the back of the queue so (correctly) // the previous front buffer is now at the back of the queue to be handed // to the application // // The allocator actually has one more buffer than was actually requested // so the previous front buffer won't actually be requested until the next // Receive and hence the previous Flip() has time to complete. // // Video accelerator disable interface /////////////////////////////////////////// // CLASS CDDrawMediaSample implemented here /////////////////////////////////////////// // constructor CDDrawMediaSample::CDDrawMediaSample(TCHAR *pName, CBaseAllocator *pAllocator, HRESULT *phr, LPBYTE pBuffer, LONG length, bool bKernelFlip) : CMediaSample(pName, pAllocator, phr, pBuffer, length) { AMTRACE((TEXT("CDDrawMediaSample::Constructor"))); m_pDirectDrawSurface = NULL; m_dwDDrawSampleSize = 0; m_bSurfaceLocked = FALSE; m_bKernelLock = bKernelFlip; SetRect(&m_SurfaceRect, 0, 0, 0, 0); memset(&m_DibData, 0, sizeof(DIBDATA)); m_bInit = FALSE; return; } // destructor CDDrawMediaSample::~CDDrawMediaSample(void) { AMTRACE((TEXT("CDDrawMediaSample::Destructor"))); if (m_pDirectDrawSurface) { __try { m_pDirectDrawSurface->Release() ; // release surface now } __except(EXCEPTION_EXECUTE_HANDLER) { ; } m_pDirectDrawSurface = NULL; } if (m_bInit) { if (m_DibData.hBitmap) { EXECUTE_ASSERT(DeleteObject(m_DibData.hBitmap)); } if (m_DibData.hMapping) { EXECUTE_ASSERT(CloseHandle(m_DibData.hMapping)); } } return; } HRESULT CDDrawMediaSample::SetDDrawSampleSize(DWORD dwDDrawSampleSize) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::SetDDrawSampleSize"))); m_dwDDrawSampleSize = dwDDrawSampleSize; return hr; } HRESULT CDDrawMediaSample::GetDDrawSampleSize(DWORD *pdwDDrawSampleSize) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::SetDDrawSampleSize"))); if (!pdwDDrawSampleSize) { DbgLog((LOG_ERROR, 1, TEXT("Bad Arguments, pdwDDrawSampleSize = NULL"))); hr = E_POINTER; goto CleanUp; } *pdwDDrawSampleSize = m_dwDDrawSampleSize; CleanUp: return hr; } HRESULT CDDrawMediaSample::SetDDrawSurface(LPDIRECTDRAWSURFACE7 pDirectDrawSurface) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::SetDDrawSampleSize"))); if (pDirectDrawSurface) // only if new surface is not NULL... pDirectDrawSurface->AddRef() ; // ...add a ref count on it if (m_pDirectDrawSurface) // if there was a surface already... m_pDirectDrawSurface->Release() ; // ... then release it now m_pDirectDrawSurface = pDirectDrawSurface; return hr; } HRESULT CDDrawMediaSample::GetDDrawSurface(LPDIRECTDRAWSURFACE7 *ppDirectDrawSurface) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::SetDDrawSampleSize"))); if (!ppDirectDrawSurface) { DbgLog((LOG_ERROR, 1, TEXT("Bad Arguments, ppDirectDrawSurface = NULL"))); hr = E_INVALIDARG; goto CleanUp; } *ppDirectDrawSurface = m_pDirectDrawSurface; CleanUp: return hr; } // overridden to expose IDirectDrawMediaSample STDMETHODIMP CDDrawMediaSample::QueryInterface(REFIID riid, void **ppv) { HRESULT hr = NOERROR; AMTRACE((TEXT("CDDrawMediaSample::QueryInterface"))); if (riid == IID_IDirectDrawMediaSample && m_pDirectDrawSurface) { hr = GetInterface(static_cast(this), ppv); #ifdef DEBUG if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("GetInterface(IDirectDrawMediaSample*) failed, hr = 0x%x"), hr)); } #endif } else { hr = CMediaSample::QueryInterface(riid, ppv); #ifdef DEBUG if (FAILED(hr)) { DbgLog((LOG_ERROR, 2, TEXT("CUnknown::NonDelegatingQueryInterface failed, hr = 0x%x"), hr)); } #endif } return hr; } // Implement IDirectDrawMediaSample STDMETHODIMP CDDrawMediaSample::GetSurfaceAndReleaseLock(IDirectDrawSurface **ppDirectDrawSurface, RECT* pRect) { HRESULT hr = NOERROR; BYTE *pBufferPtr; AMTRACE((TEXT("CDDrawMediaSample::GetSurfaceAndReleaseLock"))); // make sure the surface is locked if (!m_bSurfaceLocked) { DbgLog((LOG_ERROR, 4, TEXT("m_bSurfaceLocked is FALSE, can't unlock surface twice, returning E_UNEXPECTED"))); goto CleanUp; } // make sure you have a direct draw surface pointer if (!m_pDirectDrawSurface) { DbgLog((LOG_ERROR, 1, TEXT("m_pDirectDrawSurface is NULL, returning E_FAIL"))); hr = E_FAIL; goto CleanUp; } hr = GetPointer(&pBufferPtr); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("GetPointer() failed, hr = 0x%x"), hr)); goto CleanUp; } ASSERT(m_pDirectDrawSurface); hr = m_pDirectDrawSurface->Unlock(NULL); // TBD: was (LPVOID)pBufferPtr); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pDirectDrawSurface->Unlock failed, hr = 0x%x"), hr)); goto CleanUp; } // Can't do this to make the 829/848 work with the ovmixer. The reason is that those // drivers unlock the surface just after GetBuffer (to avoid the win16 lock), however // there is a bunch of code in the proxy which ASSERTS for a valid pointer value /* // update the pointer value, however keep the SampleSize around hr = SetPointer(NULL, 0); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("SetPointer() failed, hr = 0x%x"), hr)); goto CleanUp; } */ if (ppDirectDrawSurface) { hr = m_pDirectDrawSurface->QueryInterface( IID_IDirectDrawSurface7, (VOID**)ppDirectDrawSurface ); if( FAILED( hr )) { ASSERT( FALSE ); *ppDirectDrawSurface = NULL; } } else if (pRect) { *pRect = m_SurfaceRect; } m_bSurfaceLocked = FALSE; CleanUp: return hr; } STDMETHODIMP CDDrawMediaSample::LockMediaSamplePointer(void) { HRESULT hr = NOERROR; DWORD dwDDrawSampleSize = 0; DDSURFACEDESC2 ddSurfaceDesc; DWORD dwLockFlags = DDLOCK_WAIT; AMTRACE((TEXT("CDDrawMediaSample::LockMediaSamplePointer"))); // make sure the surface is locked if (m_bSurfaceLocked) { DbgLog((LOG_ERROR, 1, TEXT("m_bSurfaceLocked is TRUE, can't lock surface twice, returning E_UNEXPECTED"))); hr = E_UNEXPECTED; goto CleanUp; } // make sure you have a direct draw surface pointer if (!m_pDirectDrawSurface) { DbgLog((LOG_ERROR, 1, TEXT("m_pDirectDrawSurface is NULL, returning E_FAIL"))); hr = E_FAIL; goto CleanUp; } // set the dwSize of ddSurfaceDesc INITDDSTRUCT(ddSurfaceDesc); // lock the surface - no need to grab the win16 lock ASSERT(m_pDirectDrawSurface); // Using DDLOCK_NOSYSLOCK caused us to get DDERR_SURFACEBUSY on some of // our blts to the primary for painting the color key so we've // stopped using it for now. IDirectDrawSurface7 *pSurface7; if (m_bKernelLock && SUCCEEDED(m_pDirectDrawSurface->QueryInterface( IID_IDirectDrawSurface7, (void **)&pSurface7))) { pSurface7->Release(); dwLockFlags |= DDLOCK_NOSYSLOCK; } hr = m_pDirectDrawSurface->Lock( NULL, &ddSurfaceDesc, dwLockFlags, (HANDLE)NULL); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pDirectDrawSurface->Lock() failed, hr = 0x%x"), hr)); goto CleanUp; } hr = GetDDrawSampleSize(&dwDDrawSampleSize); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("GetDDrawSampleSize() failed, hr = 0x%x"), hr)); goto CleanUp; } ASSERT(dwDDrawSampleSize); // update the pointer value hr = SetPointer((BYTE*)ddSurfaceDesc.lpSurface, dwDDrawSampleSize); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("SetPointer() failed, hr = 0x%x"), hr)); goto CleanUp; } m_bSurfaceLocked = TRUE; CleanUp: return hr; } // Set the shared memory DIB information void CDDrawMediaSample::SetDIBData(DIBDATA *pDibData) { ASSERT(pDibData); m_DibData = *pDibData; m_pBuffer = m_DibData.pBase; m_cbBuffer = m_dwDDrawSampleSize; m_bInit = TRUE; } // Retrieve the shared memory DIB data DIBDATA *CDDrawMediaSample::GetDIBData() { ASSERT(m_bInit == TRUE); return &m_DibData; } /////////////////////////////////////////// // CLASS CVPMInputAllocator implemented here /////////////////////////////////////////// // constructor CVPMInputAllocator::CVPMInputAllocator(CVPMInputPin& pPin, HRESULT *phr) : CBaseAllocator(NAME("Video Allocator"), NULL, phr, TRUE, true) , m_pPin( pPin ) { AMTRACE((TEXT("CVPMInputAllocator::Constructor"))); // REVIEW don't overwrite a failure code from CBaseAllocator return; } // destructor CVPMInputAllocator::~CVPMInputAllocator() { AMTRACE((TEXT("CVPMInputAllocator::Destructor"))); } // Override this to publicise IDirectDrawMediaSampleAllocator STDMETHODIMP CVPMInputAllocator::NonDelegatingQueryInterface(REFIID riid, void **ppv) { HRESULT hr = E_NOINTERFACE; AMTRACE((TEXT("CVPMInputAllocator::NonDelegatingQueryInterface"))); CAutoLock cLock( &m_pPin.GetFilter().GetFilterLock() ); return hr; } STDMETHODIMP CVPMInputAllocator::SetProperties(ALLOCATOR_PROPERTIES* pRequest, ALLOCATOR_PROPERTIES* pActual) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::SetProperties"))); // call the base class hr = CBaseAllocator::SetProperties(pRequest, pActual); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseAllocator::SetProperties() failed, hr = 0x%x"), hr)); goto CleanUp; } // tell the pin hr = m_pPin.OnSetProperties(pRequest, pActual); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pPin.AllocateSurfaces() failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: return hr; } // called when we receive a sample HRESULT CVPMInputAllocator::GetBuffer(IMediaSample **ppSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime, DWORD dwFlags) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::GetBuffer"))); // call the base class IMediaSample *pSample = NULL; hr = CBaseAllocator::GetBuffer(&pSample,pStartTime,pEndTime,0); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseAllocator::GetBuffer() failed, hr = 0x%x"), hr)); goto CleanUp; } // tell the pin hr = m_pPin.OnGetBuffer(&pSample, pStartTime, pEndTime, dwFlags); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pPin.OnGetBuffer() failed, hr = 0x%x"), hr)); goto CleanUp; } CleanUp: { // REVIEW why lock? There are no variables in the allocato // accessed here CAutoLock cAllocatorLock(this); if (FAILED(hr)) { if (pSample) { pSample->Release(); } *ppSample = NULL; } else { ASSERT(pSample != NULL); *ppSample = pSample; } } return hr; } // called when the sample is released HRESULT CVPMInputAllocator::ReleaseBuffer(IMediaSample *pSample) { HRESULT hr = NOERROR; AMTRACE((TEXT("CVPMInputAllocator::ReleaseBuffer"))); // unlock the sample first hr = ((CDDrawMediaSample*)pSample)->GetSurfaceAndReleaseLock(NULL, NULL); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("pSample->GetSurfaceAndReleaseLock() failed, hr = 0x%x"), hr)); // goto CleanUp; // if this happens we still have to release the sample to the free list, so don't bail out } { CAutoLock cAllocatorLock(this); // Copy of base class code - put at end of the list { CheckPointer(pSample,E_POINTER); ValidateReadPtr(pSample,sizeof(IMediaSample)); BOOL bRelease = FALSE; { CAutoLock cal(this); /* Put back on the free list */ CMediaSample **ppTail; for (ppTail = &m_lFree.m_List; *ppTail; ppTail = &((CDDrawMediaSample *)(*ppTail))->Next()) { } *ppTail = (CMediaSample *)pSample; ((CDDrawMediaSample *)pSample)->Next() = NULL; m_lFree.m_nOnList++; if (m_lWaiting != 0) { NotifySample(); } // if there is a pending Decommit, then we need to complete it by // calling Free() when the last buffer is placed on the free list LONG l1 = m_lFree.GetCount(); if (m_bDecommitInProgress && (l1 == m_lAllocated)) { Free(); m_bDecommitInProgress = FALSE; bRelease = TRUE; } } if (m_pNotify) { m_pNotify->NotifyRelease(); } // For each buffer there is one AddRef, made in GetBuffer and released // here. This may cause the allocator and all samples to be deleted if (bRelease) { Release(); } } } return hr; } // allocate memory for the sample HRESULT CVPMInputAllocator::Alloc() { HRESULT hr = NOERROR; CDDrawMediaSample **ppSampleList = NULL; DWORD i; LONG lToAllocate; AMTRACE((TEXT("CVPMInputAllocator::Alloc"))); // call the base class hr = CBaseAllocator::Alloc(); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CBaseAllocator::Alloc() failed, hr = 0x%x"), hr)); goto CleanUp; } // allocate memory for pointers lToAllocate = (m_pPin.m_dwBackBufferCount > 1 && !m_pPin.m_bSyncOnFill && m_pPin.m_bCanOverAllocateBuffers) ? m_lCount + 1 : m_lCount; // allocate memory for an array of pointers ppSampleList = new CDDrawMediaSample *[lToAllocate]; if (!ppSampleList) { DbgLog((LOG_ERROR, 1, TEXT("new BYTE[m_lCount*sizeof(CDDrawMediaSample*)] failed"))); hr = E_OUTOFMEMORY; goto CleanUp; } for (i = 0; i < (DWORD)(lToAllocate); i++) { // Create a new sample ppSampleList[i] = new CDDrawMediaSample(NAME("Sample"), this, (HRESULT *) &hr, NULL, (LONG) 0, DDKERNELCAPS_LOCK & m_pPin.m_pVPMFilter.KernelCaps() ? true : false); // REVIEW - actually hr can't be a failure // so we don't need to delete ppSampleList[i] here if (FAILED(hr) || ppSampleList[i] == NULL) { if (SUCCEEDED(hr)) hr = E_OUTOFMEMORY; DbgLog((LOG_ERROR, 1, TEXT("new CDDrawMediaSample failed, hr = 0x%x"), hr)); goto CleanUp; } // this cannot fail m_lFree.Add(ppSampleList[i]); m_lAllocated++; } ASSERT(m_lAllocated == lToAllocate); // tell the pin hr = m_pPin.OnAlloc(ppSampleList, lToAllocate); if (FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("m_pPin.OnAlloc(), hr = 0x%x"), hr)); goto CleanUp; } CleanUp: delete[] ppSampleList; return hr; } void CVPMInputAllocator::Free(void) { CDDrawMediaSample *pSample; AMTRACE((TEXT("CVPMInputAllocator::Free"))); /* Should never be deleting this unless all buffers are freed */ // REVIEW - might not be true if we failed to allocate a sample ASSERT(m_lAllocated == m_lFree.GetCount()); /* Free up all the CMediaSamples */ for (;;) { pSample = (CDDrawMediaSample *) m_lFree.RemoveHead(); if (pSample != NULL) { delete pSample; } else { break; } } m_lAllocated = 0; return; }