[v2 PATCH 4/5] mf/sar: Implement sample processing.

Nikolay Sivov nsivov at codeweavers.com
Wed May 13 08:42:09 CDT 2020


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mf/sar.c   | 275 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/mfapi.h |   1 +
 2 files changed, 271 insertions(+), 5 deletions(-)

diff --git a/dlls/mf/sar.c b/dlls/mf/sar.c
index 5f035a801ce..59ee9c3dc4e 100644
--- a/dlls/mf/sar.c
+++ b/dlls/mf/sar.c
@@ -28,6 +28,7 @@
 
 #include "wine/debug.h"
 #include "wine/heap.h"
+#include "wine/list.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
 
@@ -42,6 +43,32 @@ enum audio_renderer_flags
 {
     SAR_SHUT_DOWN = 0x1,
     SAR_PREROLLED = 0x2,
+    SAR_SAMPLE_REQUESTED = 0x4,
+};
+
+enum queued_object_type
+{
+    OBJECT_TYPE_SAMPLE,
+    OBJECT_TYPE_MARKER,
+};
+
+struct queued_object
+{
+    struct list entry;
+    enum queued_object_type type;
+    union
+    {
+        struct
+        {
+            IMFSample *sample;
+            unsigned int frame_offset;
+        } sample;
+        struct
+        {
+            MFSTREAMSINK_MARKER_TYPE type;
+            PROPVARIANT context;
+        } marker;
+    } u;
 };
 
 struct audio_renderer
@@ -56,6 +83,7 @@ struct audio_renderer
     IMFSimpleAudioVolume IMFSimpleAudioVolume_iface;
     IMFAudioStreamVolume IMFAudioStreamVolume_iface;
     IMFAudioPolicy IMFAudioPolicy_iface;
+    IMFAsyncCallback render_callback;
     LONG refcount;
     IMFMediaEventQueue *event_queue;
     IMFMediaEventQueue *stream_event_queue;
@@ -64,14 +92,34 @@ struct audio_renderer
     IMFMediaType *current_media_type;
     IMMDevice *device;
     IAudioClient *audio_client;
+    IAudioRenderClient *audio_render_client;
     IAudioStreamVolume *stream_volume;
     ISimpleAudioVolume *audio_volume;
     HANDLE buffer_ready_event;
+    MFWORKITEM_KEY buffer_ready_key;
+    unsigned int frame_size;
+    struct list queue;
     enum stream_state state;
     unsigned int flags;
     CRITICAL_SECTION cs;
 };
 
+static void release_pending_object(struct queued_object *object)
+{
+    list_remove(&object->entry);
+    switch (object->type)
+    {
+        case OBJECT_TYPE_SAMPLE:
+            if (object->u.sample.sample)
+                IMFSample_Release(object->u.sample.sample);
+            break;
+        case OBJECT_TYPE_MARKER:
+            PropVariantClear(&object->u.marker.context);
+            break;
+    }
+    heap_free(object);
+}
+
 static struct audio_renderer *impl_from_IMFMediaSink(IMFMediaSink *iface)
 {
     return CONTAINING_RECORD(iface, struct audio_renderer, IMFMediaSink_iface);
@@ -122,6 +170,11 @@ static struct audio_renderer *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler
     return CONTAINING_RECORD(iface, struct audio_renderer, IMFMediaTypeHandler_iface);
 }
 
