[PATCH 6/6] evr/presenter: Implement sample presenting functionality.
Nikolay Sivov
nsivov at codeweavers.com
Tue Nov 17 04:41:40 CST 2020
Signed-off-by: Nikolay Sivov <nsivov at 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)))
--
2.29.2
More information about the wine-devel
mailing list