Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/mixer.c | 11 +++++++++ dlls/evr/tests/evr.c | 56 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-)
diff --git a/dlls/evr/mixer.c b/dlls/evr/mixer.c index 997c5054f12..ddf34d05f44 100644 --- a/dlls/evr/mixer.c +++ b/dlls/evr/mixer.c @@ -1272,8 +1272,19 @@ static HRESULT WINAPI video_mixer_transform_ProcessOutput(IMFTransform *iface, D }
if (SUCCEEDED(hr)) + { video_mixer_render(mixer, surface);
+ timestamp = duration = 0; + if (SUCCEEDED(IMFSample_GetSampleTime(mixer->inputs[0].sample, ×tamp))) + { + IMFSample_SetSampleTime(buffers->pSample, timestamp); + + IMFSample_GetSampleDuration(mixer->inputs[0].sample, &duration); + IMFSample_SetSampleDuration(buffers->pSample, duration); + } + } + if (SUCCEEDED(hr)) { for (i = 0; i < mixer->input_count; ++i) diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index c456bcf5653..c83ce0374eb 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -2219,13 +2219,14 @@ static void test_mixer_samples(void) IMFDesiredSample *desired; IDirect3DDevice9 *device; IMFMediaType *video_type; - DWORD color, status; + DWORD count, flags, color, status; IMFTransform *mixer; - IMFSample *sample; + IMFSample *sample, *sample2; IDirect3D9 *d3d; HWND window; UINT token; HRESULT hr; + LONGLONG pts, duration;
window = create_window(); d3d = Direct3DCreate9(D3D_SDK_VERSION); @@ -2395,6 +2396,57 @@ static void test_mixer_samples(void) hr = IMFTransform_ProcessInput(mixer, 5, sample, 0); ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr);
+ /* ProcessOutput() sets sample time and duration. */ + hr = MFCreateVideoSampleFromSurface((IUnknown *)surface, &sample2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_SetUINT32(sample2, &IID_IMFSample, 1); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_SetSampleFlags(sample2, 0x123); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_GetSampleTime(sample2, &pts); + ok(hr == MF_E_NO_SAMPLE_TIMESTAMP, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_GetSampleDuration(sample2, &duration); + ok(hr == MF_E_NO_SAMPLE_DURATION, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_SetSampleTime(sample, 0); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_SetSampleDuration(sample, 0); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + memset(buffers, 0, sizeof(buffers)); + buffers[0].pSample = sample2; + hr = IMFTransform_ProcessOutput(mixer, 0, 1, buffers, &status); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + + hr = IMFSample_GetSampleTime(sample2, &pts); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(!pts, "Unexpected sample time.\n"); + + hr = IMFSample_GetSampleDuration(sample2, &duration); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(!duration, "Unexpected duration\n"); + + /* Flags are not copied. */ + hr = IMFSample_GetSampleFlags(sample2, &flags); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(flags == 0x123, "Unexpected flags %#x.\n", flags); + + /* Attributes are not removed. */ + hr = IMFSample_GetCount(sample2, &count); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(count == 1, "Unexpected attribute count %u.\n", count); + + hr = IMFSample_GetCount(sample, &count); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(!count, "Unexpected attribute count %u.\n", count); + + IMFSample_Release(sample2); + hr = IMFTransform_ProcessMessage(mixer, MFT_MESSAGE_COMMAND_DRAIN, 0); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/mixer.c | 4 +++- dlls/evr/tests/evr.c | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/evr/mixer.c b/dlls/evr/mixer.c index ddf34d05f44..a7ce3949bec 100644 --- a/dlls/evr/mixer.c +++ b/dlls/evr/mixer.c @@ -1650,11 +1650,13 @@ static HRESULT WINAPI video_mixer_getservice_GetService(IMFGetService *iface, RE { return IMFGetService_QueryInterface(iface, riid, obj); } + + return E_NOINTERFACE; }
FIXME("Unsupported service %s, riid %s.\n", debugstr_guid(service), debugstr_guid(riid));
- return E_NOTIMPL; + return MF_E_UNSUPPORTED_SERVICE; }
static const IMFGetServiceVtbl video_mixer_getservice_vtbl = diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index c83ce0374eb..60d74d1a224 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -489,6 +489,10 @@ static void test_default_mixer(void) check_service_interface(transform, &MR_VIDEO_MIXER_SERVICE, &IID_IMFVideoProcessor, TRUE); check_service_interface(transform, &MR_VIDEO_MIXER_SERVICE, &IID_IMFVideoMixerControl, TRUE); check_service_interface(transform, &MR_VIDEO_MIXER_SERVICE, &IID_IMFVideoPositionMapper, TRUE); + check_service_interface(transform, &MR_VIDEO_MIXER_SERVICE, &IID_IMFTransform, FALSE); + + hr = MFGetService((IUnknown *)transform, &MR_VIDEO_RENDER_SERVICE, &IID_IUnknown, (void **)&unk); + ok(hr == MF_E_UNSUPPORTED_SERVICE, "Unexpected hr %#x.\n", hr);
if (SUCCEEDED(MFGetService((IUnknown *)transform, &MR_VIDEO_MIXER_SERVICE, &IID_IMFVideoMixerControl2, (void **)&mixer_control2))) {
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/presenter.c | 2 +- dlls/evr/tests/evr.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 84ef223e0fc..51792cdc2cd 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -1258,7 +1258,7 @@ static HRESULT WINAPI video_presenter_getservice_GetService(IMFGetService *iface
FIXME("Unimplemented service %s.\n", debugstr_guid(service));
- return E_NOTIMPL; + return MF_E_UNSUPPORTED_SERVICE; }
static const IMFGetServiceVtbl video_presenter_getservice_vtbl = diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 60d74d1a224..55986414a70 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -1129,6 +1129,7 @@ static void test_default_presenter(void) IDirect3DDeviceManager9 *dm; IMFVideoDeviceID *deviceid; HWND hwnd, hwnd2; + IUnknown *unk; DWORD flags; float rate; HRESULT hr; @@ -1162,8 +1163,12 @@ static void test_default_presenter(void) check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFGetService, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoDeviceID, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFQualityAdvise, TRUE); + check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFTransform, FALSE); check_service_interface(presenter, &MR_VIDEO_ACCELERATION_SERVICE, &IID_IDirect3DDeviceManager9, TRUE);
+ hr = MFGetService((IUnknown *)presenter, &MR_VIDEO_MIXER_SERVICE, &IID_IUnknown, (void **)&unk); + ok(hr == MF_E_UNSUPPORTED_SERVICE, "Unexpected hr %#x.\n", hr); + hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IMFVideoDeviceID, (void **)&deviceid); ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/presenter.c | 90 ++++++++++++++++++++++++++++++++++++++++++++ dlls/evr/tests/evr.c | 19 +++++++++- 2 files changed, 108 insertions(+), 1 deletion(-)
diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index 51792cdc2cd..ac81634f7ca 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -68,6 +68,7 @@ struct video_presenter IMFVideoPositionMapper IMFVideoPositionMapper_iface; IQualProp IQualProp_iface; IMFQualityAdvise IMFQualityAdvise_iface; + IDirect3DDeviceManager9 IDirect3DDeviceManager9_iface; IMFVideoSampleAllocatorNotify allocator_cb; IUnknown IUnknown_inner; IUnknown *outer_unk; @@ -152,6 +153,11 @@ static struct video_presenter *impl_from_IMFQualityAdvise(IMFQualityAdvise *ifac return CONTAINING_RECORD(iface, struct video_presenter, IMFQualityAdvise_iface); }
+static struct video_presenter *impl_from_IDirect3DDeviceManager9(IDirect3DDeviceManager9 *iface) +{ + return CONTAINING_RECORD(iface, struct video_presenter, IDirect3DDeviceManager9_iface); +} + static void video_presenter_notify_renderer(struct video_presenter *presenter, LONG event, LONG_PTR param1, LONG_PTR param2) { @@ -480,6 +486,10 @@ static HRESULT WINAPI video_presenter_inner_QueryInterface(IUnknown *iface, REFI { *obj = &presenter->IMFQualityAdvise_iface; } + else if (IsEqualIID(riid, &IID_IDirect3DDeviceManager9)) + { + *obj = &presenter->IDirect3DDeviceManager9_iface; + } else { WARN("Unimplemented interface %s.\n", debugstr_guid(riid)); @@ -1486,6 +1496,85 @@ static const IMFQualityAdviseVtbl video_presenter_quality_advise_vtbl = video_presenter_quality_advise_DropTime, };
+static HRESULT WINAPI video_presenter_device_manager_QueryInterface(IDirect3DDeviceManager9 *iface, + REFIID riid, void **obj) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IMFVideoPresenter_QueryInterface(&presenter->IMFVideoPresenter_iface, riid, obj); +} + +static ULONG WINAPI video_presenter_device_manager_AddRef(IDirect3DDeviceManager9 *iface) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IMFVideoPresenter_AddRef(&presenter->IMFVideoPresenter_iface); +} + +static ULONG WINAPI video_presenter_device_manager_Release(IDirect3DDeviceManager9 *iface) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IMFVideoPresenter_Release(&presenter->IMFVideoPresenter_iface); +} + +static HRESULT WINAPI video_presenter_device_manager_ResetDevice(IDirect3DDeviceManager9 *iface, + IDirect3DDevice9 *device, UINT token) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_ResetDevice(presenter->device_manager, device, token); +} + +static HRESULT WINAPI video_presenter_device_manager_OpenDeviceHandle(IDirect3DDeviceManager9 *iface, HANDLE *hdevice) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_OpenDeviceHandle(presenter->device_manager, hdevice); +} + +static HRESULT WINAPI video_presenter_device_manager_CloseDeviceHandle(IDirect3DDeviceManager9 *iface, HANDLE hdevice) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_CloseDeviceHandle(presenter->device_manager, hdevice); +} + +static HRESULT WINAPI video_presenter_device_manager_TestDevice(IDirect3DDeviceManager9 *iface, HANDLE hdevice) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_TestDevice(presenter->device_manager, hdevice); +} + +static HRESULT WINAPI video_presenter_device_manager_LockDevice(IDirect3DDeviceManager9 *iface, HANDLE hdevice, + IDirect3DDevice9 **device, BOOL block) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_LockDevice(presenter->device_manager, hdevice, device, block); +} + +static HRESULT WINAPI video_presenter_device_manager_UnlockDevice(IDirect3DDeviceManager9 *iface, HANDLE hdevice, + BOOL savestate) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_UnlockDevice(presenter->device_manager, hdevice, savestate); +} + +static HRESULT WINAPI video_presenter_device_manager_GetVideoService(IDirect3DDeviceManager9 *iface, HANDLE hdevice, + REFIID riid, void **service) +{ + struct video_presenter *presenter = impl_from_IDirect3DDeviceManager9(iface); + return IDirect3DDeviceManager9_GetVideoService(presenter->device_manager, hdevice, riid, service); +} + +static const IDirect3DDeviceManager9Vtbl video_presenter_device_manager_vtbl = +{ + video_presenter_device_manager_QueryInterface, + video_presenter_device_manager_AddRef, + video_presenter_device_manager_Release, + video_presenter_device_manager_ResetDevice, + video_presenter_device_manager_OpenDeviceHandle, + video_presenter_device_manager_CloseDeviceHandle, + video_presenter_device_manager_TestDevice, + video_presenter_device_manager_LockDevice, + video_presenter_device_manager_UnlockDevice, + video_presenter_device_manager_GetVideoService, +}; + HRESULT WINAPI MFCreateVideoPresenter(IUnknown *owner, REFIID riid_device, REFIID riid, void **obj) { TRACE("%p, %s, %s, %p.\n", owner, debugstr_guid(riid_device), debugstr_guid(riid), obj); @@ -1558,6 +1647,7 @@ HRESULT evr_presenter_create(IUnknown *outer, void **out) object->IMFQualityAdvise_iface.lpVtbl = &video_presenter_quality_advise_vtbl; object->allocator_cb.lpVtbl = &video_presenter_allocator_cb_vtbl; object->IUnknown_inner.lpVtbl = &video_presenter_inner_vtbl; + object->IDirect3DDeviceManager9_iface.lpVtbl = &video_presenter_device_manager_vtbl; object->outer_unk = outer ? outer : &object->IUnknown_inner; object->refcount = 1; object->src_rect.right = object->src_rect.bottom = 1.0f; diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 55986414a70..34a69559b65 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -1128,8 +1128,8 @@ static void test_default_presenter(void) IMFRateSupport *rate_support; IDirect3DDeviceManager9 *dm; IMFVideoDeviceID *deviceid; + IUnknown *unk, *unk2; HWND hwnd, hwnd2; - IUnknown *unk; DWORD flags; float rate; HRESULT hr; @@ -1153,6 +1153,7 @@ static void test_default_presenter(void) check_interface(presenter, &IID_IMFVideoPresenter, TRUE); check_interface(presenter, &IID_IMFVideoDeviceID, TRUE); check_interface(presenter, &IID_IMFQualityAdvise, TRUE); + check_interface(presenter, &IID_IDirect3DDeviceManager9, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoPositionMapper, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoDisplayControl, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoPresenter, TRUE); @@ -1164,8 +1165,24 @@ static void test_default_presenter(void) check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoDeviceID, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFQualityAdvise, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFTransform, FALSE); + check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IDirect3DDeviceManager9, TRUE); check_service_interface(presenter, &MR_VIDEO_ACCELERATION_SERVICE, &IID_IDirect3DDeviceManager9, TRUE);
+ /* Query arbitrary supported interface back from device manager wrapper. */ + hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IDirect3DDeviceManager9, (void **)&dm); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + hr = IDirect3DDeviceManager9_QueryInterface(dm, &IID_IQualProp, (void **)&unk); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + IUnknown_Release(unk); + hr = IDirect3DDeviceManager9_QueryInterface(dm, &IID_IUnknown, (void **)&unk); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + hr = IMFVideoPresenter_QueryInterface(presenter, &IID_IUnknown, (void **)&unk2); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(unk == unk2, "Unexpected interface.\n"); + IUnknown_Release(unk2); + IUnknown_Release(unk); + IDirect3DDeviceManager9_Release(dm); + hr = MFGetService((IUnknown *)presenter, &MR_VIDEO_MIXER_SERVICE, &IID_IUnknown, (void **)&unk); ok(hr == MF_E_UNSUPPORTED_SERVICE, "Unexpected hr %#x.\n", hr);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/tests/evr.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/evr/tests/evr.c b/dlls/evr/tests/evr.c index 34a69559b65..18822e1e4fa 100644 --- a/dlls/evr/tests/evr.c +++ b/dlls/evr/tests/evr.c @@ -1154,6 +1154,7 @@ static void test_default_presenter(void) check_interface(presenter, &IID_IMFVideoDeviceID, TRUE); check_interface(presenter, &IID_IMFQualityAdvise, TRUE); check_interface(presenter, &IID_IDirect3DDeviceManager9, TRUE); + todo_wine check_interface(presenter, &IID_IMFQualityAdviseLimits, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoPositionMapper, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoDisplayControl, TRUE); check_service_interface(presenter, &MR_VIDEO_RENDER_SERVICE, &IID_IMFVideoPresenter, TRUE);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/evr/presenter.c | 324 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 273 insertions(+), 51 deletions(-)
diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index ac81634f7ca..f0733f6f060 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -48,6 +48,17 @@ enum presenter_flags enum streaming_thread_message { EVRM_STOP = WM_USER, + EVRM_PRESENT = WM_USER + 1, + EVRM_PROCESS_INPUT = WM_USER + 2, +}; + +struct sample_queue +{ + IMFSample **samples; + unsigned int size; + unsigned int used; + unsigned int front; + unsigned int back; };
struct streaming_thread @@ -55,6 +66,7 @@ struct streaming_thread HANDLE hthread; HANDLE ready_event; DWORD tid; + struct sample_queue queue; };
struct video_presenter @@ -84,7 +96,9 @@ struct video_presenter
IMFVideoSampleAllocator *allocator; struct streaming_thread thread; + unsigned int allocator_capacity; IMFMediaType *media_type; + LONGLONG frame_time_threshold; UINT reset_token; HWND video_window; MFVideoNormalizedRect src_rect; @@ -261,10 +275,28 @@ static HRESULT video_presenter_set_media_type(struct video_presenter *presenter,
video_presenter_reset_media_type(presenter);
- if (SUCCEEDED(hr = IMFVideoSampleAllocator_InitializeSampleAllocator(presenter->allocator, 3, media_type))) + if (SUCCEEDED(hr = IMFVideoSampleAllocator_InitializeSampleAllocator(presenter->allocator, + presenter->allocator_capacity, media_type))) { + MFRatio ratio; + UINT64 rate, frametime; + presenter->media_type = media_type; IMFMediaType_AddRef(presenter->media_type); + + if (SUCCEEDED(IMFMediaType_GetUINT64(presenter->media_type, &MF_MT_FRAME_RATE, &rate))) + { + ratio.Denominator = rate; + ratio.Numerator = rate >> 32; + } + else + { + ratio.Denominator = 1; + ratio.Numerator = 30; + } + + MFFrameRateToAverageTimePerFrame(ratio.Numerator, ratio.Denominator, &frametime); + presenter->frame_time_threshold = frametime / 4; } else WARN("Failed to initialize sample allocator, hr %#x.\n", hr); @@ -303,81 +335,167 @@ static HRESULT video_presenter_invalidate_media_type(struct video_presenter *pre return hr; }
-static DWORD CALLBACK video_presenter_streaming_thread(void *arg) +static void video_presenter_sample_queue_init(struct video_presenter *presenter) { - struct video_presenter *presenter = arg; - BOOL stop_thread = FALSE; - MSG msg; + struct sample_queue *queue = &presenter->thread.queue;
- PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + if (queue->size) + return;
- SetEvent(presenter->thread.ready_event); + memset(queue, 0, sizeof(*queue)); + queue->samples = heap_calloc(presenter->allocator_capacity, sizeof(*queue->samples)); + queue->size = presenter->allocator_capacity; + queue->back = queue->size - 1; +}
- while (!stop_thread) +static void video_presenter_sample_queue_push(struct video_presenter *presenter, IMFSample *sample) +{ + struct sample_queue *queue = &presenter->thread.queue; + + EnterCriticalSection(&presenter->cs); + if (queue->used != queue->size) { - MsgWaitForMultipleObjects(0, NULL, FALSE, INFINITE, QS_POSTMESSAGE); + queue->back = (queue->back + 1) % queue->size; + queue->samples[queue->back] = sample; + queue->used++; + IMFSample_AddRef(sample); + } + LeaveCriticalSection(&presenter->cs); +}
- while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) - { - switch (msg.message) - { - case EVRM_STOP: - stop_thread = TRUE; - break; +static BOOL video_presenter_sample_queue_pop(struct video_presenter *presenter, IMFSample **sample) +{ + struct sample_queue *queue = &presenter->thread.queue;
- default: - ; - } - } + EnterCriticalSection(&presenter->cs); + if (queue->used) + { + *sample = queue->samples[queue->front]; + queue->front = (queue->front + 1) % queue->size; + queue->used--; } + else + *sample = NULL; + LeaveCriticalSection(&presenter->cs);
- return 0; + return *sample != NULL; }
-static HRESULT video_presenter_start_streaming(struct video_presenter *presenter) +static HRESULT video_presenter_get_sample_surface(IMFSample *sample, IDirect3DSurface9 **surface) { - if (presenter->thread.hthread) - return S_OK; + IMFMediaBuffer *buffer; + IMFGetService *gs; + HRESULT hr;
- if (!(presenter->thread.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL))) - return HRESULT_FROM_WIN32(GetLastError()); + if (FAILED(hr = IMFSample_GetBufferByIndex(sample, 0, &buffer))) + return hr;
- if (!(presenter->thread.hthread = CreateThread(NULL, 0, video_presenter_streaming_thread, - presenter, 0, &presenter->thread.tid))) + hr = IMFMediaBuffer_QueryInterface(buffer, &IID_IMFGetService, (void **)&gs); + IMFMediaBuffer_Release(buffer); + if (FAILED(hr)) + return hr; + + hr = IMFGetService_GetService(gs, &MR_BUFFER_SERVICE, &IID_IDirect3DSurface9, (void **)surface); + IMFGetService_Release(gs); + return hr; +} + +static void video_presenter_sample_present(struct video_presenter *presenter, IMFSample *sample) +{ + IDirect3DSurface9 *surface, *backbuffer; + IDirect3DDevice9 *device; + HRESULT hr; + + if (!presenter->swapchain) + return; + + if (FAILED(hr = video_presenter_get_sample_surface(sample, &surface))) { - WARN("Failed to create streaming thread.\n"); - CloseHandle(presenter->thread.ready_event); - presenter->thread.ready_event = NULL; - return E_FAIL; + WARN("Failed to get sample surface, hr %#x.\n", hr); + return; }
- video_presenter_set_allocator_callback(presenter, &presenter->allocator_cb); - - WaitForSingleObject(presenter->thread.ready_event, INFINITE); - CloseHandle(presenter->thread.ready_event); - presenter->thread.ready_event = NULL; + if (FAILED(hr = IDirect3DSwapChain9_GetBackBuffer(presenter->swapchain, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer))) + { + WARN("Failed to get a backbuffer, hr %#x.\n", hr); + IDirect3DSurface9_Release(surface); + return; + }
- TRACE("Started streaming thread, tid %#x.\n", presenter->thread.tid); + IDirect3DSwapChain9_GetDevice(presenter->swapchain, &device); + IDirect3DDevice9_StretchRect(device, surface, NULL, backbuffer, NULL, D3DTEXF_POINT); + IDirect3DSwapChain9_Present(presenter->swapchain, NULL, NULL, NULL, NULL, 0);
- return S_OK; + IDirect3DDevice9_Release(device); + IDirect3DSurface9_Release(backbuffer); + IDirect3DSurface9_Release(surface); }
-static HRESULT video_presenter_end_streaming(struct video_presenter *presenter) +static void video_presenter_check_queue(struct video_presenter *presenter, + unsigned int *next_wait) { - if (!presenter->thread.hthread) - return S_OK; + LONGLONG pts, clocktime, delta; + unsigned int wait = 0; + BOOL present = TRUE; + IMFSample *sample; + MFTIME systime; + HRESULT hr;
- PostThreadMessageW(presenter->thread.tid, EVRM_STOP, 0, 0); + while (video_presenter_sample_queue_pop(presenter, &sample)) + { + wait = 0;
- WaitForSingleObject(presenter->thread.hthread, INFINITE); - CloseHandle(presenter->thread.hthread); + if (presenter->clock) + { + pts = clocktime = 0;
- TRACE("Terminated streaming thread tid %#x.\n", presenter->thread.tid); + hr = IMFSample_GetSampleTime(sample, &pts); + if (SUCCEEDED(hr)) + hr = IMFClock_GetCorrelatedTime(presenter->clock, 0, &clocktime, &systime);
- memset(&presenter->thread, 0, sizeof(presenter->thread)); - video_presenter_set_allocator_callback(presenter, NULL); + delta = pts - clocktime; + if (delta > 3 * presenter->frame_time_threshold) + { + /* Convert 100ns -> msec */ + wait = (delta - 3 * presenter->frame_time_threshold) / 100000; + present = FALSE; + } + }
- return S_OK; + if (present) + video_presenter_sample_present(presenter, sample); + else + video_presenter_sample_queue_push(presenter, sample); + + IMFSample_Release(sample); + + if (wait > 0) + break; + } + + if (!wait) + wait = INFINITE; + + *next_wait = wait; +} + +static void video_presenter_schedule_sample(struct video_presenter *presenter, IMFSample *sample) +{ + if (!presenter->thread.tid) + { + WARN("Streaming thread hasn't been started.\n"); + return; + } + + if (presenter->clock) + { + video_presenter_sample_queue_push(presenter, sample); + PostThreadMessageW(presenter->thread.tid, EVRM_PRESENT, 0, 0); + } + else + { + video_presenter_sample_present(presenter, sample); + } }
static HRESULT video_presenter_process_input(struct video_presenter *presenter) @@ -431,7 +549,8 @@ static HRESULT video_presenter_process_input(struct video_presenter *presenter) if (buffer.pEvents) IMFCollection_Release(buffer.pEvents);
- /* FIXME: for now drop output sample back to the pool */ + video_presenter_schedule_sample(presenter, sample); + IMFSample_Release(sample); } } @@ -439,6 +558,102 @@ static HRESULT video_presenter_process_input(struct video_presenter *presenter) return S_OK; }
+static DWORD CALLBACK video_presenter_streaming_thread(void *arg) +{ + struct video_presenter *presenter = arg; + unsigned int wait = INFINITE; + BOOL stop_thread = FALSE; + MSG msg; + + PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + SetEvent(presenter->thread.ready_event); + + while (!stop_thread) + { + if (MsgWaitForMultipleObjects(0, NULL, FALSE, wait, QS_POSTMESSAGE) == WAIT_TIMEOUT) + video_presenter_check_queue(presenter, &wait); + + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + BOOL peek = TRUE; + + switch (msg.message) + { + case EVRM_STOP: + stop_thread = TRUE; + break; + + case EVRM_PRESENT: + if (peek) + { + video_presenter_check_queue(presenter, &wait); + peek = wait != INFINITE; + } + break; + + case EVRM_PROCESS_INPUT: + EnterCriticalSection(&presenter->cs); + video_presenter_process_input(presenter); + LeaveCriticalSection(&presenter->cs); + break; + default: + ; + } + } + } + + return 0; +} + +static HRESULT video_presenter_start_streaming(struct video_presenter *presenter) +{ + if (presenter->thread.hthread) + return S_OK; + + video_presenter_sample_queue_init(presenter); + + if (!(presenter->thread.ready_event = CreateEventW(NULL, FALSE, FALSE, NULL))) + return HRESULT_FROM_WIN32(GetLastError()); + + if (!(presenter->thread.hthread = CreateThread(NULL, 0, video_presenter_streaming_thread, + presenter, 0, &presenter->thread.tid))) + { + WARN("Failed to create streaming thread.\n"); + CloseHandle(presenter->thread.ready_event); + presenter->thread.ready_event = NULL; + return E_FAIL; + } + + video_presenter_set_allocator_callback(presenter, &presenter->allocator_cb); + + WaitForSingleObject(presenter->thread.ready_event, INFINITE); + CloseHandle(presenter->thread.ready_event); + presenter->thread.ready_event = NULL; + + TRACE("Started streaming thread, tid %#x.\n", presenter->thread.tid); + + return S_OK; +} + +static HRESULT video_presenter_end_streaming(struct video_presenter *presenter) +{ + if (!presenter->thread.hthread) + return S_OK; + + PostThreadMessageW(presenter->thread.tid, EVRM_STOP, 0, 0); + + WaitForSingleObject(presenter->thread.hthread, INFINITE); + CloseHandle(presenter->thread.hthread); + + TRACE("Terminated streaming thread tid %#x.\n", presenter->thread.tid); + + memset(&presenter->thread, 0, sizeof(presenter->thread)); + video_presenter_set_allocator_callback(presenter, NULL); + + return S_OK; +} + static HRESULT WINAPI video_presenter_inner_QueryInterface(IUnknown *iface, REFIID riid, void **obj) { struct video_presenter *presenter = impl_from_IUnknown(iface); @@ -1343,7 +1558,13 @@ static ULONG WINAPI video_presenter_allocator_cb_Release(IMFVideoSampleAllocator
static HRESULT WINAPI video_presenter_allocator_cb_NotifyRelease(IMFVideoSampleAllocatorNotify *iface) { - return E_NOTIMPL; + struct video_presenter *presenter = impl_from_IMFVideoSampleAllocatorNotify(iface); + + /* Release notification is executed under allocator lock, instead of processing samples here + notify streaming thread. */ + PostThreadMessageW(presenter->thread.tid, EVRM_PROCESS_INPUT, 0, 0); + + return S_OK; }
static const IMFVideoSampleAllocatorNotifyVtbl video_presenter_allocator_cb_vtbl = @@ -1652,6 +1873,7 @@ HRESULT evr_presenter_create(IUnknown *outer, void **out) object->refcount = 1; object->src_rect.right = object->src_rect.bottom = 1.0f; object->ar_mode = MFVideoARMode_PreservePicture | MFVideoARMode_PreservePixel; + object->allocator_capacity = 3; InitializeCriticalSection(&object->cs);
if (FAILED(hr = DXVA2CreateDirect3DDeviceManager9(&object->reset_token, &object->device_manager)))