windows-nt/Source/XPSP1/NT/multimedia/dshow/streams/ddstream/ddstrm.cpp
2020-09-26 16:20:57 +08:00

1177 lines
38 KiB
C++

// Copyright (c) 1997 - 1998 Microsoft Corporation. All Rights Reserved.
// DDStrm.cpp : Implementation of CDDStream
#include "stdafx.h"
#include "project.h"
//#define SHOWSURFACES
#ifdef SHOWSURFACES
// See if we can blt this to the screen
void ShowSurface(IDirectDrawSurface *pSurface)
{
CComPtr<IDirectDraw> pDDraw;
CComPtr<IDirectDrawSurface2> pSurface2;
DDSURFACEDESC ddsdSurf;
ddsdSurf.dwSize = sizeof(ddsdSurf);
HRESULT hr = pSurface->QueryInterface(IID_IDirectDrawSurface2, (void **)&pSurface2);
if (SUCCEEDED(hr)) {
hr = pSurface2->GetDDInterface((void **)&pDDraw);
}
if (SUCCEEDED(hr)) {
hr = pSurface->GetSurfaceDesc(&ddsdSurf);
}
if (SUCCEEDED(hr)) {
CComPtr<IDirectDrawSurface> pPrimary;
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
HRESULT hr = pDDraw->CreateSurface(&ddsd, &pPrimary, NULL);
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = ddsdSurf.dwWidth;
rc.bottom = ddsdSurf.dwHeight;
if (SUCCEEDED(hr)) {
pPrimary->Blt(&rc, pSurface, &rc, DDBLT_WAIT, NULL);
} else {
}
}
}
#endif
/////////////////////////////////////////////////////////////////////////////
// CDDStream
CDDStream::CDDStream() :
m_dwForcedFormatFlags(0),
m_Height(0),
m_Width(0),
m_lLastPitch(0),
m_pMyReadOnlySample(NULL),
m_pDefPixelFormat(GetDefaultPixelFormatPtr(NULL))
{
}
HRESULT CDDStream::InitDirectDraw()
{
HRESULT hr = NOERROR;
if (!m_pDirectDraw) {
CComPtr<IDirectDraw> pDDraw;
hr = DirectDrawCreate(NULL, &pDDraw, NULL);
if (SUCCEEDED(hr)) {
hr = pDDraw->SetCooperativeLevel(NULL, DDSCL_NORMAL);
}
if (SUCCEEDED(hr)) {
m_pDirectDraw = pDDraw;
}
}
return hr;
}
HRESULT CDDStream::InternalAllocateSample(
DWORD dwFlags,
bool bIsInternalSample,
IDirectDrawStreamSample **ppDDSample,
bool bTemp
)
{
AUTO_CRIT_LOCK;
HRESULT hr = S_OK;
CComPtr <IDirectDrawSurface> pSurface;
CComPtr<IDirectDrawPalette> pPalette;
//
// Create the direct draw object here if necessary. It is important to call the
// SetDirectDraw method so it can set other member variables appropriately
//
if (!m_pDirectDraw) {
hr = InitDirectDraw();
if (FAILED(hr)) {
goto Exit;
}
}
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);
GetFormatInternal(&ddsd, &pPalette, NULL, NULL);
hr = m_pDirectDraw->CreateSurface(&ddsd, &pSurface, NULL);
if (SUCCEEDED(hr)) {
if (pPalette) {
pSurface->SetPalette(pPalette);
}
RECT rect = {0, 0, ddsd.dwWidth, ddsd.dwHeight};
hr = InternalCreateSample(pSurface,
&rect,
dwFlags,
bIsInternalSample,
ppDDSample,
bTemp);
// No need to release surface if create fails since pSurface is a CComPtr
if (SUCCEEDED(hr) && !bIsInternalSample) {
// Make sure the surface has a palette if the stream has one
if (pPalette == NULL && m_pDirectDrawPalette) {
pSurface->SetPalette(m_pDirectDrawPalette);
}
}
}
Exit:
return hr;
}
STDMETHODIMP CDDStream::SetSameFormat(IMediaStream *pStream, DWORD dwFlags)
{
TRACEINTERFACE(_T("IDirectDrawStream::SetSameFormat(0x%8.8X, 0x%8.8X)\n"),
pStream, dwFlags);
CComQIPtr<IDirectDrawMediaStream, &IID_IDirectDrawMediaStream> pSource(pStream);
if (!pSource) {
return MS_E_INCOMPATIBLE;
}
DDSURFACEDESC ddsdCurrent;
CComPtr <IDirectDrawPalette> pPalette;
ddsdCurrent.dwSize = sizeof(ddsdCurrent);
HRESULT hr = pSource->GetFormat(&ddsdCurrent, &pPalette, NULL, 0);
/* Lock the source format */
ddsdCurrent.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
if (SUCCEEDED(hr)) {
hr = pSource->SetFormat(&ddsdCurrent, pPalette);
}
if (SUCCEEDED(hr)) {
hr = SetFormat(&ddsdCurrent, pPalette);
if (SUCCEEDED(hr)) {
CComPtr<IDirectDraw> pDD;
hr = pSource->GetDirectDraw(&pDD);
if (SUCCEEDED(hr)) {
hr = SetDirectDraw(pDD);
}
}
}
return hr;
}
STDMETHODIMP CDDStream::AllocateSample(DWORD dwFlags, IStreamSample **ppSample)
{
TRACEINTERFACE(_T("IDirectDrawStream::AllocateSample(0x%8.8X, 0x%8.8X)\n"),
dwFlags, ppSample);
HRESULT hr;
if (ppSample) {
*ppSample = NULL;
}
if (!ppSample || dwFlags) {
hr = E_INVALIDARG;
} else {
IDirectDrawStreamSample *pDDSample = NULL;
hr = InternalAllocateSample(0, false, &pDDSample);
*ppSample = pDDSample;
}
return hr;
}
STDMETHODIMP CDDStream::CreateSharedSample(IStreamSample *pExistingSample,
DWORD dwFlags,
IStreamSample **ppNewSample)
{
TRACEINTERFACE(_T("IDirectDrawStream::CreateSharedSample(0x%8.8X, 0x%8.8X, 0x%8.8X)\n"),
pExistingSample, dwFlags, ppNewSample);
*ppNewSample = NULL;
CComQIPtr<IDirectDrawStreamSample, &IID_IDirectDrawStreamSample> pSource(pExistingSample);
if (!pSource) {
return MS_E_INCOMPATIBLE;
}
CComPtr<IDirectDrawSurface> pSurface;
RECT rect;
pSource->GetSurface(&pSurface, &rect);
IDirectDrawStreamSample * pDDSample;
HRESULT hr = CreateSample(pSurface, &rect, 0, &pDDSample);
if (SUCCEEDED(hr)) {
*ppNewSample = pDDSample;
}
return hr;
}
//
// IDirectDrawMediaStream
//
void CDDStream::InitSurfaceDesc(LPDDSURFACEDESC lpddsd)
{
lpddsd->dwFlags = 0;
if (m_Height) {
lpddsd->dwHeight = m_Height;
lpddsd->dwWidth = m_Width;
} else {
lpddsd->dwHeight = lpddsd->dwWidth = 100;
}
if ((m_dwForcedFormatFlags & DDSD_PIXELFORMAT) || m_pConnectedPin) {
memcpy(&lpddsd->ddpfPixelFormat, &m_PixelFormat, sizeof(m_PixelFormat));
} else {
memcpy(&lpddsd->ddpfPixelFormat, m_pDefPixelFormat, sizeof(m_PixelFormat));
}
lpddsd->ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
}
STDMETHODIMP CDDStream::GetFormat(DDSURFACEDESC *pDDSDCurrent,
IDirectDrawPalette **ppDirectDrawPalette,
DDSURFACEDESC *pDDSDDesired,
DWORD *pdwFlags)
{
if(!m_pConnectedPin) {
return MS_E_NOSTREAM;
}
return GetFormatInternal(pDDSDCurrent, ppDirectDrawPalette, pDDSDDesired, pdwFlags);
}
STDMETHODIMP CDDStream::GetFormatInternal(DDSURFACEDESC *pDDSDCurrent,
IDirectDrawPalette **ppDirectDrawPalette,
DDSURFACEDESC *pDDSDDesired,
DWORD *pdwFlags)
{
TRACEINTERFACE(_T("IDirectDrawStream::GetFormat(0x%8.8X, 0x%8.8X, 0x%8.8X, 0x%8.8X)\n"),
pDDSDCurrent, ppDirectDrawPalette, pDDSDDesired, pdwFlags);
//
// If we have never connected, and the format is not set, then default
// to returning a height and width (100 x 100) and a caps of
// data interchange type,
//
// If we are connected but haven't allocated a sureface, simply return the
// correct height and width, and a caps of data interchange type.
//
// If we have a set format, then return the height, width, pixel format,
// and caps of the current surfacedesc we have.
//
if (pDDSDCurrent) {
InitSurfaceDesc(pDDSDCurrent);
pDDSDCurrent->dwFlags = DDSD_HEIGHT | DDSD_WIDTH | DDSD_CAPS | m_dwForcedFormatFlags;
if (m_cAllocated) {
pDDSDCurrent->dwFlags |= DDSD_PIXELFORMAT;
}
}
if (pDDSDDesired) {
InitSurfaceDesc(pDDSDDesired);
if (m_pConnectedPin) {
pDDSDDesired->dwFlags |= DDSD_HEIGHT | DDSD_WIDTH;
}
}
if (ppDirectDrawPalette) {
*ppDirectDrawPalette = m_pDirectDrawPalette;
if (*ppDirectDrawPalette) {
(*ppDirectDrawPalette)->AddRef();
}
}
if (pdwFlags) {
*pdwFlags = m_bSamplesAreReadOnly ? DDSFF_PROGRESSIVERENDER : 0;
}
return S_OK;
}
STDMETHODIMP CDDStream::SetFormat(const DDSURFACEDESC *lpDDSurfaceDesc,
IDirectDrawPalette *pDirectDrawPalette)
{
TRACEINTERFACE(_T("IDirectDrawStream::SetFormat(0x%8.8X, 0x%8.8X)\n"),
lpDDSurfaceDesc, pDirectDrawPalette);
HRESULT hr = InternalSetFormat(lpDDSurfaceDesc, pDirectDrawPalette, false);
if (hr == VFW_E_TYPE_NOT_ACCEPTED) {
hr = DDERR_INVALIDSURFACETYPE;
}
return hr;
}
HRESULT CDDStream::RenegotiateMediaType(const DDSURFACEDESC *lpDDSurfaceDesc,
IDirectDrawPalette *pPalette,
const AM_MEDIA_TYPE *pmt)
{
HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
// If the type is acceptable and we're using
// our own allocator then QueryAccept is OK - we can
// just return the new type from GetBuffer
if (m_bUsingMyAllocator) {
if (S_OK == m_pConnectedPin->QueryAccept(pmt)) {
hr = S_OK;
}
}
// Check if we'll be able to make a read-only sample
if (m_bSamplesAreReadOnly) {
// If the pixel format is not OK
if (!IsSupportedType(&lpDDSurfaceDesc->ddpfPixelFormat)) {
hr = VFW_E_TYPE_NOT_ACCEPTED;
}
}
//
// If we're stopped then we can attempt to reconnect
//
if (S_OK != hr && m_FilterState == State_Stopped) {
AM_MEDIA_TYPE SavedType;
DDSURFACEDESC ddsdSaved;
CComPtr<IDirectDrawPalette> pPaletteSaved;
ddsdSaved.dwSize = sizeof(ddsdSaved);
ConnectionMediaType(&SavedType);
GetFormatInternal(&ddsdSaved, &pPaletteSaved, NULL, NULL);
CComPtr<IPin> pConnected = m_pConnectedPin;
Disconnect();
pConnected->Disconnect();
IPin *ppinIn;
IPin *ppinOut;
if (m_Direction == PINDIR_INPUT) {
ppinIn = this;
ppinOut = pConnected;
} else {
ppinOut = this;
ppinIn = pConnected;
}
HRESULT hrTmp = InternalSetFormat(lpDDSurfaceDesc, pPalette, false); // Recurse!
if (SUCCEEDED(hrTmp)) {
CComQIPtr<IGraphBuilder, &IID_IGraphBuilder>
pBuilder(m_pFilterGraph);
hrTmp = pBuilder->Connect(ppinOut, ppinIn);
}
if (FAILED(hrTmp)) {
SetFormat(&ddsdSaved, pPaletteSaved);
m_pFilterGraph->ConnectDirect(ppinOut, ppinIn, &SavedType);
} else {
hr = S_OK;
}
CoTaskMemFree(SavedType.pbFormat);
}
return hr;
}
HRESULT CDDStream::InternalSetFormat(const DDSURFACEDESC *lpDDSurfaceDesc,
IDirectDrawPalette *pPalette,
bool bFromPin,
bool bQuery)
{
if (!lpDDSurfaceDesc) {
return E_POINTER;
}
if (lpDDSurfaceDesc->dwSize != sizeof(*lpDDSurfaceDesc)) {
return DDERR_INVALIDPARAMS;
}
DDSURFACEDESC ddsd;
bool bPaletteAllocated = false;
Lock();
DDSURFACEDESC ddsdCopy;
if (m_pConnectedPin && !bQuery &&
(bFromPin && !(m_dwForcedFormatFlags & (DDSD_WIDTH | DDSD_HEIGHT)) ||
!bFromPin && pPalette == NULL &&
lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 8
)
) {
/* See what size the connected pin would like :
-- If the width and height haven't been specified set them
to the output pin's preferred values
-- If no palette is specified try to get one from the output
pin
*/
AM_MEDIA_TYPE *pmt;
IEnumMediaTypes *pEnum;
HRESULT hr = m_pConnectedPin->EnumMediaTypes(&pEnum);
if (SUCCEEDED(hr)) {
ULONG ulGot;
bool bBreak = false;
while (!bBreak && S_OK == pEnum->Next(1, &pmt, &ulGot)) {
if (pmt->formattype == FORMAT_VideoInfo) {
VIDEOINFO *pvi = (VIDEOINFO *)pmt->pbFormat;
if (bFromPin) {
ddsdCopy = *lpDDSurfaceDesc;
ddsdCopy.dwWidth = pvi->bmiHeader.biWidth;
ddsdCopy.dwHeight = pvi->bmiHeader.biHeight < 0 ?
-pvi->bmiHeader.biHeight :
pvi->bmiHeader.biHeight;
lpDDSurfaceDesc = &ddsdCopy;
bBreak = true;
} else {
if (pmt->subtype == MEDIASUBTYPE_RGB8) {
DDSURFACEDESC ddsd;
_ASSERTE(pPalette == NULL);
if (SUCCEEDED(ConvertMediaTypeToSurfaceDesc(
pmt,
m_pDirectDraw,
&pPalette,
&ddsd)) &&
pPalette != NULL) {
bPaletteAllocated = true;
}
bBreak = true;
}
}
}
DeleteMediaType(pmt);
}
pEnum->Release();
}
}
InitSurfaceDesc(&ddsd);
ddsd.dwFlags = lpDDSurfaceDesc->dwFlags;
bool bMatches = true;
bool bPixelFmtMatches = true;
BOOL bContradictsForced = FALSE;
if (ddsd.dwFlags & (DDSD_HEIGHT | DDSD_WIDTH)) {
if (ddsd.dwHeight != lpDDSurfaceDesc->dwHeight ||
ddsd.dwWidth != lpDDSurfaceDesc->dwWidth) {
bMatches = false;
ddsd.dwHeight = lpDDSurfaceDesc->dwHeight;
ddsd.dwWidth = lpDDSurfaceDesc->dwWidth;
bContradictsForced |= (m_dwForcedFormatFlags & DDSD_HEIGHT);
}
}
if (ddsd.dwFlags & DDSD_PIXELFORMAT) {
if (!ComparePixelFormats(&ddsd.ddpfPixelFormat,
&lpDDSurfaceDesc->ddpfPixelFormat)) {
bMatches = false;
bPixelFmtMatches = false;
bContradictsForced |= (m_dwForcedFormatFlags & DDSD_PIXELFORMAT);
}
// Always copy because ComparePixelFormats doesn't check all
// the bits but we need to save the correct format for making
// more surfaces
memcpy(&ddsd.ddpfPixelFormat, &lpDDSurfaceDesc->ddpfPixelFormat, sizeof(ddsd.ddpfPixelFormat));
}
HRESULT hr;
if (bMatches) {
hr = S_OK;
} else {
if (bContradictsForced && bFromPin) {
hr = VFW_E_TYPE_NOT_ACCEPTED;
} else {
if (m_cAllocated) {
hr = MS_E_SAMPLEALLOC;
} else {
//
// If the pin is trying to change its own type via query accept then skip the
// renegotiation phase.
//
if (bFromPin || bQuery) {
// If we're connected then this is from QueryAccept so we'll say OK. Otherwise, only
// accept a ReceiveConnection if the pixel format matches the display pixel format.
//
// NOTE - aren't we going to return S_OK always here?
// During connection m_pConnectedPin is not set anyway
// and bQuery already checks for QueryAccept (Robin)
hr = (m_pConnectedPin || bPixelFmtMatches) ? S_OK : VFW_E_TYPE_NOT_ACCEPTED;
} else {
_ASSERTE(!bQuery);
// Note: The below call to ConvertSurfaceDescToMediaType should always be done to make
// sure that the surface descriptor is valid, EVEN IF WE'RE NOT CONNECTED TO A PIN!
AM_MEDIA_TYPE *pmt;
hr = ConvertSurfaceDescToMediaType(lpDDSurfaceDesc, pPalette,
NULL, true, &pmt);
if (SUCCEEDED(hr)) {
hr = m_pConnectedPin ? RenegotiateMediaType(lpDDSurfaceDesc, pPalette, pmt) : S_OK;
DeleteMediaType(pmt);
}
}
}
}
}
//
// Even if we match we may be forcing more format flags and
// setting caps flags
if (S_OK == hr && !bQuery) {
// Don't update the pixel format if it was already forced
if (ddsd.dwFlags & DDSD_PIXELFORMAT) {
if (!bFromPin || !(m_dwForcedFormatFlags & DDSD_PIXELFORMAT)) {
memcpy(&m_PixelFormat, &ddsd.ddpfPixelFormat, sizeof(m_PixelFormat));
m_PixelFormat.dwSize = sizeof(m_PixelFormat);
}
}
if (!bFromPin) {
m_dwForcedFormatFlags = ddsd.dwFlags &
(DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS);
}
m_pDirectDrawPalette = pPalette;
if (ddsd.dwFlags & (DDSD_HEIGHT | DDSD_WIDTH)) {
m_Height = ddsd.dwHeight;
m_Width = ddsd.dwWidth;
}
}
if (bPaletteAllocated) {
pPalette->Release();
pPalette = NULL;
}
Unlock();
return hr;
}
STDMETHODIMP CDDStream::GetDirectDraw(IDirectDraw **ppDirectDraw)
{
TRACEINTERFACE(_T("IDirectDrawStream::GetDirectDraw(0x%8.8X)\n"),
ppDirectDraw);
if (!ppDirectDraw) {
return E_POINTER;
}
Lock();
*ppDirectDraw = m_pDirectDraw;
Unlock();
if (*ppDirectDraw) {
(*ppDirectDraw)->AddRef();
}
return S_OK;
}
STDMETHODIMP CDDStream::SetDirectDraw(IDirectDraw *pDirectDraw)
{
TRACEINTERFACE(_T("IDirectDrawStream::SetDirectDraw(0x%8.8X)\n"),
pDirectDraw);
HRESULT hr;
AUTO_CRIT_LOCK;
if (m_cAllocated) {
hr = IsSameObject(m_pDirectDraw, pDirectDraw) ? S_OK : MS_E_SAMPLEALLOC;
} else {
//
// NOTE: This is important! We need to release ALL objects that were allocated
// by the previous DirectDraw object since they will magically disappear
// beneath us. So far, the only object we hold is the palette so we'll copy
// the entries and then create a new object.
//
hr = S_OK;
if (m_pDirectDrawPalette) {
if (pDirectDraw) {
PALETTEENTRY aPaletteEntry[256];
hr = m_pDirectDrawPalette->GetEntries(0, 0, 256, aPaletteEntry);
if (SUCCEEDED(hr)) {
CComPtr <IDirectDrawPalette> pNewPal;
hr = pDirectDraw->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, aPaletteEntry, &pNewPal, NULL);
if (SUCCEEDED(hr)) {
m_pDirectDrawPalette = pNewPal;
}
}
} else {
m_pDirectDrawPalette = NULL; // If no direct draw object then toss the palette.
}
}
if (SUCCEEDED(hr)) {
m_pDirectDraw = pDirectDraw;
if (pDirectDraw) {
m_pDefPixelFormat = GetDefaultPixelFormatPtr(pDirectDraw);
}
}
}
return hr;
}
//
// NOTE: For this function, the caller MUST provide a rect. The format of the surface
// and the DirectDraw object are not checked for validity. They are assumed to be correct.
//
HRESULT CDDStream::InternalCreateSample(IDirectDrawSurface *pSurface, const RECT *pRect,
DWORD dwFlags, bool bIsInternalSample,
IDirectDrawStreamSample **ppSample,
bool bTemp)
{
HRESULT hr = S_OK;
*ppSample = NULL;
AUTO_CRIT_LOCK;
CDDSample *pSample;
// First check the surface format
{
DDSURFACEDESC ddsd;
CComPtr<IDirectDrawPalette> pPalette;
pSurface->GetPalette(&pPalette);
ddsd.dwSize = sizeof(ddsd);
_ASSERTE(pRect != NULL);
hr = pSurface->GetSurfaceDesc(&ddsd);
ddsd.dwWidth = pRect->right - pRect->left;
ddsd.dwHeight = pRect->bottom - pRect->top;
if (SUCCEEDED(hr)) {
hr = SetFormat(&ddsd, pPalette ? pPalette : m_pDirectDrawPalette);
}
}
if (SUCCEEDED(hr)) {
if (bIsInternalSample) {
CDDInternalSample *pInternal = new CComObject<CDDInternalSample>;
if (pInternal != NULL) {
hr = pInternal->InternalInit();
}
pSample = pInternal;
} else {
pSample = new CComObject<CDDSample>;
}
if (pSample) {
//
// InitSample will increment our m_cAllocated variable if this is not an internal sample....
//
if (SUCCEEDED(hr)) {
hr = pSample->InitSample(this, pSurface, pRect, dwFlags & DDSFF_PROGRESSIVERENDER, bIsInternalSample,
bTemp);
}
if (SUCCEEDED(hr)) {
pSample->GetControllingUnknown()->QueryInterface(IID_IDirectDrawStreamSample, (void **)ppSample);
} else {
delete pSample;
}
} else {
hr = E_OUTOFMEMORY;
}
}
#if 0
// Use the real pixel format for subsequent surfaces
if (SUCCEEDED(hr)) {
m_PixelFormat.dwFlags = ddsd.ddpfPixelFormat.dwFlags;
}
#endif
return hr;
}
STDMETHODIMP CDDStream::CreateSample(IDirectDrawSurface *pSurface, const RECT *pRect, DWORD dwFlags,
IDirectDrawStreamSample **ppSample)
{
TRACEINTERFACE(_T("IDirectDrawStream::CreateSample(0x%8.8X, 0x%8.8X, 0x%8.8X, 0x%8.8X)\n"),
pSurface, pRect, dwFlags, ppSample);
HRESULT hr;
*ppSample = NULL;
if (dwFlags & (~DDSFF_PROGRESSIVERENDER)) {
return E_INVALIDARG;
}
AUTO_CRIT_LOCK;
if (pSurface == NULL) {
if (pRect) {
hr = E_INVALIDARG;
} else {
hr = InternalAllocateSample(dwFlags, false, ppSample);
}
} else {
CComQIPtr <IDirectDrawSurface2, &IID_IDirectDrawSurface2> pSurf2(pSurface);
// Work around DDrawEx bug
IUnknown *pUnk;
hr = pSurf2->GetDDInterface((void **)&pUnk);
if (SUCCEEDED(hr)) {
IDirectDraw *pDD;
hr = pUnk->QueryInterface(IID_IDirectDraw, (void **)&pDD);
pUnk->Release();
if (SUCCEEDED(hr)) {
hr = SetDirectDraw(pDD);
pDD->Release();
}
}
if (SUCCEEDED(hr)) {
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);
hr = pSurface->GetSurfaceDesc(&ddsd);
if (SUCCEEDED(hr)) {
RECT SubRect;
if (pRect) {
SubRect = *pRect;
if (SubRect.left > SubRect.right || SubRect.right > (LONG)ddsd.dwWidth ||
SubRect.top > SubRect.bottom || SubRect.bottom > (LONG)ddsd.dwHeight) {
hr = DDERR_INVALIDRECT;
goto Exit;
}
ddsd.dwWidth = SubRect.right - SubRect.left;
ddsd.dwHeight = SubRect.bottom - SubRect.top;
} else {
SubRect.top = SubRect.left = 0;
SubRect.bottom = ddsd.dwHeight;
SubRect.right = ddsd.dwWidth;
}
//
// We don't set the CAPS flag here so we won't force a particular caps
// mode. I'm not sure if this is the right choice, but it seems more
// flexible.
//
ddsd.dwFlags &= (DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT);
CComPtr<IDirectDrawPalette> pPalette;
pSurface->GetPalette(&pPalette);
hr = SetFormat(&ddsd, pPalette);
if (SUCCEEDED(hr)) {
hr = InternalCreateSample(pSurface, &SubRect, dwFlags, false, ppSample);
}
}
}
}
Exit:
return hr;
}
// Get the time per frame
// If we're connected this comes out of the media type, otherwise we
// don't know
STDMETHODIMP CDDStream::GetTimePerFrame(
/* [out] */ STREAM_TIME *pFrameTime
)
{
if (pFrameTime == NULL) {
return E_POINTER;
}
AUTO_CRIT_LOCK;
if (m_pConnectedPin) {
*pFrameTime = ((VIDEOINFO *)m_ConnectedMediaType.pbFormat)->AvgTimePerFrame;
} else {
return MS_E_NOSTREAM;
}
return S_OK;
}
//
// IPin implementation
//
STDMETHODIMP CDDStream::ReceiveConnection(IPin * pConnector, const AM_MEDIA_TYPE *pmt)
{
AUTO_CRIT_LOCK;
//
// This helper function in CStream checks basic parameters for the Pin such as
// the connecting pin's direction (we need to check this -- Sometimes the filter
// graph will try to connect us to ourselves!) and other errors like already being
// connected, etc.
//
HRESULT hr = CheckReceiveConnectionPin(pConnector);
if (hr == NOERROR && pmt->formattype == FORMAT_VideoInfo) {
//
// Check the source accepts negative heights
//
VIDEOINFO * const pvi = (VIDEOINFO *)pmt->pbFormat;
if (pvi->bmiHeader.biHeight > 0) {
VIDEOINFO vi;
CopyMemory((PVOID)&vi, (PVOID)pmt->pbFormat,
min(pmt->cbFormat, sizeof(vi)));
AM_MEDIA_TYPE mt = *pmt;
mt.pbFormat = (PBYTE)&vi;
vi.bmiHeader.biHeight = - vi.bmiHeader.biHeight;
if (S_OK != pConnector->QueryAccept(&mt)) {
hr = VFW_E_TYPE_NOT_ACCEPTED;
}
}
}
if (hr == NOERROR) {
DDSURFACEDESC SurfaceDesc;
SurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
CComPtr <IDirectDrawPalette> pPalette;
m_pConnectedPin = pConnector;
if (NOERROR == ConvertMediaTypeToSurfaceDesc(pmt, m_pDirectDraw, &pPalette, &SurfaceDesc) &&
SUCCEEDED(InternalSetFormat(&SurfaceDesc, pPalette, true))) {
CopyMediaType(&m_ConnectedMediaType, pmt);
CopyMediaType(&m_ActualMediaType, pmt);
hr = NOERROR;
} else {
m_pConnectedPin = NULL;
hr = VFW_E_TYPE_NOT_ACCEPTED;
}
}
if (SUCCEEDED(hr)) {
pConnector->QueryInterface(IID_IQualityControl, (void **)&m_pQC);
}
return hr;
}
STDMETHODIMP CDDStream::QueryAccept(const AM_MEDIA_TYPE *pmt)
{
AUTO_CRIT_LOCK;
HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
DDSURFACEDESC SurfaceDesc;
SurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
CComPtr <IDirectDrawPalette> pPalette;
if (S_OK == ConvertMediaTypeToSurfaceDesc(pmt, m_pDirectDraw, &pPalette, &SurfaceDesc) &&
SUCCEEDED(InternalSetFormat(&SurfaceDesc, pPalette, true, true)) &&
((VIDEOINFOHEADER *)pmt->pbFormat)->bmiHeader.biHeight >= 0) {
hr = S_OK;
}
return hr;
}
STDMETHODIMP CDDStream::Receive(IMediaSample *pMediaSample)
{
bool bDummySample = false;
if (m_bFlushing || m_bStopIfNoSamples && m_cAllocated == 0) {
EndOfStream();
return S_FALSE;
}
HRESULT hr = S_OK;
#ifdef DEBUG
if (bDbgTraceTimes) {
REFERENCE_TIME rtStart, rtStop;
if (SUCCEEDED(pMediaSample->GetTime(&rtStart, &rtStop))) {
ATLTRACE(_T("AMSTREAM.DLL : Video sample received - start %dms, end %dms, duration %dms\n"),
(LONG)(rtStart / 10000), (LONG)(rtStop / 10000),
(LONG)((rtStop - rtStart) / 10000));
}
}
#endif
if (m_bUsingMyAllocator) {
CDDSample *pSrcSample = (CDDSample *)((CMediaSample *)pMediaSample)->m_pSample;
pSrcSample->ReleaseMediaSampleLock();
pSrcSample->m_bReceived = true;
if (!pSrcSample->m_bWaited) {
// Wait for render time
REFERENCE_TIME rtStart, rtStop;
if (SUCCEEDED(pMediaSample->GetTime(&rtStart, &rtStop))) {
m_pFilter->WaitUntil(rtStart);
}
}
if (pSrcSample->IsTemp()) {
bDummySample = true;
} else {
#ifdef SHOWSURFACES
ShowSurface(pSrcSample->m_pSurface);
#endif
// In this case if the read-only sample has no buddy then
// it's a temp sample for the nostall stuff
if (pSrcSample == m_pMyReadOnlySample &&
!m_pMyReadOnlySample->HasBuddy()) {
_ASSERTE(m_bNoStall);
bDummySample = true;
}
}
} else {
CDDSample *pDestSample;
REFERENCE_TIME rtStart, rtEnd;
pMediaSample->GetTime(&rtStart, &rtEnd);
hr = AllocDDSampleFromPool(&rtStart, &pDestSample);
if (SUCCEEDED(hr)) {
_ASSERTE(!pDestSample->IsTemp());
Lock();
// This is a media sample coming from a different allocator.
AM_MEDIA_TYPE *pNewMediaType;
if (pMediaSample->GetMediaType(&pNewMediaType) == S_OK) {
FreeMediaType(m_ActualMediaType);
// Note just copying has the effect
// of transferring pNewMediaType's format block
// and pUnk reference count
// Also this way we avoid allocation failures
m_ActualMediaType = *pNewMediaType;
CoTaskMemFree((PVOID)pNewMediaType);
}
if (SUCCEEDED(hr)) {
hr = pDestSample->CopyFrom(pMediaSample, &m_ActualMediaType);
#ifdef SHOWSURFACES
ShowSurface(pDestSample->m_pSurface);
#endif
hr = pDestSample->SetCompletionStatus(hr);
// Warning! The SetCompletionStatus may delete pDestSample. Don't touch it after this point!
}
Unlock();
} else {
// Might be timeout which means we become a zombie
hr = S_OK;
bDummySample = true;
}
}
// Send quality message if clocked
// NOTE - we must do this AFTER releasing the media sample lock
// or we can deadlock on the win16 lock when querying the clock
// because dsound can be running on another thread waiting for
// the win16 lock but holding its global mutex
REFERENCE_TIME CurTime;
if (S_OK == m_pFilter->GetCurrentStreamTime(&CurTime)) {
REFERENCE_TIME rtStart, rtStop;
if (m_pQC && SUCCEEDED(pMediaSample->GetTime(&rtStart, &rtStop))) {
Quality msg;
msg.Proportion = 1000;
msg.Type = Famine;
msg.Late = CurTime - rtStart;
msg.TimeStamp = rtStart;
if (bDummySample) {
// Tell them they're later than they actually are
msg.Late += 150 * 10000;
}
// Call Notify on our connected pin
m_pQC->Notify(m_pBaseFilter, msg);
//ATLTRACE("Late by %dms\n", (LONG)((CurTime - rtStart) / 10000));
} else {
//ATLTRACE("No timestamp\n");
}
}
#ifdef DEBUG
if (bDbgTraceTimes) {
REFERENCE_TIME CurTime;
m_pFilter->GetCurrentStreamTime(&CurTime);
ATLTRACE(_T("AMSTREAM.DLL : Got sample at %dms\n"),
(LONG)(CurTime / 10000));
}
#endif
return hr;
}
STDMETHODIMP CDDStream::NotifyAllocator(IMemAllocator * pAllocator, BOOL bReadOnly)
{
if (bReadOnly) {
// If the pixel format is not OK
if (!IsSupportedType(&m_PixelFormat)) {
return VFW_E_TYPE_NOT_ACCEPTED;
}
}
return CStream::NotifyAllocator(pAllocator, bReadOnly);
}
//
// IMemAllocator implementation
//
//
// IMemAllocator
//
STDMETHODIMP CDDStream::SetProperties(ALLOCATOR_PROPERTIES* pRequest, ALLOCATOR_PROPERTIES* pActual)
{
HRESULT hr;
AUTO_CRIT_LOCK;
ZeroMemory(pActual, sizeof(*pActual));
if (pRequest->cbAlign == 0) {
hr = VFW_E_BADALIGN;
} else {
if (m_bCommitted == TRUE) {
hr = VFW_E_ALREADY_COMMITTED;
} else {
m_lRequestedBufferCount = pRequest->cBuffers;
hr = GetProperties(pActual);
}
}
return hr;
}
STDMETHODIMP CDDStream::GetProperties(ALLOCATOR_PROPERTIES* pProps)
{
AUTO_CRIT_LOCK;
AM_MEDIA_TYPE *pMediaType;
HRESULT hr = GetMediaType(-1, &pMediaType);
if (SUCCEEDED(hr)) {
VIDEOINFO *pVideoInfo = (VIDEOINFO *)pMediaType->pbFormat;
BITMAPINFOHEADER *pbmiHeader = &pVideoInfo->bmiHeader;
pProps->cbBuffer = pbmiHeader->biSizeImage;
pProps->cBuffers = m_lRequestedBufferCount ?
m_lRequestedBufferCount : 1;
pProps->cbAlign = 1;
pProps->cbPrefix = 0;
DeleteMediaType(pMediaType);
}
return hr;
}
STDMETHODIMP CDDStream::Decommit()
{
AUTO_CRIT_LOCK;
if (m_pMyReadOnlySample) {
m_pMyReadOnlySample->Die();
m_pMyReadOnlySample->GetControllingUnknown()->Release();
m_pMyReadOnlySample = NULL;
}
return CStream::Decommit();
}
//
// This method assumes the critical section is *NOT* owned!
//
HRESULT CDDStream::GetMyReadOnlySample(CDDSample *pBuddy, CDDSample **ppSample)
{
*ppSample = NULL;
CDDInternalSample *pROSample;
Lock();
if (!m_pMyReadOnlySample) {
IDirectDrawStreamSample *pDDSample;
HRESULT hr = InternalAllocateSample(DDSFF_PROGRESSIVERENDER, true, &pDDSample);
if (FAILED(hr)) {
Unlock();
return hr;
}
m_pMyReadOnlySample = (CDDInternalSample *)pDDSample;
}
pROSample = m_pMyReadOnlySample;
pROSample->GetControllingUnknown()->AddRef();
Unlock();
//
// Must leave our critical section here! This is very important since JoinToBuddy can fail.
//
HRESULT hr;
if (pBuddy) {
hr = pROSample->JoinToBuddy(pBuddy);
} else {
hr = S_OK;
}
if (hr == S_OK) {
*ppSample = pROSample;
} else {
pROSample->GetControllingUnknown()->Release();
}
return hr;
}
STDMETHODIMP CDDStream::GetBuffer(IMediaSample **ppBuffer, REFERENCE_TIME * pStartTime,
REFERENCE_TIME * pEndTime, DWORD dwFlags)
{
*ppBuffer = NULL;
if (m_bStopIfNoSamples && m_cAllocated == 0) {
return E_FAIL;
}
CDDSample *pSample;
#ifdef DEBUG
if (bDbgTraceTimes) {
ATLTRACE(_T("AMSTREAM.DLL : GetBuffer for %dms\n"),
pStartTime ? (LONG)(*pStartTime / 10000) : 0);
}
#endif
HRESULT hr = AllocDDSampleFromPool(pStartTime, &pSample);
if (SUCCEEDED(hr)) {
if (CreateInternalSample() && !pSample->m_bProgressiveRender) {
CDDSample *pMyReadOnlySample;
hr = GetMyReadOnlySample(pSample, &pMyReadOnlySample);
if (FAILED(hr)) {
return pSample->SetCompletionStatus(hr);
}
pSample = pMyReadOnlySample;
}
Lock();
pSample->m_pMediaSample->m_dwFlags = dwFlags;
m_lLastPitch = pSample->LockAndPrepareMediaSample(m_lLastPitch);
if (m_lLastPitch == 0) {
hr = pSample->SetCompletionStatus(E_UNEXPECTED); // Really strange to fail this way!
} else {
pSample->m_bReceived = false;
pSample->m_bModified = true;
*ppBuffer = (IMediaSample *)(pSample->m_pMediaSample);
(*ppBuffer)->AddRef();
}
Unlock();
}
return hr;
}
//
// Special CStream methods
//
HRESULT CDDStream::GetMediaType(ULONG Index, AM_MEDIA_TYPE **ppMediaType)
{
if (Index != 0 && Index != -1) {
return S_FALSE;
}
DDSURFACEDESC ddsd;
ddsd.dwSize = sizeof(ddsd);
CComPtr<IDirectDrawPalette> pPalette;
GetFormatInternal(&ddsd, &pPalette, NULL, NULL);
HRESULT hr = ConvertSurfaceDescToMediaType(&ddsd, pPalette, NULL, TRUE, ppMediaType);
// Don't offer a type for input - someone might use it!
if (SUCCEEDED(hr) && m_Direction == PINDIR_INPUT && Index == 0) {
// Something impossible - or at least something we'll reject
// but something they won't fall over on
(*ppMediaType)->formattype = GUID_NULL;
}
return hr;
}
// Create a temporary sample in order to throw away the data
HRESULT CDDStream::CreateTempSample(CSample **ppSample)
{
if (CreateInternalSample()) {
CDDSample *pDDSample;
HRESULT hr = GetMyReadOnlySample(NULL, &pDDSample);
*ppSample = pDDSample;
return hr;
}
//ATLTRACE("Creating temp sample\n");
IDirectDrawStreamSample *pSample;
*ppSample = NULL;
// This must be allocated as an internal sample otherwise
// we wind up AddRef'ing the filter graph and leaking
// everything (because the final release is on a filter
// thread and the filter graph hangs waiting for the thread
// that is actually doing the final release to go away).
HRESULT hr = InternalAllocateSample(0, true, &pSample, true);
if (SUCCEEDED(hr)) {
*ppSample = static_cast<CDDSample *>(pSample);
} else {
//ATLTRACE("Failed to create temp sample\n");
}
return hr;
}
STDMETHODIMP CDDStream::Initialize(IUnknown *pSourceObject, DWORD dwFlags, REFMSPID PurposeId, const STREAM_TYPE StreamType)
{
//
TRACEINTERFACE(_T("IDirectDrawStream::Initialize(0x%8.8X, 0x%8.8X, %s, %d)\n"),
pSourceObject, dwFlags, TextFromPurposeId(PurposeId), StreamType);
// It is important to call the base class first since if we are creating a peer
// stream then the Initalize call from the base class will end up calling SetSameFormat
// which will initialize this stream with the same directdraw object as it's peer.
// Otherwise, if the pSourceObject is actually a DirectDraw then we'll use that one.
//
HRESULT hr = CStream::Initialize(pSourceObject,
dwFlags & ~AMMSF_NOSTALL,
PurposeId,
StreamType);
if (SUCCEEDED(hr)) {
if (dwFlags & AMMSF_NOSTALL) {
m_bNoStall = true;
}
IDirectDraw *pDD;
if (pSourceObject &&
pSourceObject->QueryInterface(IID_IDirectDraw, (void **)&pDD) == S_OK) {
SetDirectDraw(pDD);
pDD->Release();
} else {
hr = InitDirectDraw();
}
}
return hr;
}