+static struct audio_renderer *impl_from_render_callback_IMFAsyncCallback(IMFAsyncCallback *iface)
+{
+    return CONTAINING_RECORD(iface, struct audio_renderer, render_callback);
+}
+
 static HRESULT WINAPI audio_renderer_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj)
 {
     struct audio_renderer *renderer = impl_from_IMFMediaSink(iface);
@@ -171,9 +224,18 @@ static ULONG WINAPI audio_renderer_sink_AddRef(IMFMediaSink *iface)
 
 static void audio_renderer_release_audio_client(struct audio_renderer *renderer)
 {
+    MFCancelWorkItem(renderer->buffer_ready_key);
+    renderer->buffer_ready_key = 0;
     if (renderer->audio_client)
+    {
+        IAudioClient_Stop(renderer->audio_client);
+        IAudioClient_Reset(renderer->audio_client);
         IAudioClient_Release(renderer->audio_client);
+    }
     renderer->audio_client = NULL;
+    if (renderer->audio_render_client)
+        IAudioRenderClient_Release(renderer->audio_render_client);
+    renderer->audio_render_client = NULL;
     if (renderer->stream_volume)
         IAudioStreamVolume_Release(renderer->stream_volume);
     renderer->stream_volume = NULL;
@@ -187,6 +249,7 @@ static ULONG WINAPI audio_renderer_sink_Release(IMFMediaSink *iface)
 {
     struct audio_renderer *renderer = impl_from_IMFMediaSink(iface);
     ULONG refcount = InterlockedDecrement(&renderer->refcount);
+    struct queued_object *obj, *obj2;
 
     TRACE("%p, refcount %u.\n", iface, refcount);
 
@@ -204,8 +267,12 @@ static ULONG WINAPI audio_renderer_sink_Release(IMFMediaSink *iface)
             IMFMediaType_Release(renderer->media_type);
         if (renderer->current_media_type)
             IMFMediaType_Release(renderer->current_media_type);
-        CloseHandle(renderer->buffer_ready_event);
         audio_renderer_release_audio_client(renderer);
+        CloseHandle(renderer->buffer_ready_event);
+        LIST_FOR_EACH_ENTRY_SAFE(obj, obj2, &renderer->queue, struct queued_object, entry)
+        {
+            release_pending_object(obj);
+        }
         DeleteCriticalSection(&renderer->cs);
         heap_free(renderer);
     }
@@ -388,6 +455,7 @@ static HRESULT WINAPI audio_renderer_sink_Shutdown(IMFMediaSink *iface)
     IMFMediaEventQueue_Shutdown(renderer->event_queue);
     IMFMediaEventQueue_Shutdown(renderer->stream_event_queue);
     audio_renderer_set_presentation_clock(renderer, NULL);
+    audio_renderer_release_audio_client(renderer);
     LeaveCriticalSection(&renderer->cs);
 
     return S_OK;
@@ -1248,19 +1316,82 @@ static HRESULT WINAPI audio_renderer_stream_GetMediaTypeHandler(IMFStreamSink *i
     return S_OK;
 }
 
+static HRESULT stream_queue_sample(struct audio_renderer *renderer, IMFSample *sample)
+{
+    struct queued_object *object;
+
+    if (!(object = heap_alloc_zero(sizeof(*object))))
+        return E_OUTOFMEMORY;
+
+    object->type = OBJECT_TYPE_SAMPLE;
+    object->u.sample.sample = sample;
+    IMFSample_AddRef(object->u.sample.sample);
+
+    list_add_tail(&renderer->queue, &object->entry);
+
+    return S_OK;
+}
+
 static HRESULT WINAPI audio_renderer_stream_ProcessSample(IMFStreamSink *iface, IMFSample *sample)
 {
-    FIXME("%p, %p.\n", iface, sample);
+    struct audio_renderer *renderer = impl_from_IMFStreamSink(iface);
+    HRESULT hr = S_OK;
 
-    return E_NOTIMPL;
+    TRACE("%p, %p.\n", iface, sample);
+
+    if (!sample)
+        return E_POINTER;
+
+    if (renderer->flags & SAR_SHUT_DOWN)
+        return MF_E_STREAMSINK_REMOVED;
+
+    EnterCriticalSection(&renderer->cs);
+    if (renderer->state == STREAM_STATE_RUNNING)
+        hr = stream_queue_sample(renderer, sample);
+    renderer->flags &= ~SAR_SAMPLE_REQUESTED;
+    LeaveCriticalSection(&renderer->cs);
+
+    return hr;
+}
+
+static HRESULT stream_place_marker(struct audio_renderer *renderer, MFSTREAMSINK_MARKER_TYPE marker_type,
+        const PROPVARIANT *context_value)
+{
+    struct queued_object *marker;
+    HRESULT hr = S_OK;
+
+    if (!(marker = heap_alloc_zero(sizeof(*marker))))
+        return E_OUTOFMEMORY;
+
+    marker->type = OBJECT_TYPE_MARKER;
+    marker->u.marker.type = marker_type;
+    PropVariantInit(&marker->u.marker.context);
+    if (context_value)
+        hr = PropVariantCopy(&marker->u.marker.context, context_value);
+    if (SUCCEEDED(hr))
+        list_add_tail(&renderer->queue, &marker->entry);
+    else
+        release_pending_object(marker);
+
+    return hr;
 }
 
 static HRESULT WINAPI audio_renderer_stream_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type,
         const PROPVARIANT *marker_value, const PROPVARIANT *context_value)
 {
-    FIXME("%p, %d, %p, %p.\n", iface, marker_type, marker_value, context_value);
+    struct audio_renderer *renderer = impl_from_IMFStreamSink(iface);
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("%p, %d, %p, %p.\n", iface, marker_type, marker_value, context_value);
+
+    if (renderer->flags & SAR_SHUT_DOWN)
+        return MF_E_STREAMSINK_REMOVED;
+
+    EnterCriticalSection(&renderer->cs);
+    hr = stream_place_marker(renderer, marker_type, context_value);
+    LeaveCriticalSection(&renderer->cs);
+
+    return hr;
 }
 
 static HRESULT WINAPI audio_renderer_stream_Flush(IMFStreamSink *iface)
@@ -1350,6 +1481,7 @@ static HRESULT WINAPI audio_renderer_stream_type_handler_GetMediaTypeByIndex(IMF
 
 static HRESULT audio_renderer_create_audio_client(struct audio_renderer *renderer)
 {
+    IMFAsyncResult *result;
     WAVEFORMATEX *wfx;
     HRESULT hr;
 
@@ -1372,6 +1504,8 @@ static HRESULT audio_renderer_create_audio_client(struct audio_renderer *rendere
         return hr;
     }
 
+    renderer->frame_size = wfx->wBitsPerSample * wfx->nChannels / 8;
+
     hr = IAudioClient_Initialize(renderer->audio_client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
             1000000, 0, wfx, NULL);
     CoTaskMemFree(wfx);
@@ -1394,12 +1528,26 @@ static HRESULT audio_renderer_create_audio_client(struct audio_renderer *rendere
         return hr;
     }
 
+    if (FAILED(hr = IAudioClient_GetService(renderer->audio_client, &IID_IAudioRenderClient,
+            (void **)&renderer->audio_render_client)))
+    {
+        WARN("Failed to get audio render client, hr %#x.\n", hr);
+        return hr;
+    }
+
     if (FAILED(hr = IAudioClient_SetEventHandle(renderer->audio_client, renderer->buffer_ready_event)))
     {
         WARN("Failed to set event handle, hr %#x.\n", hr);
         return hr;
     }
 
+    if (SUCCEEDED(hr = MFCreateAsyncResult(NULL, &renderer->render_callback, NULL, &result)))
+    {
+        if (FAILED(hr = MFPutWaitingWorkItem(renderer->buffer_ready_event, 0, result, &renderer->buffer_ready_key)))
+            WARN("Failed to submit wait item, hr %#x.\n", hr);
+        IMFAsyncResult_Release(result);
+    }
+
     return hr;
 }
 
@@ -1532,6 +1680,121 @@ static HRESULT audio_renderer_collect_supported_types(struct audio_renderer *ren
     return hr;
 }
 
+static HRESULT WINAPI audio_renderer_render_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
+{
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), 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 audio_renderer_render_callback_AddRef(IMFAsyncCallback *iface)
+{
+    struct audio_renderer *renderer = impl_from_render_callback_IMFAsyncCallback(iface);
+    return IMFMediaSink_AddRef(&renderer->IMFMediaSink_iface);
+}
+
+static ULONG WINAPI audio_renderer_render_callback_Release(IMFAsyncCallback *iface)
+{
+    struct audio_renderer *renderer = impl_from_render_callback_IMFAsyncCallback(iface);
+    return IMFMediaSink_Release(&renderer->IMFMediaSink_iface);
+}
+
+static HRESULT WINAPI audio_renderer_render_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
+{
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI audio_renderer_render_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
+{
+    struct audio_renderer *renderer = impl_from_render_callback_IMFAsyncCallback(iface);
+    unsigned int src_frames, dst_frames, max_frames, src_len;
+    struct queued_object *obj, *obj2;
+    BOOL keep_sample = FALSE;
+    IMFMediaBuffer *buffer;
+    BYTE *dst, *src;
+    HRESULT hr;
+
+    EnterCriticalSection(&renderer->cs);
+
+    LIST_FOR_EACH_ENTRY_SAFE(obj, obj2, &renderer->queue, struct queued_object, entry)
+    {
+        if (obj->type == OBJECT_TYPE_MARKER)
+        {
+            IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkMarker,
+                &GUID_NULL, S_OK, &obj->u.marker.context);
+        }
+        else if (obj->type == OBJECT_TYPE_SAMPLE)
+        {
+            if (SUCCEEDED(IMFSample_ConvertToContiguousBuffer(obj->u.sample.sample, &buffer)))
+            {
+                if (SUCCEEDED(IMFMediaBuffer_Lock(buffer, &src, NULL, &src_len)))
+                {
+                    if ((src_frames = src_len / renderer->frame_size))
+                    {
+                        if (SUCCEEDED(IAudioClient_GetBufferSize(renderer->audio_client, &max_frames)))
+                        {
+                            src_frames -= obj->u.sample.frame_offset;
+                            dst_frames = min(src_frames, max_frames);
+
+                            if (SUCCEEDED(hr = IAudioRenderClient_GetBuffer(renderer->audio_render_client, dst_frames, &dst)))
+                            {
+                                memcpy(dst, src + obj->u.sample.frame_offset * renderer->frame_size,
+                                        dst_frames * renderer->frame_size);
+
+                                IAudioRenderClient_ReleaseBuffer(renderer->audio_render_client, dst_frames, 0);
+
+                                obj->u.sample.frame_offset += dst_frames;
+                            }
+
+                            keep_sample = FAILED(hr) || src_frames > max_frames;
+                        }
+                    }
+                    IMFMediaBuffer_Unlock(buffer);
+                }
+                IMFMediaBuffer_Release(buffer);
+            }
+        }
+
+        if (keep_sample)
+            break;
+
+        list_remove(&obj->entry);
+        release_pending_object(obj);
+    }
+
+    if (list_empty(&renderer->queue) && !(renderer->flags & SAR_SAMPLE_REQUESTED))
+    {
+        IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL);
+        renderer->flags |= SAR_SAMPLE_REQUESTED;
+    }
+
+    if (FAILED(hr = MFPutWaitingWorkItem(renderer->buffer_ready_event, 0, result, &renderer->buffer_ready_key)))
+        WARN("Failed to submit wait item, hr %#x.\n", hr);
+
+    LeaveCriticalSection(&renderer->cs);
+
+    return S_OK;
+}
+
+static const IMFAsyncCallbackVtbl audio_renderer_render_callback_vtbl =
+{
+    audio_renderer_render_callback_QueryInterface,
+    audio_renderer_render_callback_AddRef,
+    audio_renderer_render_callback_Release,
+    audio_renderer_render_callback_GetParameters,
+    audio_renderer_render_callback_Invoke,
+};
+
 static HRESULT sar_create_object(IMFAttributes *attributes, void *user_context, IUnknown **obj)
 {
     struct audio_renderer *renderer;
@@ -1552,9 +1815,11 @@ static HRESULT sar_create_object(IMFAttributes *attributes, void *user_context,
     renderer->IMFSimpleAudioVolume_iface.lpVtbl = &audio_renderer_simple_volume_vtbl;
     renderer->IMFAudioStreamVolume_iface.lpVtbl = &audio_renderer_stream_volume_vtbl;
     renderer->IMFAudioPolicy_iface.lpVtbl = &audio_renderer_policy_vtbl;
+    renderer->render_callback.lpVtbl = &audio_renderer_render_callback_vtbl;
     renderer->refcount = 1;
     InitializeCriticalSection(&renderer->cs);
     renderer->buffer_ready_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+    list_init(&renderer->queue);
 
     if (FAILED(hr = MFCreateEventQueue(&renderer->event_queue)))
         goto failed;
diff --git a/include/mfapi.h b/include/mfapi.h
index c8ae665b10b..05ada65b9db 100644
--- a/include/mfapi.h
+++ b/include/mfapi.h
@@ -535,6 +535,7 @@ HRESULT WINAPI MFInitAttributesFromBlob(IMFAttributes *attributes, const UINT8 *
 HRESULT WINAPI MFInitMediaTypeFromWaveFormatEx(IMFMediaType *mediatype, const WAVEFORMATEX *format, UINT32 size);
 HRESULT WINAPI MFInvokeCallback(IMFAsyncResult *result);
 HRESULT WINAPI MFLockPlatform(void);
+HRESULT WINAPI MFPutWaitingWorkItem(HANDLE event, LONG priority, IMFAsyncResult *result, MFWORKITEM_KEY *key);
 HRESULT WINAPI MFPutWorkItem(DWORD queue, IMFAsyncCallback *callback, IUnknown *state);
 HRESULT WINAPI MFPutWorkItem2(DWORD queue, LONG priority, IMFAsyncCallback *callback, IUnknown *state);
 HRESULT WINAPI MFPutWorkItemEx(DWORD queue, IMFAsyncResult *result);
-- 
2.26.2




More information about the wine-devel mailing list