From 922d8a20efc650d2af8740b074a95187965ecd3a Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 29 Jul 2008 14:24:02 -0700 Subject: [PATCH] quartz: Make vmr-9 implementation working --- dlls/quartz/tests/vmr9.c | 6 +- dlls/quartz/vmr9.c | 1574 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1559 insertions(+), 21 deletions(-) diff --git a/dlls/quartz/tests/vmr9.c b/dlls/quartz/tests/vmr9.c index 2d3b6e3..4d8d427 100644 --- a/dlls/quartz/tests/vmr9.c +++ b/dlls/quartz/tests/vmr9.c @@ -512,7 +512,7 @@ static int test_obtain_interface(IVMRFilterConfig9 **cfg) return 0; hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRFilterConfig9, (void **)cfg); - todo_wine ok(hr == S_OK, "Could not create IVMRFilterConfig9 interface: %08x\n", hr); + ok(hr == S_OK, "Could not create IVMRFilterConfig9 interface: %08x\n", hr); if (hr != S_OK) { IUnknown_Release(pvmr9); @@ -617,8 +617,8 @@ static void test_query_interface(void) iface = NULL; hr = IUnknown_QueryInterface(pvmr9, &IID_IVMRFilterConfig9, (LPVOID*)&iface); - todo_wine ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); - todo_wine ok(iface != NULL, "Pointer is NULL\n"); + ok(hr == S_OK, "IUnknown_QueryInterface returned %x\n", hr); + ok(iface != NULL, "Pointer is NULL\n"); if (iface) { ref = IUnknown_Release(iface); diff --git a/dlls/quartz/vmr9.c b/dlls/quartz/vmr9.c index 5d8ff6d..f949af6 100644 --- a/dlls/quartz/vmr9.c +++ b/dlls/quartz/vmr9.c @@ -46,17 +46,39 @@ WINE_DEFAULT_DEBUG_CHANNEL(quartz); - static const WCHAR wcsInputPinName[] = {'i','n','p','u','t',' ','p','i','n',0}; static const IBaseFilterVtbl VMR9_Vtbl; static const IUnknownVtbl IInner_VTable; static const IPinVtbl VMR9_InputPin_Vtbl; +static const IVMRFilterConfig9Vtbl VMR9_FilterConfig_Vtbl; +static const IVMRWindowlessControl9Vtbl VMR9_WindowlessControl_Vtbl; +static const IVMRSurfaceAllocatorNotify9Vtbl IVMRSurfaceAllocatorNotify9_Vtbl; + +/* Own implementations of IVMR9 stuff, for when not in renderless mode */ +static const IVMRImagePresenter9Vtbl VMR9_ImagePresenter; +static const IVMRSurfaceAllocatorEx9Vtbl VMR9_SurfaceAllocator; typedef struct VMR9Impl { const IBaseFilterVtbl * lpVtbl; - const IUnknownVtbl *IInner_vtbl; + const IVMRFilterConfig9Vtbl *IVMRFilterConfig9_vtbl; + const IVMRWindowlessControl9Vtbl *IVMRWindowlessControl9_vtbl; + const IVMRSurfaceAllocatorNotify9Vtbl *IVMRSurfaceAllocatorNotify9_vtbl; + const IUnknownVtbl * IInner_vtbl; + + IVMRSurfaceAllocatorEx9 *allocator; + BOOL allocator_is_ex; + IVMRImagePresenter9 *presenter; + /* The Video Mixing Renderer supports 3 modes, renderless, windowless and windowed + * What I do is implement windowless as a special case of renderless, and then + * windowed also as a special case of windowless. This is probably the easiest way. + * + * Since these interfaces are internal reference counts are not needed and queryinterface isn't implemented + * ImagePresenter will probably be the same, the surface allocator isn't + */ + const IVMRSurfaceAllocatorEx9Vtbl *surfvtbl; + const IVMRImagePresenter9Vtbl *imgvtbl; /* VMR9 basic filter */ LONG ref; @@ -64,7 +86,7 @@ typedef struct VMR9Impl CRITICAL_SECTION csFilter; FILTER_STATE state; - REFERENCE_TIME rtStreamStart, rtLastStop; + REFERENCE_TIME rtStreamStart; IReferenceClock * pClock; FILTER_INFO filterInfo; VMR9Mode mode; @@ -74,9 +96,31 @@ typedef struct VMR9Impl BOOL bUnkOuterValid; BOOL bAggregatable; MediaSeekingImpl mediaSeeking; - IMediaSample *sample_held; - HANDLE state_handle, blocked, ack; + /* Presentation related members */ + HMODULE d3d9_handle; + IDirect3D9 *d3d9_ptr; + IDirect3DDevice9 *d3d9_dev; + IDirect3DVertexBuffer9 *d3d9_vertex; + IDirect3DSurface9 **d3d9_surfaces; + DWORD num_surfaces; + DWORD cur_surface; + REFERENCE_TIME rtLastStop; + VMR9AllocationInfo info; + DWORD_PTR cookie; + HWND hwnd_last, hwnd_mine; + HDC hdc_last; + HMONITOR mon_last; + HANDLE thread_hwnd, state_handle, blocked; + DWORD tid; + UINT syncmsg; + + HANDLE ack; + + BOOL reset; + RECT source_rect; + RECT target_rect; + /* Memory allocator things */ /* Preferred memory allocator for input pins, no others are currently accepted */ BaseMemAllocator memalloc; @@ -99,6 +143,75 @@ static const IMemInputPinVtbl MemInputPin_Vtbl = static void VMR9Impl_destroy(VMR9Impl *This); +static DWORD VMR9_SendSampleData(VMR9Impl *This, VMR9PresentationInfo *info, LPBYTE data, DWORD size) +{ + AM_MEDIA_TYPE *amt; + HRESULT hr = S_OK; + int width; + int height; + LPBYTE palette = NULL; + BITMAPINFOHEADER *bmiHeader; + D3DLOCKED_RECT lock; + + TRACE("%p %p %d\n", This, data, size); + + amt = &This->pInputPin->pin.mtCurrent; + + 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); + + width = bmiHeader->biWidth; + height = bmiHeader->biHeight; + palette = ((LPBYTE)bmiHeader) + bmiHeader->biSize; + + TRACE("Src Rect: %d %d %d %d\n", This->source_rect.left, This->source_rect.top, This->source_rect.right, This->source_rect.bottom); + TRACE("Dst Rect: %d %d %d %d\n", This->target_rect.left, This->target_rect.top, This->target_rect.right, This->target_rect.bottom); + + assert(!!This->thread_hwnd); + + hr = IDirect3DSurface9_LockRect(info->lpSurf, &lock, NULL, D3DLOCK_DISCARD); + assert(hr == S_OK); + + if (lock.Pitch != width * bmiHeader->biBitCount / 8) + { + WARN("Slow path! %u/%u\n", lock.Pitch, width * bmiHeader->biBitCount/8); + + while (height--) + { + memcpy(lock.pBits, data, width * bmiHeader->biBitCount / 8); + data = data + width * bmiHeader->biBitCount / 8; + lock.pBits = (char *)lock.pBits + lock.Pitch; + } + } + else memcpy(lock.pBits, data, size); + + IDirect3DSurface9_UnlockRect(info->lpSurf); + + hr = IVMRImagePresenter9_PresentImage(This->presenter, This->cookie, info); + return hr; +} + +static HRESULT VMR9_SurfaceAllocator_UpdateDeviceReset(VMR9Impl *This); + static HRESULT VMR9_Sample(LPVOID iface, IMediaSample * pSample) { VMR9Impl *This = (VMR9Impl *)iface; @@ -123,6 +236,22 @@ static HRESULT VMR9_Sample(LPVOID iface, IMediaSample * pSample) TRACE("%p %p\n", iface, pSample); + if (!This->hwnd_mine) + { + ERR("No window!\n"); + LeaveCriticalSection(&This->csFilter); + return S_FALSE; + } + + /* It is possible that there is no device at this point */ + + if (!This->allocator || !This->presenter) + { + ERR("NO PRESENTER!!\n"); + LeaveCriticalSection(&This->csFilter); + return S_FALSE; + } + hr = IMediaSample_GetTime(pSample, &tStart, &tStop); if (FAILED(hr)) info.dwFlags = VMR9Sample_SrcDstRectsValid; @@ -156,6 +285,13 @@ static HRESULT VMR9_Sample(LPVOID iface, IMediaSample * pSample) } This->rtLastStop = tStop; + /* If we render ourselves, and this is a preroll sample, discard it */ + if (This->hwnd_mine && (info.dwFlags & VMR9Sample_Preroll)) + { + LeaveCriticalSection(&This->csFilter); + return S_OK; + } + hr = IMediaSample_GetPointer(pSample, &pbSrcStream); if (FAILED(hr)) { @@ -213,20 +349,46 @@ static HRESULT VMR9_Sample(LPVOID iface, IMediaSample * pSample) info.szAspectRatio.cx = This->bmiheader.biWidth; info.szAspectRatio.cy = This->bmiheader.biHeight; - SetEvent(This->state_handle); - if (This->state == State_Paused) + if (!This->hwnd_mine) { - This->sample_held = pSample; + ERR("NO WINDOW!\n"); LeaveCriticalSection(&This->csFilter); - WaitForSingleObject(This->blocked, INFINITE); - This->sample_held = NULL; + return S_FALSE; } - else - LeaveCriticalSection(&This->csFilter); + + IMediaSample_AddRef(pSample); + + hr = SendMessageW(This->hwnd_mine, This->syncmsg, (WPARAM)pSample, (LPARAM)&info); + + SetEvent(This->state_handle); + LeaveCriticalSection(&This->csFilter); + if (This->state == State_Paused) + WaitForSingleObject(This->blocked, INFINITE); return hr; } +static UINT d3d9_adapter_from_hwnd(VMR9Impl *This, HWND hwnd) +{ + UINT d3d9_adapter; + RECT window; + + This->mon_last = MonitorFromRect(&window, MONITOR_DEFAULTTONULL); + if (!This->mon_last) + d3d9_adapter = 0; + else + { + for (d3d9_adapter = 0; d3d9_adapter < IDirect3D9_GetAdapterCount(This->d3d9_ptr); ++d3d9_adapter) + { + if (This->mon_last == IDirect3D9_GetAdapterMonitor(This->d3d9_ptr, d3d9_adapter)) + break; + } + if (d3d9_adapter >= IDirect3D9_GetAdapterCount(This->d3d9_ptr)) + d3d9_adapter = 0; + } + return d3d9_adapter; +} + static HRESULT VMR9_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt) { VMR9Impl *This = iface; @@ -241,6 +403,9 @@ static HRESULT VMR9_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt) This->bmiheader = format->bmiHeader; TRACE("Resolution: %dx%d\n", format->bmiHeader.biWidth, format->bmiHeader.biHeight); + This->source_rect.right = format->bmiHeader.biWidth; + This->source_rect.bottom = format->bmiHeader.biHeight; + This->source_rect.top = This->source_rect.left = 0; } else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2)) { @@ -248,15 +413,27 @@ static HRESULT VMR9_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt) This->bmiheader = format->bmiHeader; - TRACE("Resolution: %dx%d\n", format->bmiHeader.biWidth, format->bmiHeader.biHeight); + ERR("Resolution: %dx%d\n", format->bmiHeader.biWidth, format->bmiHeader.biHeight); + This->source_rect.right = format->bmiHeader.biWidth; + This->source_rect.bottom = format->bmiHeader.biHeight; + This->source_rect.top = This->source_rect.left = 0; } else { ERR("Format type %s not supported\n", debugstr_guid(&pmt->formattype)); return S_FALSE; } + ERR("BiCompression: %s\n", debugstr_an((char *)&This->bmiheader.biCompression, 4)); if (This->bmiheader.biCompression) - return S_FALSE; + { + /* Test out stuff */ + UINT adapter = d3d9_adapter_from_hwnd(This, This->hwnd_mine); + HRESULT hr; + + hr = IDirect3D9_CheckDeviceFormat(This->d3d9_ptr, adapter, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, 0, D3DRTYPE_TEXTURE, This->bmiheader.biCompression); + ERR("HR is: %08x (%s)\n", hr, (hr == D3DERR_INVALIDCALL ? "invalid call" : (hr == D3DERR_NOTAVAILABLE ? "unsupported format" : "unknown"))); + return (FAILED(hr)) ? S_FALSE : S_OK; + } return S_OK; } @@ -309,6 +486,16 @@ static HRESULT VMR9Impl_Change(IBaseFilter *iface) return S_OK; } +static IDirect3D9 *init_d3d9(HMODULE d3d9_handle) +{ + IDirect3D9 * (__stdcall * d3d9_create)(UINT SDKVersion); + + d3d9_create = (void *)GetProcAddress(d3d9_handle, "Direct3DCreate9"); + if (!d3d9_create) return NULL; + + return d3d9_create(D3D_SDK_VERSION); +} + static HRESULT VMR9_MemoryAllocator_Alloc(IMemAllocator *iface) { ICOM_THIS_MULTI(VMR9Impl, memalloc, iface); @@ -411,6 +598,7 @@ HRESULT VMR9Impl_create(IUnknown * pUnkOuter, LPVOID * ppv) HRESULT hr; PIN_INFO piInput; VMR9Impl * pVMR9; + int x; ERR("(%p, %p)\n", pUnkOuter, ppv); @@ -418,14 +606,51 @@ HRESULT VMR9Impl_create(IUnknown * pUnkOuter, LPVOID * ppv) pVMR9 = CoTaskMemAlloc(sizeof(VMR9Impl)); + pVMR9->d3d9_handle = LoadLibraryA("d3d9.dll"); + if (!pVMR9->d3d9_handle) + { + WARN("Could not load d3d9.dll\n"); + CoTaskMemFree(pVMR9); + return VFW_E_DDRAW_CAPS_NOT_SUITABLE; + } + + pVMR9->d3d9_ptr = init_d3d9(pVMR9->d3d9_handle); + if (!pVMR9->d3d9_ptr) + { + WARN("Could not initialize d3d9.dll\n"); + CloseHandle(pVMR9->d3d9_handle); + CoTaskMemFree(pVMR9); + return VFW_E_DDRAW_CAPS_NOT_SUITABLE; + } + x = 0; + do + { + D3DDISPLAYMODE mode; + + hr = IDirect3D9_EnumAdapterModes(pVMR9->d3d9_ptr, x++, D3DFMT_X8R8G8B8, 0, &mode); + } while (hr == S_OK); + ERR("HR: %08x\n", hr); + if (hr == D3DERR_NOTAVAILABLE) + { + ERR("Format not supported\n"); + IUnknown_Release(pVMR9->d3d9_ptr); + CloseHandle(pVMR9->d3d9_handle); + CoTaskMemFree(pVMR9); + return VFW_E_DDRAW_CAPS_NOT_SUITABLE; + } + pVMR9->pUnkOuter = pUnkOuter; pVMR9->bUnkOuterValid = FALSE; pVMR9->bAggregatable = FALSE; pVMR9->IInner_vtbl = &IInner_VTable; pVMR9->lpVtbl = &VMR9_Vtbl; + pVMR9->IVMRFilterConfig9_vtbl = &VMR9_FilterConfig_Vtbl; + pVMR9->IVMRWindowlessControl9_vtbl = &VMR9_WindowlessControl_Vtbl; + pVMR9->IVMRSurfaceAllocatorNotify9_vtbl = &IVMRSurfaceAllocatorNotify9_Vtbl; pVMR9->ref = 1; + pVMR9->ref_surface = 0; InitializeCriticalSection(&pVMR9->csFilter); pVMR9->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": VMR9Impl.csFilter"); @@ -433,9 +658,21 @@ HRESULT VMR9Impl_create(IUnknown * pUnkOuter, LPVOID * ppv) pVMR9->pClock = NULL; ZeroMemory(&pVMR9->filterInfo, sizeof(pVMR9->filterInfo)); pVMR9->mode = 0; + pVMR9->d3d9_dev = NULL; + pVMR9->d3d9_surfaces = NULL; + pVMR9->d3d9_vertex = NULL; + pVMR9->num_surfaces = pVMR9->cur_surface = 0; pVMR9->rtLastStop = -1; pVMR9->ack = CreateEventW(NULL, 0, 0, NULL); + pVMR9->allocator = NULL; + pVMR9->presenter = NULL; + pVMR9->hwnd_last = pVMR9->hwnd_mine = NULL; + pVMR9->hdc_last = NULL; + pVMR9->mon_last = NULL; + pVMR9->reset = 0; + pVMR9->sample_memory = NULL; + /* construct input pin */ piInput.dir = PINDIR_INPUT; piInput.pFilter = (IBaseFilter *)pVMR9; @@ -450,7 +687,7 @@ HRESULT VMR9Impl_create(IUnknown * pUnkOuter, LPVOID * ppv) &pVMR9->csFilter, &pVMR9->memalloc); - hr = InputPin_Construct(&VMR9_InputPin_Vtbl, &piInput, VMR9_Sample, (LPVOID)pVMR9, VMR9_QueryAccept, NULL, &pVMR9->csFilter, NULL, (IPin **)&pVMR9->pInputPin); + hr = InputPin_Construct(&VMR9_InputPin_Vtbl, &piInput, VMR9_Sample, (LPVOID)pVMR9, VMR9_QueryAccept, NULL, &pVMR9->csFilter, (IMemAllocator *)&pVMR9->memalloc, (IPin **)&pVMR9->pInputPin); if (SUCCEEDED(hr)) { @@ -458,14 +695,20 @@ HRESULT VMR9Impl_create(IUnknown * pUnkOuter, LPVOID * ppv) pVMR9->mediaSeeking.lpVtbl = &VMR9_MediaSeeking_Vtbl; *ppv = (LPVOID)pVMR9; - TRACE("Created at %p\n", pVMR9); + ZeroMemory(&pVMR9->source_rect, sizeof(RECT)); + ZeroMemory(&pVMR9->target_rect, sizeof(RECT)); + pVMR9->syncmsg = RegisterWindowMessageA("IVMR9 message for drawing a single surface"); + ERR("Created at %p\n", pVMR9); pVMR9->state_handle = CreateEventW(NULL, TRUE, TRUE, NULL); pVMR9->blocked = CreateEventW(NULL, FALSE, FALSE, NULL); } else { + IMemAllocator_Release((IMemAllocator *)&pVMR9->memalloc); pVMR9->csFilter.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&pVMR9->csFilter); + IUnknown_Release(pVMR9->d3d9_ptr); + CloseHandle(pVMR9->d3d9_handle); CoTaskMemFree(pVMR9); } @@ -476,7 +719,7 @@ static void VMR9Impl_destroy(VMR9Impl *This) { IPin *pConnectedTo; - TRACE("Destroying\n"); + ERR("Destroying\n"); if (This->pClock) IReferenceClock_Release(This->pClock); @@ -488,6 +731,11 @@ static void VMR9Impl_destroy(VMR9Impl *This) IPin_Disconnect((IPin *)This->pInputPin); IPin_Release((IPin *)This->pInputPin); + IMemAllocator_Release((IMemAllocator *)&This->memalloc); + + IUnknown_Release(This->d3d9_ptr); + CloseHandle(This->d3d9_handle); + This->csFilter.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&This->csFilter); @@ -499,6 +747,283 @@ static void VMR9Impl_destroy(VMR9Impl *This) CoTaskMemFree(This); } +static LRESULT CALLBACK VideoWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + VMR9Impl* pVMR9; + LPRECT lprect = (LPRECT)lParam; + + pVMR9 = (VMR9Impl *)GetWindowLongW(hwnd, 0); + if (!pVMR9) + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + + if (uMsg == pVMR9->syncmsg) + { + IMediaSample *sample = (IMediaSample *)wParam; + VMR9PresentationInfo *info = (VMR9PresentationInfo *)lParam; + HRESULT hr; + BYTE *pbSrcStream; + DWORD cbSrcStream; + + IMediaSample_GetPointer(sample, &pbSrcStream); + cbSrcStream = IMediaSample_GetActualDataLength(sample); + + /* Update everything first, this is needed because the surface might be destroyed in the reset */ + VMR9_SurfaceAllocator_UpdateDeviceReset(pVMR9); + + if (!pVMR9->d3d9_dev) + { + TRACE("Device has left me!\n"); + IMediaSample_Release(sample); + return S_OK; + } + + hr = IVMRSurfaceAllocator9_GetSurface(pVMR9->allocator, pVMR9->cookie, (++pVMR9->cur_surface)%pVMR9->num_surfaces, 0, &info->lpSurf); + + if (FAILED(hr)) + { + IMediaSample_Release(sample); + return hr; + } + + VMR9_SendSampleData(pVMR9, info, pbSrcStream, cbSrcStream); + IMediaSample_Release(info->lpSurf); + IMediaSample_Release(sample); + return hr; + } + + switch(uMsg) + { + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *pos = (WINDOWPOS *)lParam; + RECT rect; + rect.top = pos->y; + rect.left = pos->x; + rect.bottom = rect.top + pos->cy; + rect.right = rect.left + pos->cx; + + lprect = ▭ + ERR("WM_WINDOWPOSCHANGING %d %d %d %d\n", lprect->left, lprect->top, lprect->right, lprect->bottom); + return TRUE; + } + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS *pos = (WINDOWPOS *)lParam; + RECT rect; + rect.top = pos->y; + rect.left = pos->x; + rect.bottom = rect.top + pos->cy; + rect.right = rect.left + pos->cx; + lprect = ▭ + + FIXME("Flags: %08x\n", pos->flags); + + ERR("WM_WINDOWPOSCHANGED %d %d %d %d\n", lprect->left, lprect->top, lprect->right, lprect->bottom); + GetWindowRect(hwnd, &pVMR9->target_rect); + if (pVMR9->mode == VMR9Mode_Windowed) + { + pVMR9->target_rect.left += GetSystemMetrics(SM_CXFRAME); + pVMR9->target_rect.right -= GetSystemMetrics(SM_CXFRAME); + pVMR9->target_rect.top += GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); + pVMR9->target_rect.bottom -= GetSystemMetrics(SM_CYFRAME); + } + if (!(pos->flags & SWP_NOSIZE)) + pVMR9->reset = TRUE; + return TRUE; + } + + case WM_SIZING: + ERR("WM_SIZING %d %d %d %d\n", lprect->left, lprect->top, lprect->right, lprect->bottom); + SetWindowPos(hwnd, NULL, lprect->left, lprect->top, lprect->right - lprect->left, lprect->bottom - lprect->top, SWP_NOZORDER); + ERR("WM_SIZING: target_rect=(%d,%d),(%d,%d)\n", + pVMR9->target_rect.left, + pVMR9->target_rect.top, + pVMR9->target_rect.right - pVMR9->target_rect.left, + pVMR9->target_rect.bottom - pVMR9->target_rect.top); + return TRUE; + + case WM_SIZE: + ERR("WM_SIZE %d %d\n", LOWORD(lParam), HIWORD(lParam)); + GetClientRect(hwnd, &pVMR9->target_rect); + ERR("WM_SIZE: target_rect=(%d,%d),(%d,%d)\n", + pVMR9->target_rect.left, + pVMR9->target_rect.top, + pVMR9->target_rect.right - pVMR9->target_rect.left, + pVMR9->target_rect.bottom - pVMR9->target_rect.top); + return TRUE; + + case WM_ERASEBKGND: + return TRUE; + + case WM_DESTROY: + { + int x; + + TRACE("Number of surfaces: %u\n", pVMR9->num_surfaces); + for (x = 0; x < pVMR9->num_surfaces; ++x) + { + IDirect3DSurface9 *surface = pVMR9->d3d9_surfaces[x]; + TRACE("Releasing surface %p\n", surface); + if (surface) + IUnknown_Release(surface); + } + + CoTaskMemFree(pVMR9->d3d9_surfaces); + pVMR9->d3d9_surfaces = NULL; + pVMR9->num_surfaces = 0; + if (pVMR9->d3d9_vertex) + { + IUnknown_Release(pVMR9->d3d9_vertex); + pVMR9->d3d9_vertex = NULL; + } + if (pVMR9->d3d9_dev) + { + IUnknown_Release(pVMR9->d3d9_dev); + pVMR9->d3d9_dev = NULL; + } + } + + default: + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + return 0; +} + +static WCHAR classname[] = { 'I','V','M','R','9',' ','C','l','a','s','s', 0 }; +static WCHAR windowname[] = { 'I','V','M','R','9',' ','W','i','n','d','o','w', 0 }; +static int wnd_class_registered; + +static HRESULT VMR9_SurfaceAllocator_SetAllocationSettings(VMR9Impl *This, VMR9AllocationInfo *allocinfo); + +static DWORD WINAPI MessageLoop(LPVOID lpParameter) +{ + MSG msg; + BOOL fGotMessage; + VMR9Impl *This = lpParameter; + WNDCLASSW winclass; + + winclass.style = 0; + winclass.lpfnWndProc = VideoWndProcW; + winclass.cbClsExtra = 0; + winclass.cbWndExtra = sizeof(VMR9Impl *); + winclass.hInstance = 0; + winclass.hIcon = NULL; + winclass.hCursor = NULL; + winclass.hbrBackground = GetStockObject(BLACK_BRUSH); + winclass.lpszMenuName = NULL; + winclass.lpszClassName = classname; + + if (!wnd_class_registered) + { + if (!RegisterClassW(&winclass)) + { + ERR("Unable to register window %u\n", GetLastError()); + return FALSE; + } + wnd_class_registered = TRUE; + } + + if (This->mode == VMR9Mode_Windowed) + { + if (!This->target_rect.bottom || !This->target_rect.right) + { + This->target_rect = This->source_rect; + This->target_rect.right += 2 * GetSystemMetrics(SM_CXFRAME); + This->target_rect.bottom += 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); + } + This->hwnd_mine = CreateWindowExW(0, winclass.lpszClassName, windowname, WS_SIZEBOX, + This->target_rect.left, This->target_rect.top, + This->target_rect.right - This->target_rect.left, + This->target_rect.bottom - This->target_rect.top, + NULL, NULL, NULL, NULL); + } + else + This->hwnd_mine = CreateWindowExW(WS_EX_NOPARENTNOTIFY, winclass.lpszClassName, windowname, WS_CHILD, + This->target_rect.left, This->target_rect.top, + This->target_rect.right - This->target_rect.left, + This->target_rect.bottom - This->target_rect.top, + This->hwnd_last, NULL, NULL, NULL); + + SetWindowLongW(This->hwnd_mine, 0, (LONG)This); + SetEvent(This->ack); + + TRACE("Starting message loop\n"); + + while ((fGotMessage = GetMessageW(&msg, NULL, 0, 0)) != 0 && fGotMessage != -1) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + TRACE("End of message loop\n"); + + return 0; +} + +static BOOL CreateRenderingWindow(VMR9Impl *This, VMR9AllocationInfo *info, DWORD *numbuffers) +{ + D3DPRESENT_PARAMETERS d3dpp; + DWORD d3d9_adapter; + HRESULT hr; + + TRACE("(%p)->()\n", This); + + This->thread_hwnd = CreateThread(NULL, 0, MessageLoop, This, 0, &This->tid); + if (!This->thread_hwnd) + return FALSE; + + WaitForSingleObject(This->ack, INFINITE); + + if (!This->hwnd_mine) return FALSE; + + /* Obtain a monitor and d3d9 device */ + d3d9_adapter = d3d9_adapter_from_hwnd(This, This->hwnd_mine); + + /* Now try to create the d3d9 device */ + ZeroMemory(&d3dpp, sizeof(d3dpp)); + d3dpp.Windowed = TRUE; + d3dpp.hDeviceWindow = This->hwnd_mine; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.BackBufferHeight = This->target_rect.bottom - This->target_rect.top; + d3dpp.BackBufferWidth = This->target_rect.right - This->target_rect.left; + + hr = IDirect3D9_CreateDevice(This->d3d9_ptr, d3d9_adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &This->d3d9_dev); + if (FAILED(hr)) + { + ERR("Could not create device: %08x\n", hr); + DestroyWindow(This->hwnd_mine); + This->hwnd_mine = NULL; + return FALSE; + } + + This->d3d9_surfaces = CoTaskMemAlloc(*numbuffers * sizeof(IDirect3DSurface9 *)); + ZeroMemory(This->d3d9_surfaces, *numbuffers * sizeof(IDirect3DSurface9 *)); + + This->info = *info; + hr = VMR9_SurfaceAllocator_SetAllocationSettings(This, info); + if (FAILED(hr)) + ERR("Setting allocation settings failed: %08x\n", hr); + + /* Don't update This->num_surfaces here, should be done by the caller */ + if (SUCCEEDED(hr)) + { + hr = IVMRSurfaceAllocatorNotify9_AllocateSurfaceHelper((IVMRSurfaceAllocatorNotify9 *)&This->IVMRSurfaceAllocatorNotify9_vtbl, + info, numbuffers, This->d3d9_surfaces); + if (FAILED(hr)) + ERR("Allocating surfaces failed: %08x\n", hr); + } + + if (FAILED(hr)) + { + IVMRSurfaceAllocatorEx9_TerminateDevice(This->allocator, This->cookie); + DestroyWindow(This->hwnd_mine); + This->hwnd_mine = NULL; + return FALSE; + } + + return TRUE; +} + static HRESULT WINAPI VMR9Inner_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv) { ICOM_THIS_MULTI(VMR9Impl, IInner_vtbl, iface); @@ -519,6 +1044,12 @@ static HRESULT WINAPI VMR9Inner_QueryInterface(IUnknown * iface, REFIID riid, LP *ppv = (LPVOID)This; else if (IsEqualIID(riid, &IID_IMediaSeeking)) *ppv = &This->mediaSeeking; + else if (IsEqualIID(riid, &IID_IVMRFilterConfig9)) + *ppv = &This->IVMRFilterConfig9_vtbl; + else if (IsEqualIID(riid, &IID_IVMRWindowlessControl9) && This->mode == VMR9Mode_Windowless) + *ppv = &This->IVMRWindowlessControl9_vtbl; + else if (IsEqualIID(riid, &IID_IVMRSurfaceAllocatorNotify9) && This->mode == VMR9Mode_Renderless) + *ppv = &This->IVMRSurfaceAllocatorNotify9_vtbl; if (*ppv) { @@ -678,6 +1209,9 @@ static HRESULT WINAPI VMR9_Stop(IBaseFilter * iface) EnterCriticalSection(&This->csFilter); { + if (This->state == State_Running) + IVMRImagePresenter9_StopPresenting(This->presenter, This->cookie); + This->state = State_Stopped; SetEvent(This->state_handle); SetEvent(This->blocked); @@ -699,9 +1233,16 @@ static HRESULT WINAPI VMR9_Pause(IBaseFilter * iface) if (This->state == State_Stopped) { This->pInputPin->end_of_stream = 0; + if (!This->presenter || !This->allocator) + { + ERR("Not ready!\n"); + hr = VFW_E_WRONG_STATE; + } if (SUCCEEDED(hr)) ResetEvent(This->state_handle); } + else if (This->state == State_Running) + IVMRImagePresenter9_StopPresenting(This->presenter, This->cookie); if (SUCCEEDED(hr)) { This->state = State_Paused; @@ -713,6 +1254,65 @@ static HRESULT WINAPI VMR9_Pause(IBaseFilter * iface) return hr; } +static HRESULT VMR9_maybe_init(VMR9Impl *This, BOOL force) +{ + VMR9AllocationInfo info; + DWORD buffers; + HRESULT hr; + + ERR("my mode: %u, my window: %p, my last window: %p\n", This->mode, This->hwnd_mine, This->hwnd_last); + if (This->hwnd_mine || !This->pInputPin->pin.pConnectedTo) + return S_OK; + + if (This->mode == VMR9Mode_Windowless && !This->hwnd_last) + return (force ? VFW_E_RUNTIME_ERROR : S_OK); + + ERR("Initializing\n"); +// info.dwFlags = VMR9AllocFlag_TextureSurface; + info.dwFlags = VMR9AllocFlag_OffscreenSurface; + info.dwHeight = This->source_rect.bottom; + info.dwWidth = This->source_rect.right; + info.Pool = D3DPOOL_DEFAULT; + info.MinBuffers = 2; + FIXME("Reduce ratio to least common denominator\n"); + info.szAspectRatio.cx = info.dwWidth; + info.szAspectRatio.cy = info.dwHeight; + info.szNativeSize.cx = This->bmiheader.biWidth; + info.szNativeSize.cy = This->bmiheader.biHeight; + buffers = 2; + + switch (This->bmiheader.biBitCount) + { + case 8: info.Format = D3DFMT_R3G3B2; break; + case 15: info.Format = D3DFMT_X1R5G5B5; break; + case 16: info.Format = D3DFMT_R5G6B5; break; + case 24: info.Format = D3DFMT_R8G8B8; break; + case 32: info.Format = D3DFMT_X8R8G8B8; break; + default: + FIXME("Unknown bpp %u\n", This->bmiheader.biBitCount); + hr = E_INVALIDARG; + } + + This->cur_surface = 0; + assert(This->num_surfaces == 0); + assert(!This->d3d9_surfaces); + hr = IVMRSurfaceAllocatorEx9_InitializeDevice(This->allocator, This->cookie, &info, &buffers); + if (SUCCEEDED(hr)) + { + This->source_rect.left = This->source_rect.top = 0; + This->source_rect.right = This->bmiheader.biWidth; + This->source_rect.bottom = This->bmiheader.biHeight; + + This->num_surfaces = buffers; + } + else + { + assert(!This->d3d9_surfaces); + This->num_surfaces = 0; + } + return hr; +} + static HRESULT WINAPI VMR9_Run(IBaseFilter * iface, REFERENCE_TIME tStart) { VMR9Impl *This = (VMR9Impl *)iface; @@ -721,6 +1321,7 @@ static HRESULT WINAPI VMR9_Run(IBaseFilter * iface, REFERENCE_TIME tStart) TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(tStart)); EnterCriticalSection(&This->csFilter); + hr = VMR9_maybe_init(This, TRUE); if (SUCCEEDED(hr)) { This->rtStreamStart = tStart; @@ -731,11 +1332,21 @@ static HRESULT WINAPI VMR9_Run(IBaseFilter * iface, REFERENCE_TIME tStart) /* Kick back to live if paused */ SetEvent(This->blocked); - } + if (!This->presenter || !This->allocator) + { + ERR("Not ready!\n"); + hr = VFW_E_WRONG_STATE; + } + else hr = IVMRImagePresenter9_StartPresenting(This->presenter, This->cookie); + } + ShowWindow(This->hwnd_mine, SW_SHOW); if (SUCCEEDED(hr)) This->state = State_Running; This->pInputPin->end_of_stream = 0; + + /* Reset device1 */ + This->reset = TRUE; } LeaveCriticalSection(&This->csFilter); @@ -919,6 +1530,7 @@ static HRESULT WINAPI VMR9_InputPin_EndOfStream(IPin * iface) static HRESULT WINAPI VMR9_InputPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt) { InputPin *This = (InputPin *)iface; + VMR9Impl *pVMR9 = (VMR9Impl *)This->pin.pinInfo.pFilter; HRESULT hr = S_OK; TRACE("()\n"); @@ -931,6 +1543,11 @@ static HRESULT WINAPI VMR9_InputPin_ReceiveConnection(IPin * iface, IPin * pRece if (SUCCEEDED(hr)) assert(IsEqualIID(&This->pin.mtCurrent.majortype, &MEDIATYPE_Video)); + if (!pVMR9->mode && SUCCEEDED(hr)) + hr = IVMRFilterConfig9_SetRenderingMode((IVMRFilterConfig9 *)&pVMR9->IVMRFilterConfig9_vtbl, VMR9Mode_Windowed); + + if (SUCCEEDED(hr)) + hr = VMR9_maybe_init(pVMR9, FALSE); if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED) IPin_Disconnect(iface); } @@ -943,15 +1560,32 @@ static HRESULT WINAPI VMR9_InputPin_ReceiveConnection(IPin * iface, IPin * pRece static HRESULT WINAPI VMR9_InputPin_Disconnect(IPin * iface) { InputPin *This = (InputPin *)iface; + VMR9Impl *pVMR9 = (VMR9Impl *)This->pin.pinInfo.pFilter; HRESULT hr = S_OK; TRACE("()\n"); + if (!pVMR9->mode) + return S_FALSE; + EnterCriticalSection(This->pin.pCritSec); if (!This->pin.pConnectedTo) { hr = S_FALSE; } + else if (pVMR9->allocator && pVMR9->presenter) + { + if (pVMR9->state != State_Stopped) + { + ERR("Disconnecting while not stopped! UNTESTED!!\n"); + } + if (pVMR9->state == State_Running) + hr = IVMRImagePresenter9_StopPresenting(pVMR9->presenter, pVMR9->cookie); + IVMRSurfaceAllocatorEx9_TerminateDevice(pVMR9->allocator, pVMR9->cookie); + pVMR9->num_surfaces = 0; + assert(!pVMR9->d3d9_surfaces); + } + if (This->pin.pConnectedTo) { IPin_Release(This->pin.pConnectedTo); @@ -1019,3 +1653,907 @@ static const IPinVtbl VMR9_InputPin_Vtbl = VMR9_InputPin_EndFlush, InputPin_NewSegment }; + +static HRESULT WINAPI VMR9FilterConfig_QueryInterface(IVMRFilterConfig9 *iface, REFIID riid, LPVOID * ppv) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + return IBaseFilter_QueryInterface((IBaseFilter *)This, riid, ppv); +} + +static ULONG WINAPI VMR9FilterConfig_AddRef(IVMRFilterConfig9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + return IUnknown_AddRef((IBaseFilter *)This); +} + +static ULONG WINAPI VMR9FilterConfig_Release(IVMRFilterConfig9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + return IUnknown_Release((IBaseFilter *)This); +} + +static HRESULT WINAPI VMR9FilterConfig_SetImageCompositor(IVMRFilterConfig9 *iface, IVMRImageCompositor9 *compositor) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + FIXME("(%p/%p)->(%p) stub\n", iface, This, compositor); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9FilterConfig_SetNumberOfStreams(IVMRFilterConfig9 *iface, DWORD max) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + FIXME("(%p/%p)->(%u) stub\n", iface, This, max); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9FilterConfig_GetNumberOfStreams(IVMRFilterConfig9 *iface, DWORD *max) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + FIXME("(%p/%p)->(%p) stub\n", iface, This, max); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9FilterConfig_SetRenderingPrefs(IVMRFilterConfig9 *iface, DWORD renderflags) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + FIXME("(%p/%p)->(%u) stub\n", iface, This, renderflags); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9FilterConfig_GetRenderingPrefs(IVMRFilterConfig9 *iface, DWORD *renderflags) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + FIXME("(%p/%p)->(%p) stub\n", iface, This, renderflags); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9FilterConfig_SetRenderingMode(IVMRFilterConfig9 *iface, DWORD mode) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + ERR("(%p/%p)->(%u)\n", iface, This, mode); + + EnterCriticalSection(&This->csFilter); + if (This->mode) + { + LeaveCriticalSection(&This->csFilter); + return VFW_E_WRONG_STATE; + } + + switch (mode) + { + case VMR9Mode_Windowed: + case VMR9Mode_Windowless: + This->allocator = (IVMRSurfaceAllocatorEx9 *)&This->surfvtbl; + This->presenter = (IVMRImagePresenter9 *)&This->imgvtbl; + This->imgvtbl = &VMR9_ImagePresenter; + This->surfvtbl = &VMR9_SurfaceAllocator; + This->allocator_is_ex = 0; + This->cookie = ~0; + break; + case VMR9Mode_Renderless: + break; + default: + LeaveCriticalSection(&This->csFilter); + return E_INVALIDARG; + } + + This->mode = mode; + LeaveCriticalSection(&This->csFilter); + return S_OK; +} + +static HRESULT WINAPI VMR9FilterConfig_GetRenderingMode(IVMRFilterConfig9 *iface, DWORD *mode) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRFilterConfig9_vtbl, iface); + + TRACE("(%p/%p)->(%p) stub\n", iface, This, mode); + if (!mode) + return E_POINTER; + + if (This->mode) + *mode = This->mode; + else + *mode = VMR9Mode_Windowed; + + return S_OK; +} + +static const IVMRFilterConfig9Vtbl VMR9_FilterConfig_Vtbl = +{ + VMR9FilterConfig_QueryInterface, + VMR9FilterConfig_AddRef, + VMR9FilterConfig_Release, + VMR9FilterConfig_SetImageCompositor, + VMR9FilterConfig_SetNumberOfStreams, + VMR9FilterConfig_GetNumberOfStreams, + VMR9FilterConfig_SetRenderingPrefs, + VMR9FilterConfig_GetRenderingPrefs, + VMR9FilterConfig_SetRenderingMode, + VMR9FilterConfig_GetRenderingMode +}; + +static HRESULT WINAPI VMR9WindowlessControl_QueryInterface(IVMRWindowlessControl9 *iface, REFIID riid, LPVOID * ppv) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + return IBaseFilter_QueryInterface((IBaseFilter *)This, riid, ppv); +} + +static ULONG WINAPI VMR9WindowlessControl_AddRef(IVMRWindowlessControl9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + return IUnknown_AddRef((IBaseFilter *)This); +} + +static ULONG WINAPI VMR9WindowlessControl_Release(IVMRWindowlessControl9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + return IUnknown_Release((IBaseFilter *)This); +} + +static HRESULT WINAPI VMR9WindowlessControl_GetNativeVideoSize(IVMRWindowlessControl9 *iface, LONG *width, LONG *height, LONG *arwidth, LONG *arheight) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + TRACE("(%p/%p)->(%p, %p, %p, %p)\n", iface, This, width, height, arwidth, arheight); + + if (!width || !height || !arwidth || !arheight) + { + ERR("Got no pointer\n"); + return E_POINTER; + } + + *width = This->bmiheader.biWidth; + *height = This->bmiheader.biHeight; + *arwidth = This->bmiheader.biWidth; + *arheight = This->bmiheader.biHeight; + + return S_OK; +} + +static HRESULT WINAPI VMR9WindowlessControl_GetMinIdealVideoSize(IVMRWindowlessControl9 *iface, LONG *width, LONG *height) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9WindowlessControl_GetMaxIdealVideoSize(IVMRWindowlessControl9 *iface, LONG *width, LONG *height) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +#define USED_FVF (D3DFVF_XYZRHW | D3DFVF_TEX1) +struct VERTEX { float x, y, z, rhw, u, v; }; + +/* Recreate all surfaces (If allocated as D3DPOOL_DEFAULT) and survive! */ +static HRESULT VMR9_SurfaceAllocator_UpdateDeviceReset(VMR9Impl *This) +{ + struct VERTEX t_vert[4]; + UINT width, height; + INT i; + void *bits = NULL; + D3DPRESENT_PARAMETERS d3dpp; + HRESULT hr; + + assert(This->hwnd_mine); + if (!This->d3d9_surfaces || !This->reset) + return S_OK; + + This->reset = FALSE; + ERR("RESETTING\n"); + if (This->d3d9_vertex) + { + IDirect3DVertexBuffer9_Release(This->d3d9_vertex); + This->d3d9_vertex = NULL; + } + + for (i = 0; i < This->num_surfaces; ++i) + { + IDirect3DSurface9 *surface = This->d3d9_surfaces[i]; + TRACE("Releasing surface %p\n", surface); + if (surface) + IUnknown_Release(surface); + } + ZeroMemory(This->d3d9_surfaces, sizeof(IDirect3DSurface9 *) * This->num_surfaces); + + /* Now try to create the d3d9 device */ + ZeroMemory(&d3dpp, sizeof(d3dpp)); + d3dpp.Windowed = TRUE; + d3dpp.hDeviceWindow = This->hwnd_mine; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + + if (This->d3d9_dev) + IDirect3DDevice9_Release(This->d3d9_dev); + This->d3d9_dev = NULL; + hr = IDirect3D9_CreateDevice(This->d3d9_ptr, d3d9_adapter_from_hwnd(This, This->hwnd_mine), D3DDEVTYPE_HAL, NULL, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp, &This->d3d9_dev); + if (hr != S_OK) + { + ERR("--> Creating device: %08x\n", hr); + return S_OK; + } + + IVMRSurfaceAllocatorNotify9_AllocateSurfaceHelper((IVMRSurfaceAllocatorNotify9 *)&This->IVMRSurfaceAllocatorNotify9_vtbl, + &This->info, &This->num_surfaces, This->d3d9_surfaces); + + This->reset = FALSE; + + if (!(This->info.dwFlags & VMR9AllocFlag_TextureSurface)) + return S_OK; + + hr = IDirect3DDevice9_CreateVertexBuffer(This->d3d9_dev, 4 * sizeof(struct VERTEX), D3DUSAGE_WRITEONLY, USED_FVF, + This->info.Pool, &This->d3d9_vertex, NULL); + + width = This->info.dwWidth; + height = This->info.dwHeight; + + for (i = 0; i < sizeof(t_vert) / sizeof(t_vert[0]); ++i) + { + if (i % 2) + { + t_vert[i].x = (float)This->target_rect.right - (float)This->target_rect.left - 0.5f; + t_vert[i].u = (float)This->source_rect.right / (float)width; + } + else + { + t_vert[i].x = -0.5f; + t_vert[i].u = (float)This->source_rect.left / (float)width; + } + + if (i % 4 < 2) + { + t_vert[i].y = -0.5f; + t_vert[i].v = (float)This->source_rect.bottom / (float)height; + } + else + { + t_vert[i].y = (float)This->target_rect.bottom - (float)This->target_rect.top - 0.5f; + t_vert[i].v = (float)This->source_rect.top / (float)height; + } + t_vert[i].z = 0.0f; + t_vert[i].rhw = 1.0f; + } + + FIXME("Vertex rectangle:\n"); + FIXME("X, Y: %f, %f\n", t_vert[0].x, t_vert[0].y); + FIXME("X, Y: %f, %f\n", t_vert[3].x, t_vert[3].y); + FIXME("TOP, LEFT: %f, %f\n", t_vert[0].u, t_vert[0].v); + FIXME("DOWN, BOTTOM: %f, %f\n", t_vert[3].u, t_vert[3].v); + + IDirect3DVertexBuffer9_Lock(This->d3d9_vertex, 0, sizeof(t_vert), &bits, 0); + memcpy(bits, t_vert, sizeof(t_vert)); + IDirect3DVertexBuffer9_Unlock(This->d3d9_vertex); + + return S_OK; +} + +static HRESULT WINAPI VMR9WindowlessControl_SetVideoPosition(IVMRWindowlessControl9 *iface, const RECT *source, const RECT *dest) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + TRACE("(%p/%p)->(%p, %p)\n", iface, This, source, dest); + + EnterCriticalSection(&This->csFilter); + + if (source) + This->source_rect = *source; + if (dest) + { + This->target_rect = *dest; + if (This->hwnd_mine) + { + FIXME("Output rectangle: starting at %dx%d, up to point %dx%d\n", dest->left, dest->top, dest->right, dest->bottom); + SetWindowPos(This->hwnd_mine, NULL, dest->left, dest->top, dest->right - dest->left, + dest->bottom-dest->top, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_NOREDRAW); + } + This->reset = TRUE; + } + + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI VMR9WindowlessControl_GetVideoPosition(IVMRWindowlessControl9 *iface, RECT *source, RECT *dest) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + if (source) + *source = This->source_rect; + + if (dest) + *dest = This->target_rect; + + FIXME("(%p/%p)->(%p/%p) stub\n", iface, This, source, dest); + return S_OK; +} + +static HRESULT WINAPI VMR9WindowlessControl_GetAspectRatioMode(IVMRWindowlessControl9 *iface, DWORD *mode) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9WindowlessControl_SetAspectRatioMode(IVMRWindowlessControl9 *iface, DWORD mode) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9WindowlessControl_SetVideoClippingWindow(IVMRWindowlessControl9 *iface, HWND hwnd) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + ERR("(%p/%p)->(%p)\n", iface, This, hwnd); + + EnterCriticalSection(&This->csFilter); + This->hwnd_last = hwnd; + VMR9_maybe_init(This, FALSE); + if (!hwnd) + IVMRSurfaceAllocatorEx9_TerminateDevice(This->allocator, This->cookie); + LeaveCriticalSection(&This->csFilter); + return S_OK; +} + +static HRESULT WINAPI VMR9WindowlessControl_RepaintVideo(IVMRWindowlessControl9 *iface, HWND hwnd, HDC hdc) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + HRESULT hr; + + FIXME("(%p/%p)->(...) semi-stub\n", iface, This); + + EnterCriticalSection(&This->csFilter); + if (hwnd != This->hwnd_last && hwnd != This->hwnd_mine) + { + ERR("Not handling changing windows yet!!!\n"); + LeaveCriticalSection(&This->csFilter); + return S_OK; + } + + if (!This->d3d9_dev) + { + ERR("No device created!\n"); + LeaveCriticalSection(&This->csFilter); + return VFW_E_WRONG_STATE; + } + + /* Windowless extension */ + hr = IDirect3DDevice9_Present(This->d3d9_dev, NULL, NULL, This->hwnd_mine, NULL); + LeaveCriticalSection(&This->csFilter); + + return hr; +} + +static HRESULT WINAPI VMR9WindowlessControl_DisplayModeChanged(IVMRWindowlessControl9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9WindowlessControl_GetCurrentImage(IVMRWindowlessControl9 *iface, BYTE **dib) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + FIXME("(%p/%p)->(...) stub\n", iface, This); + FIXME("(%p/%p)->(...) stub\n", iface, This); + FIXME("(%p/%p)->(...) stub\n", iface, This); + FIXME("(%p/%p)->(...) stub\n", iface, This); + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9WindowlessControl_SetBorderColor(IVMRWindowlessControl9 *iface, COLORREF color) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9WindowlessControl_GetBorderColor(IVMRWindowlessControl9 *iface, COLORREF *color) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRWindowlessControl9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static const IVMRWindowlessControl9Vtbl VMR9_WindowlessControl_Vtbl = +{ + VMR9WindowlessControl_QueryInterface, + VMR9WindowlessControl_AddRef, + VMR9WindowlessControl_Release, + VMR9WindowlessControl_GetNativeVideoSize, + VMR9WindowlessControl_GetMinIdealVideoSize, + VMR9WindowlessControl_GetMaxIdealVideoSize, + VMR9WindowlessControl_SetVideoPosition, + VMR9WindowlessControl_GetVideoPosition, + VMR9WindowlessControl_GetAspectRatioMode, + VMR9WindowlessControl_SetAspectRatioMode, + VMR9WindowlessControl_SetVideoClippingWindow, + VMR9WindowlessControl_RepaintVideo, + VMR9WindowlessControl_DisplayModeChanged, + VMR9WindowlessControl_GetCurrentImage, + VMR9WindowlessControl_SetBorderColor, + VMR9WindowlessControl_GetBorderColor +}; + +static HRESULT WINAPI VMR9SurfaceAllocatorNotify_QueryInterface(IVMRSurfaceAllocatorNotify9 *iface, REFIID riid, LPVOID * ppv) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + + return IBaseFilter_QueryInterface((IBaseFilter *)This, riid, ppv); +} + +static ULONG WINAPI VMR9SurfaceAllocatorNotify_AddRef(IVMRSurfaceAllocatorNotify9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + LONG ret = InterlockedIncrement(&This->ref_surface); + + return ret; +} + +static ULONG WINAPI VMR9SurfaceAllocatorNotify_Release(IVMRSurfaceAllocatorNotify9 *iface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + LONG ret = InterlockedDecrement(&This->ref_surface); + + /* Should we release our references on allocator and presenter if ref falls to 0? */ + return ret; +} + + +static HRESULT WINAPI VMR9SurfaceAllocatorNotify_AdviseSurfaceAllocator(IVMRSurfaceAllocatorNotify9 *iface, DWORD_PTR id, IVMRSurfaceAllocator9 *alloc) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + + /* FIXME: This code is not tested!!! */ + FIXME("(%p/%p)->(...) stub\n", iface, This); + This->cookie = id; + + if (This->presenter) + return VFW_E_WRONG_STATE; + + if (IUnknown_QueryInterface(alloc, &IID_IVMRImagePresenter9, (void **)&This->presenter) != S_OK) + return E_NOINTERFACE; + + if (IUnknown_QueryInterface(alloc, &IID_IVMRSurfaceAllocatorEx9, (void **)&This->allocator) == S_OK) + This->allocator_is_ex = 1; + else + { + This->allocator = (IVMRSurfaceAllocatorEx9 *)alloc; + IUnknown_AddRef(alloc); + This->allocator_is_ex = 0; + } + + return S_OK; +} + +static HRESULT WINAPI VMR9SurfaceAllocatorNotify_SetD3DDevice(IVMRSurfaceAllocatorNotify9 *iface, IDirect3DDevice9 *device, HMONITOR monitor) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9SurfaceAllocatorNotify_ChangeD3DDevice(IVMRSurfaceAllocatorNotify9 *iface, IDirect3DDevice9 *device, HMONITOR monitor) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VMR9SurfaceAllocatorNotify_AllocateSurfaceHelper(IVMRSurfaceAllocatorNotify9 *iface, VMR9AllocationInfo *allocinfo, DWORD *numbuffers, IDirect3DSurface9 **surface) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + INT i; + HRESULT hr = S_OK; + + FIXME("(%p/%p)->(%p, %p => %u, %p) semi-stub\n", iface, This, allocinfo, numbuffers, (numbuffers ? *numbuffers : 0), surface); + + if (!allocinfo || !numbuffers || !surface) + return E_POINTER; + + if (!*numbuffers || *numbuffers < allocinfo->MinBuffers) + { + ERR("Invalid number of buffers?\n"); + return E_INVALIDARG; + } + + if (!This->d3d9_dev) + { + ERR("No direct3d device when requested to allocate a surface!\n"); + return VFW_E_WRONG_STATE; + } + + if (allocinfo->dwFlags & VMR9AllocFlag_OffscreenSurface) + { + ERR("Creating offscreen surface\n"); + for (i = 0; i < *numbuffers; ++i) + { + hr = IDirect3DDevice9_CreateOffscreenPlainSurface(This->d3d9_dev, allocinfo->dwWidth, allocinfo->dwHeight, + allocinfo->Format, allocinfo->Pool, &surface[i], NULL); + if (FAILED(hr)) + break; + } + } + else if (allocinfo->dwFlags & VMR9AllocFlag_TextureSurface) + { + TRACE("Creating texture surface\n"); + for (i = 0; i < *numbuffers; ++i) + { + IDirect3DTexture9 *texture; + + hr = IDirect3DDevice9_CreateTexture(This->d3d9_dev, allocinfo->dwWidth, allocinfo->dwHeight, 1, 0, + allocinfo->Format, allocinfo->Pool, &texture, NULL); + if (FAILED(hr)) + break; + IDirect3DTexture9_GetSurfaceLevel(texture, 0, &surface[i]); + IDirect3DTexture9_Release(texture); + } + } + else + { + FIXME("Could not allocate for type %08x\n", allocinfo->dwFlags); + return E_NOTIMPL; + } + + if (i >= allocinfo->MinBuffers) + { + hr = S_OK; + *numbuffers = i; + } + else + { + ERR("Allocation failed\n"); + for (--i; i >= 0; --i) + { + IDirect3DSurface9_Release(surface[i]); + } + *numbuffers = 0; + } + return hr; +} + +static HRESULT WINAPI VMR9SurfaceAllocatorNotify_NotifyEvent(IVMRSurfaceAllocatorNotify9 *iface, LONG code, LONG_PTR param1, LONG_PTR param2) +{ + ICOM_THIS_MULTI(VMR9Impl, IVMRSurfaceAllocatorNotify9_vtbl, iface); + + FIXME("(%p/%p)->(...) stub\n", iface, This); + return E_NOTIMPL; +} + +static const IVMRSurfaceAllocatorNotify9Vtbl IVMRSurfaceAllocatorNotify9_Vtbl = +{ + VMR9SurfaceAllocatorNotify_QueryInterface, + VMR9SurfaceAllocatorNotify_AddRef, + VMR9SurfaceAllocatorNotify_Release, + VMR9SurfaceAllocatorNotify_AdviseSurfaceAllocator, + VMR9SurfaceAllocatorNotify_SetD3DDevice, + VMR9SurfaceAllocatorNotify_ChangeD3DDevice, + VMR9SurfaceAllocatorNotify_AllocateSurfaceHelper, + VMR9SurfaceAllocatorNotify_NotifyEvent +}; + +/* Only IUnknown_Release should be called of all the IUnknown methods */ +static HRESULT WINAPI VMR9_ImagePresenter_QueryInterface(IVMRImagePresenter9 *iface, REFIID riid, LPVOID * ppv) +{ + FIXME("unexpected call!\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI VMR9_ImagePresenter_AddRef(IVMRImagePresenter9 *iface) +{ + FIXME("unexpected call!\n"); + return 2; +} + +static ULONG WINAPI VMR9_ImagePresenter_Release(IVMRImagePresenter9 *iface) +{ + return 1; +} + +static HRESULT WINAPI VMR9_ImagePresenter_StartPresenting(IVMRImagePresenter9 *iface, DWORD_PTR id) +{ + ICOM_THIS_MULTI(VMR9Impl, imgvtbl, iface); + + TRACE("(%p/%p)->(...) stub\n", iface, This); + return S_OK; +} + +static HRESULT WINAPI VMR9_ImagePresenter_StopPresenting(IVMRImagePresenter9 *iface, DWORD_PTR id) +{ + ICOM_THIS_MULTI(VMR9Impl, imgvtbl, iface); + + TRACE("(%p/%p)->(...) stub\n", iface, This); + return S_OK; +} + +static HRESULT VMR9_ImagePresenter_PresentTexture(VMR9Impl *This, IDirect3DSurface9 *surface) +{ + IDirect3DTexture9 *texture = NULL; + HRESULT hr; + + hr = IDirect3DDevice9_SetFVF(This->d3d9_dev, USED_FVF); + if (hr != S_OK) + { + FIXME("SetFVF: %08x\n", hr); + return hr; + } + + hr = IDirect3DDevice9_SetStreamSource(This->d3d9_dev, 0, This->d3d9_vertex, 0, sizeof(struct VERTEX)); + if (hr != S_OK) + { + FIXME("SetStreamSource: %08x\n", hr); + return hr; + } + + IDirect3DSurface9_GetContainer(surface, &IID_IDirect3DTexture9, (void **) &texture); + hr = IDirect3DDevice9_SetTexture(This->d3d9_dev, 0, (IDirect3DBaseTexture9 *)texture); + IDirect3DTexture9_Release(texture); + if (hr != S_OK) + { + FIXME("SetTexture: %08x\n", hr); + return hr; + } + + hr = IDirect3DDevice9_DrawPrimitive(This->d3d9_dev, D3DPT_TRIANGLESTRIP, 0, 2); + if (hr != S_OK) + { + FIXME("DrawPrimitive: %08x\n", hr); + return hr; + } + + return S_OK; +} + +static HRESULT VMR9_ImagePresenter_PresentOffscreenSurface(VMR9Impl *This, IDirect3DSurface9 *surface) +{ + HRESULT hr; + IDirect3DSurface9 *target = NULL; + RECT target_rect; + + hr = IDirect3DDevice9_GetBackBuffer(This->d3d9_dev, 0, 0, D3DBACKBUFFER_TYPE_MONO, &target); + if (hr != S_OK) + { + ERR("IDirect3DDevice9_GetBackBuffer -- %08x\n", hr); + return hr; + } + + target_rect = This->target_rect; + target_rect.right -= target_rect.left; + target_rect.bottom -= target_rect.top; + target_rect.left = target_rect.top = 0; + + /* Flip */ + target_rect.top = target_rect.bottom; + target_rect.bottom = 0; + + hr = IDirect3DDevice9_StretchRect(This->d3d9_dev, surface, &This->source_rect, target, &target_rect, D3DTEXF_LINEAR); + if (hr != S_OK) + ERR("IDirect3DDevice9_StretchRect -- %08x\n", hr); + IDirect3DSurface9_Release(target); + + return hr; +} + +static HRESULT WINAPI VMR9_ImagePresenter_PresentImage(IVMRImagePresenter9 *iface, DWORD_PTR id, VMR9PresentationInfo *info) +{ + ICOM_THIS_MULTI(VMR9Impl, imgvtbl, iface); + HRESULT hr; + RECT output; + BOOL render = FALSE; + + GetWindowRect(This->hwnd_mine, &output); + TRACE("Output rectangle: starting at %dx%d, up to point %dx%d\n", output.left, output.top, output.right, output.bottom); + + /* This might happen if we don't have active focus (eg on a different virtual desktop) */ + if (!This->d3d9_dev) + return S_OK; + + /* Display image here */ + hr = IDirect3DDevice9_Clear(This->d3d9_dev, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); + if (hr != S_OK) + FIXME("hr: %08x\n", hr); + hr = IDirect3DDevice9_BeginScene(This->d3d9_dev); + if (hr == S_OK) + { + if (This->d3d9_vertex) + hr = VMR9_ImagePresenter_PresentTexture(This, info->lpSurf); + else + hr = VMR9_ImagePresenter_PresentOffscreenSurface(This, info->lpSurf); + render = SUCCEEDED(hr); + } + else + FIXME("BeginScene: %08x\n", hr); + hr = IDirect3DDevice9_EndScene(This->d3d9_dev); + if (render && SUCCEEDED(hr)) + { + hr = IDirect3DDevice9_Present(This->d3d9_dev, NULL, NULL, This->hwnd_mine, NULL); + if (hr != S_OK) + FIXME("Presenting image: %08x\n", hr); + } + + TRACE("(%p/%p)->(...) stub\n", iface, This); + return S_OK; +} + +static const IVMRImagePresenter9Vtbl VMR9_ImagePresenter = +{ + VMR9_ImagePresenter_QueryInterface, + VMR9_ImagePresenter_AddRef, + VMR9_ImagePresenter_Release, + VMR9_ImagePresenter_StartPresenting, + VMR9_ImagePresenter_StopPresenting, + VMR9_ImagePresenter_PresentImage +}; + +static HRESULT WINAPI VMR9_SurfaceAllocator_QueryInterface(IVMRSurfaceAllocatorEx9 *iface, REFIID riid, LPVOID * ppv) +{ + FIXME("unexpected call!\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI VMR9_SurfaceAllocator_AddRef(IVMRSurfaceAllocatorEx9 *iface) +{ + FIXME("unexpected call!\n"); + return 2; +} + +static ULONG WINAPI VMR9_SurfaceAllocator_Release(IVMRSurfaceAllocatorEx9 *iface) +{ + return 1; +} + +static HRESULT VMR9_SurfaceAllocator_SetAllocationSettings(VMR9Impl *This, VMR9AllocationInfo *allocinfo) +{ + D3DCAPS9 caps; + UINT width, height; + HRESULT hr; + + if (!(allocinfo->dwFlags & VMR9AllocFlag_TextureSurface)) + /* Only needed for texture surfaces */ + return S_OK; + + hr = IDirect3DDevice9_GetDeviceCaps(This->d3d9_dev, &caps); + if (FAILED(hr)) + return hr; + + if (!(caps.TextureCaps & D3DPTEXTURECAPS_POW2) || (caps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY)) + { + width = allocinfo->dwWidth; + height = allocinfo->dwHeight; + } + else + { + width = height = 1; + while (width < allocinfo->dwWidth) + width *= 2; + + while (height < allocinfo->dwHeight) + height *= 2; + FIXME("NPOW2 support missing, not using proper surfaces!\n"); + } + + if (caps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) + { + if (height > width) + width = height; + else + height = width; + FIXME("Square texture support required..\n"); + } + + hr = IDirect3DDevice9_CreateVertexBuffer(This->d3d9_dev, 4 * sizeof(struct VERTEX), D3DUSAGE_WRITEONLY, USED_FVF, allocinfo->Pool, &This->d3d9_vertex, NULL); + if (FAILED(hr)) + { + ERR("Couldn't create vertex buffer: %08x\n", hr); + return hr; + } + + This->reset = TRUE; + allocinfo->dwHeight = height; + allocinfo->dwWidth = width; + + return hr; +} + +static HRESULT WINAPI VMR9_SurfaceAllocator_InitializeDevice(IVMRSurfaceAllocatorEx9 *iface, DWORD_PTR id, VMR9AllocationInfo *allocinfo, DWORD *numbuffers) +{ + ICOM_THIS_MULTI(VMR9Impl, surfvtbl, iface); + HRESULT hr = S_OK; + + EnterCriticalSection(&This->csFilter); + if (This->mode != VMR9Mode_Windowed && !This->hwnd_last) + { + ERR("No window set\n"); + hr = VFW_E_WRONG_STATE; + goto leave; + } + + if (!CreateRenderingWindow(This, allocinfo, numbuffers)) + { + ERR("Failed to create rendering window, expect no output!\n"); + hr = VFW_E_WRONG_STATE; + goto leave; + } + +leave: + LeaveCriticalSection(&This->csFilter); + return hr; +} + +static HRESULT WINAPI VMR9_SurfaceAllocator_TerminateDevice(IVMRSurfaceAllocatorEx9 *iface, DWORD_PTR id) +{ + ICOM_THIS_MULTI(VMR9Impl, surfvtbl, iface); + HRESULT hr; + + EnterCriticalSection(&This->csFilter); + + if (!This->hwnd_mine) + { + LeaveCriticalSection(&This->csFilter); + return S_OK; + } + + hr = SendMessageW(This->hwnd_mine, WM_CLOSE, 0, 0); + hr = PostThreadMessageW(This->tid, WM_QUIT, 0, 0); + FIXME("SendMessageW: %08x\n", hr); + WaitForSingleObject(This->thread_hwnd, INFINITE); + This->thread_hwnd = NULL; + This->hwnd_mine = NULL; + LeaveCriticalSection(&This->csFilter); + + return S_OK; +} + +static HRESULT WINAPI VMR9_SurfaceAllocator_GetSurface(IVMRSurfaceAllocatorEx9 *iface, DWORD_PTR id, DWORD surfaceindex, DWORD flags, IDirect3DSurface9 **surface) +{ + ICOM_THIS_MULTI(VMR9Impl, surfvtbl, iface); + + assert(surfaceindex < This->num_surfaces); + *surface = This->d3d9_surfaces[surfaceindex]; + IUnknown_AddRef(*surface); + + return S_OK; +} + +static HRESULT WINAPI VMR9_SurfaceAllocator_AdviseNotify(IVMRSurfaceAllocatorEx9 *iface, IVMRSurfaceAllocatorNotify9 *allocnotify) +{ + ICOM_THIS_MULTI(VMR9Impl, surfvtbl, iface); + + ERR("(%p/%p)->(...) THIS METHOD SHOULD NOT BE CALLED\n", iface, This); + return E_NOTIMPL; +} + +static const IVMRSurfaceAllocatorEx9Vtbl VMR9_SurfaceAllocator = +{ + VMR9_SurfaceAllocator_QueryInterface, + VMR9_SurfaceAllocator_AddRef, + VMR9_SurfaceAllocator_Release, + VMR9_SurfaceAllocator_InitializeDevice, + VMR9_SurfaceAllocator_TerminateDevice, + VMR9_SurfaceAllocator_GetSurface, + VMR9_SurfaceAllocator_AdviseNotify, + NULL /* This isn't the SurfaceAllocatorEx type yet, working on it */ +}; -- 1.5.4.1