/* * Video Renderer (Fullscreen and Windowed using Direct Draw) * * Copyright 2004 Christian Costa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #define NONAMELESSSTRUCT #define NONAMELESSUNION #include "quartz_private.h" #include "pin.h" #include "uuids.h" #include "vfwmsgs.h" #include "amvideo.h" #include "windef.h" #include "winbase.h" #include "dshow.h" #include "evcode.h" #include "strmif.h" #include "ddraw.h" #include "dvdmedia.h" #include #include "wine/unicode.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(quartz); typedef struct VideoRendererImpl { BaseRenderer renderer; BaseControlWindow baseControlWindow; BaseControlVideo baseControlVideo; IUnknown IUnknown_inner; IAMFilterMiscFlags IAMFilterMiscFlags_iface; IUnknown *outer_unk; BOOL init; HANDLE hThread; DWORD ThreadID; HANDLE hEvent; /* hEvent == evComplete? */ BOOL ThreadResult; RECT SourceRect; RECT DestRect; RECT WindowPos; LONG VideoWidth; LONG VideoHeight; } VideoRendererImpl; static inline VideoRendererImpl *impl_from_BaseWindow(BaseWindow *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, baseControlWindow.baseWindow); } static inline VideoRendererImpl *impl_from_BaseRenderer(BaseRenderer *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, renderer); } static inline VideoRendererImpl *impl_from_IBaseFilter(IBaseFilter *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, renderer.filter.IBaseFilter_iface); } static inline VideoRendererImpl *impl_from_IVideoWindow(IVideoWindow *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, baseControlWindow.IVideoWindow_iface); } static inline VideoRendererImpl *impl_from_BaseControlVideo(BaseControlVideo *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, baseControlVideo); } static inline VideoRendererImpl *impl_from_IBasicVideo(IBasicVideo *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, baseControlVideo.IBasicVideo_iface); } static DWORD WINAPI MessageLoop(LPVOID lpParameter) { VideoRendererImpl* This = lpParameter; MSG msg; BOOL fGotMessage; TRACE("Starting message loop\n"); if (FAILED(BaseWindowImpl_PrepareWindow(&This->baseControlWindow.baseWindow))) { This->ThreadResult = FALSE; SetEvent(This->hEvent); return 0; } This->ThreadResult = TRUE; SetEvent(This->hEvent); while ((fGotMessage = GetMessageW(&msg, NULL, 0, 0)) != 0 && fGotMessage != -1) { TranslateMessage(&msg); DispatchMessageW(&msg); } TRACE("End of message loop\n"); return msg.wParam; } static BOOL CreateRenderingSubsystem(VideoRendererImpl* This) { This->hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if (!This->hEvent) return FALSE; This->hThread = CreateThread(NULL, 0, MessageLoop, This, 0, &This->ThreadID); if (!This->hThread) { CloseHandle(This->hEvent); return FALSE; } WaitForSingleObject(This->hEvent, INFINITE); if (!This->ThreadResult) { CloseHandle(This->hEvent); CloseHandle(This->hThread); return FALSE; } return TRUE; } static void VideoRenderer_AutoShowWindow(VideoRendererImpl *This) { if (!This->init && (!This->WindowPos.right || !This->WindowPos.top)) { DWORD style = GetWindowLongW(This->baseControlWindow.baseWindow.hWnd, GWL_STYLE); DWORD style_ex = GetWindowLongW(This->baseControlWindow.baseWindow.hWnd, GWL_EXSTYLE); if (!This->WindowPos.right) { if (This->DestRect.right) { This->WindowPos.left = This->DestRect.left; This->WindowPos.right = This->DestRect.right; } else { This->WindowPos.left = This->SourceRect.left; This->WindowPos.right = This->SourceRect.right; } } if (!This->WindowPos.bottom) { if (This->DestRect.bottom) { This->WindowPos.top = This->DestRect.top; This->WindowPos.bottom = This->DestRect.bottom; } else { This->WindowPos.top = This->SourceRect.top; This->WindowPos.bottom = This->SourceRect.bottom; } } AdjustWindowRectEx(&This->WindowPos, style, FALSE, style_ex); TRACE("WindowPos: %d %d %d %d\n", This->WindowPos.left, This->WindowPos.top, This->WindowPos.right, This->WindowPos.bottom); SetWindowPos(This->baseControlWindow.baseWindow.hWnd, NULL, This->WindowPos.left, This->WindowPos.top, This->WindowPos.right - This->WindowPos.left, This->WindowPos.bottom - This->WindowPos.top, SWP_NOZORDER|SWP_NOMOVE|SWP_DEFERERASE); GetClientRect(This->baseControlWindow.baseWindow.hWnd, &This->DestRect); } else if (!This->init) This->DestRect = This->WindowPos; This->init = TRUE; if (This->baseControlWindow.AutoShow) ShowWindow(This->baseControlWindow.baseWindow.hWnd, SW_SHOW); } static DWORD VideoRenderer_SendSampleData(VideoRendererImpl* This, LPBYTE data, DWORD size) { AM_MEDIA_TYPE amt; HRESULT hr = S_OK; BITMAPINFOHEADER *bmiHeader; TRACE("(%p)->(%p, %d)\n", This, data, size); hr = IPin_ConnectionMediaType(&This->renderer.pInputPin->pin.IPin_iface, &amt); if (FAILED(hr)) { ERR("Unable to retrieve media type\n"); return hr; } if (IsEqualIID(&amt.formattype, &FORMAT_VideoInfo)) { bmiHeader = &((VIDEOINFOHEADER *)amt.pbFormat)->bmiHeader; } else if (IsEqualIID(&amt.formattype, &FORMAT_VideoInfo2)) { bmiHeader = &((VIDEOINFOHEADER2 *)amt.pbFormat)->bmiHeader; } else { FIXME("Unknown type %s\n", debugstr_guid(&amt.subtype)); return VFW_E_RUNTIME_ERROR; } TRACE("biSize = %d\n", bmiHeader->biSize); TRACE("biWidth = %d\n", bmiHeader->biWidth); TRACE("biHeight = %d\n", bmiHeader->biHeight); TRACE("biPlanes = %d\n", bmiHeader->biPlanes); TRACE("biBitCount = %d\n", bmiHeader->biBitCount); TRACE("biCompression = %s\n", debugstr_an((LPSTR)&(bmiHeader->biCompression), 4)); TRACE("biSizeImage = %d\n", bmiHeader->biSizeImage); if (!This->baseControlWindow.baseWindow.hDC) { ERR("Cannot get DC from window!\n"); return E_FAIL; } TRACE("Src Rect: %d %d %d %d\n", This->SourceRect.left, This->SourceRect.top, This->SourceRect.right, This->SourceRect.bottom); TRACE("Dst Rect: %d %d %d %d\n", This->DestRect.left, This->DestRect.top, This->DestRect.right, This->DestRect.bottom); StretchDIBits(This->baseControlWindow.baseWindow.hDC, This->DestRect.left, This->DestRect.top, This->DestRect.right -This->DestRect.left, This->DestRect.bottom - This->DestRect.top, This->SourceRect.left, This->SourceRect.top, This->SourceRect.right - This->SourceRect.left, This->SourceRect.bottom - This->SourceRect.top, data, (BITMAPINFO *)bmiHeader, DIB_RGB_COLORS, SRCCOPY); return S_OK; } static HRESULT WINAPI VideoRenderer_ShouldDrawSampleNow(BaseRenderer *This, IMediaSample *pSample, REFERENCE_TIME *pStartTime, REFERENCE_TIME *pEndTime) { /* Preroll means the sample isn't shown, this is used for key frames and things like that */ if (IMediaSample_IsPreroll(pSample) == S_OK) return E_FAIL; return S_FALSE; } static HRESULT WINAPI VideoRenderer_DoRenderSample(BaseRenderer* iface, IMediaSample * pSample) { VideoRendererImpl *This = impl_from_BaseRenderer(iface); LPBYTE pbSrcStream = NULL; LONG cbSrcStream = 0; HRESULT hr; TRACE("(%p)->(%p)\n", This, pSample); hr = IMediaSample_GetPointer(pSample, &pbSrcStream); if (FAILED(hr)) { ERR("Cannot get pointer to sample data (%x)\n", hr); return hr; } cbSrcStream = IMediaSample_GetActualDataLength(pSample); TRACE("val %p %d\n", pbSrcStream, cbSrcStream); #if 0 /* For debugging purpose */ { int i; for(i = 0; i < cbSrcStream; i++) { if ((i!=0) && !(i%16)) TRACE("\n"); TRACE("%02x ", pbSrcStream[i]); } TRACE("\n"); } #endif SetEvent(This->hEvent); if (This->renderer.filter.state == State_Paused) { VideoRenderer_SendSampleData(This, pbSrcStream, cbSrcStream); SetEvent(This->hEvent); if (This->renderer.filter.state == State_Paused) { /* Flushing */ return S_OK; } if (This->renderer.filter.state == State_Stopped) { return VFW_E_WRONG_STATE; } } else { VideoRenderer_SendSampleData(This, pbSrcStream, cbSrcStream); } return S_OK; } static HRESULT WINAPI VideoRenderer_CheckMediaType(BaseRenderer *iface, const AM_MEDIA_TYPE * pmt) { VideoRendererImpl *This = impl_from_BaseRenderer(iface); if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) return S_FALSE; if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_RGB32) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_RGB24) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_RGB565) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_RGB8)) { LONG height; if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) { VIDEOINFOHEADER *format = (VIDEOINFOHEADER *)pmt->pbFormat; This->SourceRect.left = 0; This->SourceRect.top = 0; This->SourceRect.right = This->VideoWidth = format->bmiHeader.biWidth; height = format->bmiHeader.biHeight; if (height < 0) This->SourceRect.bottom = This->VideoHeight = -height; else This->SourceRect.bottom = This->VideoHeight = height; } else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) { VIDEOINFOHEADER2 *format2 = (VIDEOINFOHEADER2 *)pmt->pbFormat; This->SourceRect.left = 0; This->SourceRect.top = 0; This->SourceRect.right = This->VideoWidth = format2->bmiHeader.biWidth; height = format2->bmiHeader.biHeight; if (height < 0) This->SourceRect.bottom = This->VideoHeight = -height; else This->SourceRect.bottom = This->VideoHeight = height; } else { WARN("Format type %s not supported\n", debugstr_guid(&pmt->formattype)); return S_FALSE; } return S_OK; } return S_FALSE; } static HRESULT WINAPI VideoRenderer_EndFlush(BaseRenderer* iface) { VideoRendererImpl *This = impl_from_BaseRenderer(iface); TRACE("(%p)->()\n", iface); if (This->renderer.pMediaSample) { ResetEvent(This->hEvent); LeaveCriticalSection(iface->pInputPin->pin.pCritSec); LeaveCriticalSection(&iface->csRenderLock); LeaveCriticalSection(&iface->filter.csFilter); WaitForSingleObject(This->hEvent, INFINITE); EnterCriticalSection(&iface->filter.csFilter); EnterCriticalSection(&iface->csRenderLock); EnterCriticalSection(iface->pInputPin->pin.pCritSec); } if (This->renderer.filter.state == State_Paused) { ResetEvent(This->hEvent); } return BaseRendererImpl_EndFlush(iface); } static VOID WINAPI VideoRenderer_OnStopStreaming(BaseRenderer* iface) { VideoRendererImpl *This = impl_from_BaseRenderer(iface); TRACE("(%p)->()\n", This); SetEvent(This->hEvent); if (This->baseControlWindow.AutoShow) /* Black it out */ RedrawWindow(This->baseControlWindow.baseWindow.hWnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE); } static VOID WINAPI VideoRenderer_OnStartStreaming(BaseRenderer* iface) { VideoRendererImpl *This = impl_from_BaseRenderer(iface); TRACE("(%p)\n", This); if (This->renderer.pInputPin->pin.pConnectedTo && (This->renderer.filter.state == State_Stopped || !This->renderer.pInputPin->end_of_stream)) { if (This->renderer.filter.state == State_Stopped) { ResetEvent(This->hEvent); VideoRenderer_AutoShowWindow(This); } } } static LPWSTR WINAPI VideoRenderer_GetClassWindowStyles(BaseWindow *This, DWORD *pClassStyles, DWORD *pWindowStyles, DWORD *pWindowStylesEx) { static const WCHAR classnameW[] = { 'W','i','n','e',' ','A','c','t','i','v','e','M','o','v','i','e',' ','C','l','a','s','s',0 }; *pClassStyles = 0; *pWindowStyles = WS_SIZEBOX; *pWindowStylesEx = 0; return (LPWSTR)classnameW; } static RECT WINAPI VideoRenderer_GetDefaultRect(BaseWindow *iface) { VideoRendererImpl *This = impl_from_BaseWindow(iface); static RECT defRect; defRect.left = defRect.top = 0; defRect.right = This->VideoWidth; defRect.bottom = This->VideoHeight; return defRect; } static BOOL WINAPI VideoRenderer_OnSize(BaseWindow *iface, LONG Width, LONG Height) { VideoRendererImpl *This = impl_from_BaseWindow(iface); TRACE("WM_SIZE %d %d\n", Width, Height); GetClientRect(iface->hWnd, &This->DestRect); TRACE("WM_SIZING: DestRect=(%d,%d),(%d,%d)\n", This->DestRect.left, This->DestRect.top, This->DestRect.right - This->DestRect.left, This->DestRect.bottom - This->DestRect.top); return BaseWindowImpl_OnSize(iface, Width, Height); } static const BaseRendererFuncTable BaseFuncTable = { VideoRenderer_CheckMediaType, VideoRenderer_DoRenderSample, /**/ NULL, NULL, NULL, VideoRenderer_OnStartStreaming, VideoRenderer_OnStopStreaming, NULL, NULL, NULL, VideoRenderer_ShouldDrawSampleNow, NULL, /**/ NULL, NULL, NULL, NULL, VideoRenderer_EndFlush, }; static const BaseWindowFuncTable renderer_BaseWindowFuncTable = { VideoRenderer_GetClassWindowStyles, VideoRenderer_GetDefaultRect, NULL, BaseControlWindowImpl_PossiblyEatMessage, VideoRenderer_OnSize }; static HRESULT WINAPI VideoRenderer_GetSourceRect(BaseControlVideo* iface, RECT *pSourceRect) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); CopyRect(pSourceRect,&This->SourceRect); return S_OK; } static HRESULT WINAPI VideoRenderer_GetStaticImage(BaseControlVideo* iface, LONG *pBufferSize, LONG *pDIBImage) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); BITMAPINFOHEADER *bmiHeader; LONG needed_size; AM_MEDIA_TYPE *amt = &This->renderer.pInputPin->pin.mtCurrent; char *ptr; FIXME("(%p/%p)->(%p, %p): partial stub\n", This, iface, pBufferSize, pDIBImage); EnterCriticalSection(&This->renderer.filter.csFilter); if (!This->renderer.pMediaSample) { LeaveCriticalSection(&This->renderer.filter.csFilter); return (This->renderer.filter.state == State_Paused ? E_UNEXPECTED : VFW_E_NOT_PAUSED); } if (IsEqualIID(&amt->formattype, &FORMAT_VideoInfo)) { bmiHeader = &((VIDEOINFOHEADER *)amt->pbFormat)->bmiHeader; } else if (IsEqualIID(&amt->formattype, &FORMAT_VideoInfo2)) { bmiHeader = &((VIDEOINFOHEADER2 *)amt->pbFormat)->bmiHeader; } else { FIXME("Unknown type %s\n", debugstr_guid(&amt->subtype)); LeaveCriticalSection(&This->renderer.filter.csFilter); return VFW_E_RUNTIME_ERROR; } needed_size = bmiHeader->biSize; needed_size += IMediaSample_GetActualDataLength(This->renderer.pMediaSample); if (!pDIBImage) { *pBufferSize = needed_size; LeaveCriticalSection(&This->renderer.filter.csFilter); return S_OK; } if (needed_size < *pBufferSize) { ERR("Buffer too small %u/%u\n", needed_size, *pBufferSize); LeaveCriticalSection(&This->renderer.filter.csFilter); return E_FAIL; } *pBufferSize = needed_size; memcpy(pDIBImage, bmiHeader, bmiHeader->biSize); IMediaSample_GetPointer(This->renderer.pMediaSample, (BYTE **)&ptr); memcpy((char *)pDIBImage + bmiHeader->biSize, ptr, IMediaSample_GetActualDataLength(This->renderer.pMediaSample)); LeaveCriticalSection(&This->renderer.filter.csFilter); return S_OK; } static HRESULT WINAPI VideoRenderer_GetTargetRect(BaseControlVideo* iface, RECT *pTargetRect) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); CopyRect(pTargetRect,&This->DestRect); return S_OK; } static VIDEOINFOHEADER* WINAPI VideoRenderer_GetVideoFormat(BaseControlVideo* iface) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); AM_MEDIA_TYPE *pmt; TRACE("(%p/%p)\n", This, iface); pmt = &This->renderer.pInputPin->pin.mtCurrent; if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) { return (VIDEOINFOHEADER*)pmt->pbFormat; } else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) { static VIDEOINFOHEADER vih; VIDEOINFOHEADER2 *vih2 = (VIDEOINFOHEADER2*)pmt->pbFormat; memcpy(&vih,vih2,sizeof(VIDEOINFOHEADER)); memcpy(&vih.bmiHeader, &vih2->bmiHeader, sizeof(BITMAPINFOHEADER)); return &vih; } else { ERR("Unknown format type %s\n", qzdebugstr_guid(&pmt->formattype)); return NULL; } } static HRESULT WINAPI VideoRenderer_IsDefaultSourceRect(BaseControlVideo* iface) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); FIXME("(%p/%p)->(): stub !!!\n", This, iface); return S_OK; } static HRESULT WINAPI VideoRenderer_IsDefaultTargetRect(BaseControlVideo* iface) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); FIXME("(%p/%p)->(): stub !!!\n", This, iface); return S_OK; } static HRESULT WINAPI VideoRenderer_SetDefaultSourceRect(BaseControlVideo* iface) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); This->SourceRect.left = 0; This->SourceRect.top = 0; This->SourceRect.right = This->VideoWidth; This->SourceRect.bottom = This->VideoHeight; return S_OK; } static HRESULT WINAPI VideoRenderer_SetDefaultTargetRect(BaseControlVideo* iface) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); RECT rect; if (!GetClientRect(This->baseControlWindow.baseWindow.hWnd, &rect)) return E_FAIL; This->DestRect.left = 0; This->DestRect.top = 0; This->DestRect.right = rect.right; This->DestRect.bottom = rect.bottom; return S_OK; } static HRESULT WINAPI VideoRenderer_SetSourceRect(BaseControlVideo* iface, RECT *pSourceRect) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); CopyRect(&This->SourceRect,pSourceRect); return S_OK; } static HRESULT WINAPI VideoRenderer_SetTargetRect(BaseControlVideo* iface, RECT *pTargetRect) { VideoRendererImpl *This = impl_from_BaseControlVideo(iface); CopyRect(&This->DestRect,pTargetRect); return S_OK; } static const BaseControlVideoFuncTable renderer_BaseControlVideoFuncTable = { VideoRenderer_GetSourceRect, VideoRenderer_GetStaticImage, VideoRenderer_GetTargetRect, VideoRenderer_GetVideoFormat, VideoRenderer_IsDefaultSourceRect, VideoRenderer_IsDefaultTargetRect, VideoRenderer_SetDefaultSourceRect, VideoRenderer_SetDefaultTargetRect, VideoRenderer_SetSourceRect, VideoRenderer_SetTargetRect }; static inline VideoRendererImpl *impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, IUnknown_inner); } static HRESULT WINAPI VideoRendererInner_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) { VideoRendererImpl *This = impl_from_IUnknown(iface); TRACE("(%p/%p)->(%s, %p)\n", This, iface, qzdebugstr_guid(riid), ppv); *ppv = NULL; if (IsEqualIID(riid, &IID_IUnknown)) *ppv = &This->IUnknown_inner; else if (IsEqualIID(riid, &IID_IBasicVideo)) *ppv = &This->baseControlVideo.IBasicVideo_iface; else if (IsEqualIID(riid, &IID_IVideoWindow)) *ppv = &This->baseControlWindow.IVideoWindow_iface; else if (IsEqualIID(riid, &IID_IAMFilterMiscFlags)) *ppv = &This->IAMFilterMiscFlags_iface; else { HRESULT hr; hr = BaseRendererImpl_QueryInterface(&This->renderer.filter.IBaseFilter_iface, riid, ppv); if (SUCCEEDED(hr)) return hr; } if (*ppv) { IUnknown_AddRef((IUnknown *)*ppv); return S_OK; } if (!IsEqualIID(riid, &IID_IPin)) FIXME("No interface for %s!\n", qzdebugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI VideoRendererInner_AddRef(IUnknown *iface) { VideoRendererImpl *This = impl_from_IUnknown(iface); ULONG refCount = BaseFilterImpl_AddRef(&This->renderer.filter.IBaseFilter_iface); TRACE("(%p)->(): new ref = %d\n", This, refCount); return refCount; } static ULONG WINAPI VideoRendererInner_Release(IUnknown *iface) { VideoRendererImpl *This = impl_from_IUnknown(iface); ULONG refCount = BaseRendererImpl_Release(&This->renderer.filter.IBaseFilter_iface); TRACE("(%p)->(): new ref = %d\n", This, refCount); if (!refCount) { BaseControlWindow_Destroy(&This->baseControlWindow); BaseControlVideo_Destroy(&This->baseControlVideo); PostThreadMessageW(This->ThreadID, WM_QUIT, 0, 0); WaitForSingleObject(This->hThread, INFINITE); CloseHandle(This->hThread); CloseHandle(This->hEvent); TRACE("Destroying Video Renderer\n"); CoTaskMemFree(This); return 0; } else return refCount; } static const IUnknownVtbl IInner_VTable = { VideoRendererInner_QueryInterface, VideoRendererInner_AddRef, VideoRendererInner_Release }; static HRESULT WINAPI VideoRenderer_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv) { VideoRendererImpl *This = impl_from_IBaseFilter(iface); return IUnknown_QueryInterface(This->outer_unk, riid, ppv); } static ULONG WINAPI VideoRenderer_AddRef(IBaseFilter * iface) { VideoRendererImpl *This = impl_from_IBaseFilter(iface); return IUnknown_AddRef(This->outer_unk); } static ULONG WINAPI VideoRenderer_Release(IBaseFilter * iface) { VideoRendererImpl *This = impl_from_IBaseFilter(iface); return IUnknown_Release(This->outer_unk); } /** IMediaFilter methods **/ static HRESULT WINAPI VideoRenderer_Pause(IBaseFilter * iface) { VideoRendererImpl *This = impl_from_IBaseFilter(iface); TRACE("(%p/%p)->()\n", This, iface); EnterCriticalSection(&This->renderer.csRenderLock); if (This->renderer.filter.state != State_Paused) { if (This->renderer.filter.state == State_Stopped) { This->renderer.pInputPin->end_of_stream = 0; ResetEvent(This->hEvent); VideoRenderer_AutoShowWindow(This); } ResetEvent(This->renderer.RenderEvent); This->renderer.filter.state = State_Paused; } LeaveCriticalSection(&This->renderer.csRenderLock); return S_OK; } static const IBaseFilterVtbl VideoRenderer_Vtbl = { VideoRenderer_QueryInterface, VideoRenderer_AddRef, VideoRenderer_Release, BaseFilterImpl_GetClassID, BaseRendererImpl_Stop, VideoRenderer_Pause, BaseRendererImpl_Run, BaseRendererImpl_GetState, BaseRendererImpl_SetSyncSource, BaseFilterImpl_GetSyncSource, BaseFilterImpl_EnumPins, BaseRendererImpl_FindPin, BaseFilterImpl_QueryFilterInfo, BaseFilterImpl_JoinFilterGraph, BaseFilterImpl_QueryVendorInfo }; /*** IUnknown methods ***/ static HRESULT WINAPI BasicVideo_QueryInterface(IBasicVideo *iface, REFIID riid, LPVOID *ppvObj) { VideoRendererImpl *This = impl_from_IBasicVideo(iface); TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj); return IUnknown_QueryInterface(This->outer_unk, riid, ppvObj); } static ULONG WINAPI BasicVideo_AddRef(IBasicVideo *iface) { VideoRendererImpl *This = impl_from_IBasicVideo(iface); TRACE("(%p/%p)->()\n", This, iface); return IUnknown_AddRef(This->outer_unk); } static ULONG WINAPI BasicVideo_Release(IBasicVideo *iface) { VideoRendererImpl *This = impl_from_IBasicVideo(iface); TRACE("(%p/%p)->()\n", This, iface); return IUnknown_Release(This->outer_unk); } static const IBasicVideoVtbl IBasicVideo_VTable = { BasicVideo_QueryInterface, BasicVideo_AddRef, BasicVideo_Release, BaseControlVideoImpl_GetTypeInfoCount, BaseControlVideoImpl_GetTypeInfo, BaseControlVideoImpl_GetIDsOfNames, BaseControlVideoImpl_Invoke, BaseControlVideoImpl_get_AvgTimePerFrame, BaseControlVideoImpl_get_BitRate, BaseControlVideoImpl_get_BitErrorRate, BaseControlVideoImpl_get_VideoWidth, BaseControlVideoImpl_get_VideoHeight, BaseControlVideoImpl_put_SourceLeft, BaseControlVideoImpl_get_SourceLeft, BaseControlVideoImpl_put_SourceWidth, BaseControlVideoImpl_get_SourceWidth, BaseControlVideoImpl_put_SourceTop, BaseControlVideoImpl_get_SourceTop, BaseControlVideoImpl_put_SourceHeight, BaseControlVideoImpl_get_SourceHeight, BaseControlVideoImpl_put_DestinationLeft, BaseControlVideoImpl_get_DestinationLeft, BaseControlVideoImpl_put_DestinationWidth, BaseControlVideoImpl_get_DestinationWidth, BaseControlVideoImpl_put_DestinationTop, BaseControlVideoImpl_get_DestinationTop, BaseControlVideoImpl_put_DestinationHeight, BaseControlVideoImpl_get_DestinationHeight, BaseControlVideoImpl_SetSourcePosition, BaseControlVideoImpl_GetSourcePosition, BaseControlVideoImpl_SetDefaultSourcePosition, BaseControlVideoImpl_SetDestinationPosition, BaseControlVideoImpl_GetDestinationPosition, BaseControlVideoImpl_SetDefaultDestinationPosition, BaseControlVideoImpl_GetVideoSize, BaseControlVideoImpl_GetVideoPaletteEntries, BaseControlVideoImpl_GetCurrentImage, BaseControlVideoImpl_IsUsingDefaultSource, BaseControlVideoImpl_IsUsingDefaultDestination }; /*** IUnknown methods ***/ static HRESULT WINAPI VideoWindow_QueryInterface(IVideoWindow *iface, REFIID riid, LPVOID *ppvObj) { VideoRendererImpl *This = impl_from_IVideoWindow(iface); TRACE("(%p/%p)->(%s (%p), %p)\n", This, iface, debugstr_guid(riid), riid, ppvObj); return IUnknown_QueryInterface(This->outer_unk, riid, ppvObj); } static ULONG WINAPI VideoWindow_AddRef(IVideoWindow *iface) { VideoRendererImpl *This = impl_from_IVideoWindow(iface); TRACE("(%p/%p)->()\n", This, iface); return IUnknown_AddRef(This->outer_unk); } static ULONG WINAPI VideoWindow_Release(IVideoWindow *iface) { VideoRendererImpl *This = impl_from_IVideoWindow(iface); TRACE("(%p/%p)->()\n", This, iface); return IUnknown_Release(This->outer_unk); } static HRESULT WINAPI VideoWindow_get_FullScreenMode(IVideoWindow *iface, LONG *FullScreenMode) { VideoRendererImpl *This = impl_from_IVideoWindow(iface); FIXME("(%p/%p)->(%p): stub !!!\n", This, iface, FullScreenMode); return S_OK; } static HRESULT WINAPI VideoWindow_put_FullScreenMode(IVideoWindow *iface, LONG FullScreenMode) { VideoRendererImpl *This = impl_from_IVideoWindow(iface); FIXME("(%p/%p)->(%d): stub !!!\n", This, iface, FullScreenMode); if (FullScreenMode) { This->baseControlWindow.baseWindow.WindowStyles = GetWindowLongW(This->baseControlWindow.baseWindow.hWnd, GWL_STYLE); ShowWindow(This->baseControlWindow.baseWindow.hWnd, SW_HIDE); SetParent(This->baseControlWindow.baseWindow.hWnd, 0); SetWindowLongW(This->baseControlWindow.baseWindow.hWnd, GWL_STYLE, WS_POPUP); SetWindowPos(This->baseControlWindow.baseWindow.hWnd,HWND_TOP,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),SWP_SHOWWINDOW); GetWindowRect(This->baseControlWindow.baseWindow.hWnd, &This->DestRect); This->WindowPos = This->DestRect; } else { ShowWindow(This->baseControlWindow.baseWindow.hWnd, SW_HIDE); SetParent(This->baseControlWindow.baseWindow.hWnd, This->baseControlWindow.hwndOwner); SetWindowLongW(This->baseControlWindow.baseWindow.hWnd, GWL_STYLE, This->baseControlWindow.baseWindow.WindowStyles); GetClientRect(This->baseControlWindow.baseWindow.hWnd, &This->DestRect); SetWindowPos(This->baseControlWindow.baseWindow.hWnd,0,This->DestRect.left,This->DestRect.top,This->DestRect.right,This->DestRect.bottom,SWP_NOZORDER|SWP_SHOWWINDOW); This->WindowPos = This->DestRect; } return S_OK; } static const IVideoWindowVtbl IVideoWindow_VTable = { VideoWindow_QueryInterface, VideoWindow_AddRef, VideoWindow_Release, BaseControlWindowImpl_GetTypeInfoCount, BaseControlWindowImpl_GetTypeInfo, BaseControlWindowImpl_GetIDsOfNames, BaseControlWindowImpl_Invoke, BaseControlWindowImpl_put_Caption, BaseControlWindowImpl_get_Caption, BaseControlWindowImpl_put_WindowStyle, BaseControlWindowImpl_get_WindowStyle, BaseControlWindowImpl_put_WindowStyleEx, BaseControlWindowImpl_get_WindowStyleEx, BaseControlWindowImpl_put_AutoShow, BaseControlWindowImpl_get_AutoShow, BaseControlWindowImpl_put_WindowState, BaseControlWindowImpl_get_WindowState, BaseControlWindowImpl_put_BackgroundPalette, BaseControlWindowImpl_get_BackgroundPalette, BaseControlWindowImpl_put_Visible, BaseControlWindowImpl_get_Visible, BaseControlWindowImpl_put_Left, BaseControlWindowImpl_get_Left, BaseControlWindowImpl_put_Width, BaseControlWindowImpl_get_Width, BaseControlWindowImpl_put_Top, BaseControlWindowImpl_get_Top, BaseControlWindowImpl_put_Height, BaseControlWindowImpl_get_Height, BaseControlWindowImpl_put_Owner, BaseControlWindowImpl_get_Owner, BaseControlWindowImpl_put_MessageDrain, BaseControlWindowImpl_get_MessageDrain, BaseControlWindowImpl_get_BorderColor, BaseControlWindowImpl_put_BorderColor, VideoWindow_get_FullScreenMode, VideoWindow_put_FullScreenMode, BaseControlWindowImpl_SetWindowForeground, BaseControlWindowImpl_NotifyOwnerMessage, BaseControlWindowImpl_SetWindowPosition, BaseControlWindowImpl_GetWindowPosition, BaseControlWindowImpl_GetMinIdealImageSize, BaseControlWindowImpl_GetMaxIdealImageSize, BaseControlWindowImpl_GetRestorePosition, BaseControlWindowImpl_HideCursor, BaseControlWindowImpl_IsCursorHidden }; static VideoRendererImpl *impl_from_IAMFilterMiscFlags(IAMFilterMiscFlags *iface) { return CONTAINING_RECORD(iface, VideoRendererImpl, IAMFilterMiscFlags_iface); } static HRESULT WINAPI AMFilterMiscFlags_QueryInterface(IAMFilterMiscFlags *iface, REFIID riid, void **ppv) { VideoRendererImpl *This = impl_from_IAMFilterMiscFlags(iface); return IUnknown_QueryInterface(This->outer_unk, riid, ppv); } static ULONG WINAPI AMFilterMiscFlags_AddRef(IAMFilterMiscFlags *iface) { VideoRendererImpl *This = impl_from_IAMFilterMiscFlags(iface); return IUnknown_AddRef(This->outer_unk); } static ULONG WINAPI AMFilterMiscFlags_Release(IAMFilterMiscFlags *iface) { VideoRendererImpl *This = impl_from_IAMFilterMiscFlags(iface); return IUnknown_Release(This->outer_unk); } static ULONG WINAPI AMFilterMiscFlags_GetMiscFlags(IAMFilterMiscFlags *iface) { return AM_FILTER_MISC_FLAGS_IS_RENDERER; } static const IAMFilterMiscFlagsVtbl IAMFilterMiscFlags_Vtbl = { AMFilterMiscFlags_QueryInterface, AMFilterMiscFlags_AddRef, AMFilterMiscFlags_Release, AMFilterMiscFlags_GetMiscFlags }; HRESULT VideoRenderer_create(IUnknown *pUnkOuter, void **ppv) { HRESULT hr; VideoRendererImpl * pVideoRenderer; TRACE("(%p, %p)\n", pUnkOuter, ppv); *ppv = NULL; pVideoRenderer = CoTaskMemAlloc(sizeof(VideoRendererImpl)); pVideoRenderer->IUnknown_inner.lpVtbl = &IInner_VTable; pVideoRenderer->IAMFilterMiscFlags_iface.lpVtbl = &IAMFilterMiscFlags_Vtbl; pVideoRenderer->init = 0; ZeroMemory(&pVideoRenderer->SourceRect, sizeof(RECT)); ZeroMemory(&pVideoRenderer->DestRect, sizeof(RECT)); ZeroMemory(&pVideoRenderer->WindowPos, sizeof(RECT)); if (pUnkOuter) pVideoRenderer->outer_unk = pUnkOuter; else pVideoRenderer->outer_unk = &pVideoRenderer->IUnknown_inner; hr = BaseRenderer_Init(&pVideoRenderer->renderer, &VideoRenderer_Vtbl, pUnkOuter, &CLSID_VideoRenderer, (DWORD_PTR)(__FILE__ ": VideoRendererImpl.csFilter"), &BaseFuncTable); if (FAILED(hr)) goto fail; hr = BaseControlWindow_Init(&pVideoRenderer->baseControlWindow, &IVideoWindow_VTable, &pVideoRenderer->renderer.filter, &pVideoRenderer->renderer.filter.csFilter, &pVideoRenderer->renderer.pInputPin->pin, &renderer_BaseWindowFuncTable); if (FAILED(hr)) goto fail; hr = BaseControlVideo_Init(&pVideoRenderer->baseControlVideo, &IBasicVideo_VTable, &pVideoRenderer->renderer.filter, &pVideoRenderer->renderer.filter.csFilter, &pVideoRenderer->renderer.pInputPin->pin, &renderer_BaseControlVideoFuncTable); if (FAILED(hr)) goto fail; if (!CreateRenderingSubsystem(pVideoRenderer)) { hr = E_FAIL; goto fail; } *ppv = &pVideoRenderer->IUnknown_inner; return S_OK; fail: BaseRendererImpl_Release(&pVideoRenderer->renderer.filter.IBaseFilter_iface); CoTaskMemFree(pVideoRenderer); return hr; } HRESULT VideoRendererDefault_create(IUnknown * pUnkOuter, LPVOID * ppv) { /* TODO: Attempt to use the VMR-7 renderer instead when possible */ return VideoRenderer_create(pUnkOuter, ppv); }