Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/sample.c | 284 +++++++++++++++++++++++++++++++++++++++++-- dlls/evr/tests/evr.c | 23 ++-- 2 files changed, 283 insertions(+), 24 deletions(-)
diff --git a/dlls/evr/sample.c b/dlls/evr/sample.c index f5189443307..b65cc45a662 100644 --- a/dlls/evr/sample.c +++ b/dlls/evr/sample.c @@ -26,6 +26,7 @@
#include "wine/debug.h" #include "wine/heap.h" +#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(evr);
@@ -106,14 +107,23 @@ struct sample_allocator { IMFVideoSampleAllocator IMFVideoSampleAllocator_iface; IMFVideoSampleAllocatorCallback IMFVideoSampleAllocatorCallback_iface; + IMFAsyncCallback tracking_callback; LONG refcount;
IMFVideoSampleAllocatorNotify *callback; - unsigned int free_samples; IDirect3DDeviceManager9 *device_manager; + unsigned int free_sample_count; + struct list free_samples; + struct list used_samples; CRITICAL_SECTION cs; };
+struct queued_sample +{ + struct list entry; + IMFSample *sample; +}; + static struct sample_allocator *impl_from_IMFVideoSampleAllocator(IMFVideoSampleAllocator *iface) { return CONTAINING_RECORD(iface, struct sample_allocator, IMFVideoSampleAllocator_iface); @@ -124,6 +134,11 @@ static struct sample_allocator *impl_from_IMFVideoSampleAllocatorCallback(IMFVid return CONTAINING_RECORD(iface, struct sample_allocator, IMFVideoSampleAllocatorCallback_iface); }
+static struct sample_allocator *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct sample_allocator, tracking_callback); +} + static HRESULT WINAPI sample_allocator_QueryInterface(IMFVideoSampleAllocator *iface, REFIID riid, void **obj) { struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface); @@ -160,6 +175,25 @@ static ULONG WINAPI sample_allocator_AddRef(IMFVideoSampleAllocator *iface) return refcount; }
+static void sample_allocator_release_samples(struct sample_allocator *allocator) +{ + struct queued_sample *iter, *iter2; + + LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &allocator->free_samples, struct queued_sample, entry) + { + list_remove(&iter->entry); + IMFSample_Release(iter->sample); + heap_free(iter); + } + + LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &allocator->used_samples, struct queued_sample, entry) + { + list_remove(&iter->entry); + IMFSample_Release(iter->sample); + heap_free(iter); + } +} + static ULONG WINAPI sample_allocator_Release(IMFVideoSampleAllocator *iface) { struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface); @@ -173,6 +207,7 @@ static ULONG WINAPI sample_allocator_Release(IMFVideoSampleAllocator *iface) IMFVideoSampleAllocatorNotify_Release(allocator->callback); if (allocator->device_manager) IDirect3DDeviceManager9_Release(allocator->device_manager); + sample_allocator_release_samples(allocator); DeleteCriticalSection(&allocator->cs); heap_free(allocator); } @@ -208,24 +243,176 @@ static HRESULT WINAPI sample_allocator_SetDirectXManager(IMFVideoSampleAllocator
static HRESULT WINAPI sample_allocator_UninitializeSampleAllocator(IMFVideoSampleAllocator *iface) { - FIXME("%p.\n", iface); + struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface);
- return E_NOTIMPL; + TRACE("%p.\n", iface); + + EnterCriticalSection(&allocator->cs); + + sample_allocator_release_samples(allocator); + allocator->free_sample_count = 0; + + LeaveCriticalSection(&allocator->cs); + + return S_OK; +} + +static HRESULT sample_allocator_create_samples(struct sample_allocator *allocator, unsigned int sample_count, + IMFMediaType *media_type) +{ + IDirectXVideoProcessorService *service = NULL; + unsigned int i, width, height; + IDirect3DSurface9 *surface; + HANDLE hdevice = NULL; + GUID major, subtype; + UINT64 frame_size; + IMFSample *sample; + D3DFORMAT format; + HRESULT hr; + + if (FAILED(IMFMediaType_GetMajorType(media_type, &major))) + return MF_E_INVALIDMEDIATYPE; + + if (!IsEqualGUID(&major, &MFMediaType_Video)) + return MF_E_INVALIDMEDIATYPE; + + if (FAILED(IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &frame_size))) + return MF_E_INVALIDMEDIATYPE; + + if (FAILED(IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDMEDIATYPE; + + format = subtype.Data1; + height = frame_size; + width = frame_size >> 32; + + if (allocator->device_manager) + { + if (SUCCEEDED(hr = IDirect3DDeviceManager9_OpenDeviceHandle(allocator->device_manager, &hdevice))) + { + hr = IDirect3DDeviceManager9_GetVideoService(allocator->device_manager, hdevice, + &IID_IDirectXVideoProcessorService, (void **)&service); + } + + if (FAILED(hr)) + { + WARN("Failed to get processor service, %#x.\n", hr); + return hr; + } + } + + sample_allocator_release_samples(allocator); + + for (i = 0; i < sample_count; ++i) + { + struct queued_sample *queued_sample = heap_alloc(sizeof(*queued_sample)); + + if (service) + { + if (SUCCEEDED(hr = IDirectXVideoProcessorService_CreateSurface(service, width, height, 0, format, + D3DPOOL_DEFAULT, 0, DXVA2_VideoProcessorRenderTarget, &surface, NULL))) + { + hr = MFCreateVideoSampleFromSurface((IUnknown *)surface, &sample); + IDirect3DSurface9_Release(surface); + } + } + else + { + IMFMediaBuffer *buffer; + + if (SUCCEEDED(hr = MFCreateVideoSampleFromSurface(NULL, &sample))) + { + if (SUCCEEDED(hr = MFCreate2DMediaBuffer(width, height, format, FALSE, &buffer))) + { + hr = IMFSample_AddBuffer(sample, buffer); + IMFMediaBuffer_Release(buffer); + } + } + } + + if (FAILED(hr)) + { + WARN("Unable to allocate %u samples.\n", sample_count); + sample_allocator_release_samples(allocator); + break; + } + + queued_sample = heap_alloc(sizeof(*queued_sample)); + queued_sample->sample = sample; + list_add_tail(&allocator->free_samples, &queued_sample->entry); + allocator->free_sample_count++; + } + + if (service) + IDirectXVideoProcessorService_Release(service); + + if (allocator->device_manager) + IDirect3DDeviceManager9_CloseDeviceHandle(allocator->device_manager, hdevice); + + return hr; }
static HRESULT WINAPI sample_allocator_InitializeSampleAllocator(IMFVideoSampleAllocator *iface, DWORD sample_count, IMFMediaType *media_type) { - FIXME("%p, %u, %p.\n", iface, sample_count, media_type); + struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("%p, %u, %p.\n", iface, sample_count, media_type); + + if (!sample_count) + sample_count = 1; + + EnterCriticalSection(&allocator->cs); + + hr = sample_allocator_create_samples(allocator, sample_count, media_type); + + LeaveCriticalSection(&allocator->cs); + + return hr; }
-static HRESULT WINAPI sample_allocator_AllocateSample(IMFVideoSampleAllocator *iface, IMFSample **sample) +static HRESULT WINAPI sample_allocator_AllocateSample(IMFVideoSampleAllocator *iface, IMFSample **out) { - FIXME("%p, %p.\n", iface, sample); + struct sample_allocator *allocator = impl_from_IMFVideoSampleAllocator(iface); + IMFTrackedSample *tracked_sample; + IMFSample *sample; + HRESULT hr;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, out); + + EnterCriticalSection(&allocator->cs); + + if (list_empty(&allocator->free_samples) && list_empty(&allocator->used_samples)) + hr = MF_E_NOT_INITIALIZED; + else if (list_empty(&allocator->free_samples)) + hr = MF_E_SAMPLEALLOCATOR_EMPTY; + else + { + struct list *head = list_head(&allocator->free_samples); + + sample = LIST_ENTRY(head, struct queued_sample, entry)->sample; + + if (SUCCEEDED(hr = IMFSample_QueryInterface(sample, &IID_IMFTrackedSample, (void **)&tracked_sample))) + { + hr = IMFTrackedSample_SetAllocator(tracked_sample, &allocator->tracking_callback, NULL); + IMFTrackedSample_Release(tracked_sample); + } + + if (SUCCEEDED(hr)) + { + list_remove(head); + list_add_tail(&allocator->used_samples, head); + allocator->free_sample_count--; + + *out = sample; + IMFSample_AddRef(*out); + } + } + + LeaveCriticalSection(&allocator->cs); + + return hr; }
static const IMFVideoSampleAllocatorVtbl sample_allocator_vtbl = @@ -285,7 +472,7 @@ static HRESULT WINAPI sample_allocator_callback_GetFreeSampleCount(IMFVideoSampl
EnterCriticalSection(&allocator->cs); if (count) - *count = allocator->free_samples; + *count = allocator->free_sample_count; LeaveCriticalSection(&allocator->cs);
return S_OK; @@ -300,6 +487,77 @@ static const IMFVideoSampleAllocatorCallbackVtbl sample_allocator_callback_vtbl sample_allocator_callback_GetFreeSampleCount, };
+static HRESULT WINAPI sample_allocator_tracking_callback_QueryInterface(IMFAsyncCallback *iface, + REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI sample_allocator_tracking_callback_AddRef(IMFAsyncCallback *iface) +{ + struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface); + return IMFVideoSampleAllocator_AddRef(&allocator->IMFVideoSampleAllocator_iface); +} + +static ULONG WINAPI sample_allocator_tracking_callback_Release(IMFAsyncCallback *iface) +{ + struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface); + return IMFVideoSampleAllocator_Release(&allocator->IMFVideoSampleAllocator_iface); +} + +static HRESULT WINAPI sample_allocator_tracking_callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI sample_allocator_tracking_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct sample_allocator *allocator = impl_from_IMFAsyncCallback(iface); + struct queued_sample *iter; + IUnknown *sample = NULL; + + EnterCriticalSection(&allocator->cs); + + IMFAsyncResult_GetObject(result, (IUnknown **)&sample); + + LIST_FOR_EACH_ENTRY(iter, &allocator->used_samples, struct queued_sample, entry) + { + if (sample == (IUnknown *)iter->sample) + { + list_remove(&iter->entry); + list_add_tail(&allocator->free_samples, &iter->entry); + allocator->free_sample_count++; + break; + } + } + + IUnknown_Release(sample); + + LeaveCriticalSection(&allocator->cs); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl sample_allocator_tracking_callback_vtbl = +{ + sample_allocator_tracking_callback_QueryInterface, + sample_allocator_tracking_callback_AddRef, + sample_allocator_tracking_callback_Release, + sample_allocator_tracking_callback_GetParameters, + sample_allocator_tracking_callback_Invoke, +}; + HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj) { struct sample_allocator *object; @@ -312,7 +570,10 @@ HRESULT WINAPI MFCreateVideoSampleAllocator(REFIID riid, void **obj)
object->IMFVideoSampleAllocator_iface.lpVtbl = &sample_allocator_vtbl; object->IMFVideoSampleAllocatorCallback_iface.lpVtbl = &sample_allocator_callback_vtbl; + object->tracking_callback.lpVtbl = &sample_allocator_tracking_callback_vtbl; object->refcount = 1; + list_init(&object->used_samples); + list_init(&object->free_samples); InitializeCriticalSection(&object->cs);
hr = IMFVideoSampleAllocator_QueryInterface(&object->IMFVideoSampleAllocator_iface, riid, obj); @@ -369,8 +630,7 @@ static ULONG WINAPI video_sample_Release(IMFSample *iface) HRESULT hr;
IMFSample_LockStore(sample->sample); - refcount = InterlockedDecrement(&sample->refcount); - if (sample->tracked_result && sample->tracked_refcount == refcount) + if (sample->tracked_result && sample->tracked_refcount == (sample->refcount - 1)) { /* Call could fail if queue system is not initialized, it's not critical. */ if (FAILED(hr = MFInvokeCallback(sample->tracked_result))) @@ -381,6 +641,8 @@ static ULONG WINAPI video_sample_Release(IMFSample *iface) } IMFSample_UnlockStore(sample->sample);
+ refcount = InterlockedDecrement(&sample->refcount); + TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount) diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 8c82ae34f8d..fbcf798157e 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -1338,13 +1338,14 @@ static void test_MFCreateVideoSampleAllocator(void) ok(!count, "Unexpected count %d.\n", count);
hr = IMFVideoSampleAllocator_UninitializeSampleAllocator(allocator); -todo_wine ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample); -todo_wine ok(hr == MF_E_NOT_INITIALIZED, "Unexpected hr %#x.\n", hr);
+ hr = IMFVideoSampleAllocator_SetDirectXManager(allocator, NULL); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + hr = MFCreateMediaType(&media_type); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
@@ -1356,42 +1357,34 @@ todo_wine video_type = create_video_type(&MFVideoFormat_RGB32);
hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 2, video_type); -todo_wine ok(hr == MF_E_INVALIDMEDIATYPE, "Unexpected hr %#x.\n", hr);
/* Frame size is required. */ hr = IMFMediaType_SetUINT64(video_type, &MF_MT_FRAME_SIZE, (UINT64) 320 << 32 | 240); ok(hr == S_OK, "Unexpected hr %#x.\n", hr); hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 0, video_type); -todo_wine ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
hr = IMFVideoSampleAllocatorCallback_GetFreeSampleCount(allocator_cb, &count); ok(hr == S_OK, "Unexpected hr %#x.\n", hr); -todo_wine ok(count == 1, "Unexpected count %d.\n", count);
sample = NULL; hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample); -todo_wine ok(hr == S_OK, "Unexpected hr %#x.\n", hr); - if (SUCCEEDED(hr)) - ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample)); + ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample));
hr = IMFVideoSampleAllocator_AllocateSample(allocator, &sample2); -todo_wine ok(hr == MF_E_SAMPLEALLOCATOR_EMPTY, "Unexpected hr %#x.\n", hr);
/* Reinitialize with active sample. */ hr = IMFVideoSampleAllocator_InitializeSampleAllocator(allocator, 4, video_type); -todo_wine ok(hr == S_OK, "Unexpected hr %#x.\n", hr); - if (sample) - ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample)); +todo_wine + ok(get_refcount(sample) == 3, "Unexpected refcount %u.\n", get_refcount(sample));
hr = IMFVideoSampleAllocatorCallback_GetFreeSampleCount(allocator_cb, &count); ok(hr == S_OK, "Unexpected hr %#x.\n", hr); -todo_wine ok(count == 4, "Unexpected count %d.\n", count);
if (sample) @@ -1418,6 +1411,10 @@ todo_wine IMFGetService_Release(gs); }
+ hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMF2DBuffer, (void **)&unk); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + IUnknown_Release(unk); + IMFMediaBuffer_Release(buffer);
IMFSample_Release(sample);