// 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;
}