[PATCH 1/5] mf: Add sample grabber sink stub.

Nikolay Sivov nsivov at codeweavers.com
Mon May 20 06:47:43 CDT 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mf/main.c          |   2 +-
 dlls/mf/samplegrabber.c | 422 +++++++++++++++++++++++++++++++++++++++-
 dlls/mf/tests/mf.c      | 143 +++++++++++++-
 include/mferror.h       |  14 ++
 include/mfidl.idl       |  32 +++
 5 files changed, 606 insertions(+), 7 deletions(-)

diff --git a/dlls/mf/main.c b/dlls/mf/main.c
index b5b4b0e7c8..6db13f19db 100644
--- a/dlls/mf/main.c
+++ b/dlls/mf/main.c
@@ -395,7 +395,7 @@ static HRESULT WINAPI activate_object_ActivateObject(IMFActivate *iface, REFIID
         if (FAILED(hr = activate->funcs->create_object((IMFAttributes *)iface, activate->context, &object)))
             return hr;
 
-        if (!InterlockedCompareExchangePointer((void **)&activate->object, object, NULL))
+        if (InterlockedCompareExchangePointer((void **)&activate->object, object, NULL))
             IUnknown_Release(object);
     }
 
diff --git a/dlls/mf/samplegrabber.c b/dlls/mf/samplegrabber.c
index 7b3536467c..997c9db539 100644
--- a/dlls/mf/samplegrabber.c
+++ b/dlls/mf/samplegrabber.c
@@ -19,6 +19,7 @@
 #define COBJMACROS
 
 #include "mfidl.h"
+#include "mferror.h"
 #include "mf_private.h"
 
 #include "wine/debug.h"
@@ -26,6 +27,24 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
 
+struct sample_grabber_stream
+{
+    IMFStreamSink IMFStreamSink_iface;
+    LONG refcount;
+    IMFMediaSink *sink;
+};
+
+struct sample_grabber
+{
+    IMFMediaSink IMFMediaSink_iface;
+    LONG refcount;
+    IMFSampleGrabberSinkCallback *callback;
+    IMFMediaType *media_type;
+    BOOL is_shut_down;
+    IMFStreamSink *stream;
+    CRITICAL_SECTION cs;
+};
+
 struct sample_grabber_activate_context
 {
     IMFMediaType *media_type;
@@ -40,13 +59,412 @@ static void sample_grabber_free_private(void *user_context)
     heap_free(context);
 }
 
-static HRESULT sample_grabber_create_object(IMFAttributes *attributes, void *user_context, IUnknown **obj)
+static struct sample_grabber *impl_from_IMFMediaSink(IMFMediaSink *iface)
+{
+    return CONTAINING_RECORD(iface, struct sample_grabber, IMFMediaSink_iface);
+}
+
+static struct sample_grabber_stream *impl_from_IMFStreamSink(IMFStreamSink *iface)
 {
-    FIXME("%p, %p, %p.\n", attributes, user_context, obj);
+    return CONTAINING_RECORD(iface, struct sample_grabber_stream, IMFStreamSink_iface);
+}
+
+static HRESULT WINAPI sample_grabber_stream_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj)
+{
+    struct sample_grabber_stream *stream = impl_from_IMFStreamSink(iface);
+
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFStreamSink) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = &stream->IMFStreamSink_iface;
+    }
+    else
+    {
+        WARN("Unsupported %s.\n", debugstr_guid(riid));
+        *obj = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown *)*obj);
+
+    return S_OK;
+}
+
+static ULONG WINAPI sample_grabber_stream_AddRef(IMFStreamSink *iface)
+{
+    struct sample_grabber_stream *stream = impl_from_IMFStreamSink(iface);
+    ULONG refcount = InterlockedIncrement(&stream->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI sample_grabber_stream_Release(IMFStreamSink *iface)
+{
+    struct sample_grabber_stream *stream = impl_from_IMFStreamSink(iface);
+    ULONG refcount = InterlockedDecrement(&stream->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        IMFMediaSink_Release(stream->sink);
+        heap_free(stream);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI sample_grabber_stream_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event)
+{
+    FIXME("%p, %#x, %p.\n", iface, flags, event);
 
     return E_NOTIMPL;
 }
 
+static HRESULT WINAPI sample_grabber_stream_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback,
+        IUnknown *state)
+{
+    FIXME("%p, %p, %p.\n", iface, callback, state);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_stream_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result,
+        IMFMediaEvent **event)
+{
+    FIXME("%p, %p, %p.\n", iface, result, event);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_stream_QueueEvent(IMFStreamSink *iface, MediaEventType event_type,
+        REFGUID ext_type, HRESULT hr, const PROPVARIANT *value)
+{
+    FIXME("%p, %u, %s, %#x, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_stream_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **sink)
+{
+    struct sample_grabber_stream *stream = impl_from_IMFStreamSink(iface);
+
+    TRACE("%p, %p.\n", iface, sink);
+
+    *sink = stream->sink;
+    IMFMediaSink_AddRef(*sink);
+
+    return S_OK;
+}
+
+static HRESULT WINAPI sample_grabber_stream_GetIdentifier(IMFStreamSink *iface, DWORD *identifier)
+{
+    TRACE("%p, %p.\n", iface, identifier);
+
+    *identifier = 0;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI sample_grabber_stream_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler)
+{
+    FIXME("%p, %p.\n", iface, handler);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_stream_ProcessSample(IMFStreamSink *iface, IMFSample *sample)
+{
+    FIXME("%p, %p.\n", iface, sample);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_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);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_stream_Flush(IMFStreamSink *iface)
+{
+    FIXME("%p.\n", iface);
+
+    return E_NOTIMPL;
+}
+
+static const IMFStreamSinkVtbl sample_grabber_stream_vtbl =
+{
+    sample_grabber_stream_QueryInterface,
+    sample_grabber_stream_AddRef,
+    sample_grabber_stream_Release,
+    sample_grabber_stream_GetEvent,
+    sample_grabber_stream_BeginGetEvent,
+    sample_grabber_stream_EndGetEvent,
+    sample_grabber_stream_QueueEvent,
+    sample_grabber_stream_GetMediaSink,
+    sample_grabber_stream_GetIdentifier,
+    sample_grabber_stream_GetMediaTypeHandler,
+    sample_grabber_stream_ProcessSample,
+    sample_grabber_stream_PlaceMarker,
+    sample_grabber_stream_Flush,
+};
+
+static HRESULT WINAPI sample_grabber_sink_QueryInterface(IMFMediaSink *iface, REFIID riid, void **obj)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+
+    TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
+
+    if (IsEqualIID(riid, &IID_IMFMediaSink) ||
+            IsEqualIID(riid, &IID_IUnknown))
+    {
+        *obj = &grabber->IMFMediaSink_iface;
+    }
+    else
+    {
+        WARN("Unsupported %s.\n", debugstr_guid(riid));
+        *obj = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IUnknown_AddRef((IUnknown *)*obj);
+
+    return S_OK;
+}
+
+static ULONG WINAPI sample_grabber_sink_AddRef(IMFMediaSink *iface)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+    ULONG refcount = InterlockedIncrement(&grabber->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI sample_grabber_sink_Release(IMFMediaSink *iface)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+    ULONG refcount = InterlockedDecrement(&grabber->refcount);
+
+    TRACE("%p, refcount %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        IMFSampleGrabberSinkCallback_Release(grabber->callback);
+        IMFMediaType_Release(grabber->media_type);
+        DeleteCriticalSection(&grabber->cs);
+        heap_free(grabber);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI sample_grabber_sink_GetCharacteristics(IMFMediaSink *iface, DWORD *flags)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+
+    TRACE("%p, %p.\n", iface, flags);
+
+    if (grabber->is_shut_down)
+        return MF_E_SHUTDOWN;
+
+    *flags = MEDIASINK_FIXED_STREAMS;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI sample_grabber_sink_AddStreamSink(IMFMediaSink *iface, DWORD stream_sink_id,
+    IMFMediaType *media_type, IMFStreamSink **stream_sink)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+
+    TRACE("%p, %#x, %p, %p.\n", iface, stream_sink_id, media_type, stream_sink);
+
+    return grabber->is_shut_down ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
+}
+
+static HRESULT WINAPI sample_grabber_sink_RemoveStreamSink(IMFMediaSink *iface, DWORD stream_sink_id)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+
+    TRACE("%p, %#x.\n", iface, stream_sink_id);
+
+    return grabber->is_shut_down ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED;
+}
+
+static HRESULT WINAPI sample_grabber_sink_GetStreamSinkCount(IMFMediaSink *iface, DWORD *count)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+
+    TRACE("%p, %p.\n", iface, count);
+
+    if (grabber->is_shut_down)
+        return MF_E_SHUTDOWN;
+
+    *count = 1;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI sample_grabber_sink_GetStreamSinkByIndex(IMFMediaSink *iface, DWORD index,
+        IMFStreamSink **stream)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %u, %p.\n", iface, index, stream);
+
+    if (grabber->is_shut_down)
+        return MF_E_SHUTDOWN;
+
+    EnterCriticalSection(&grabber->cs);
+
+    if (grabber->is_shut_down)
+        hr = MF_E_SHUTDOWN;
+    else if (index > 0)
+        hr = MF_E_INVALIDINDEX;
+    else
+    {
+       *stream = grabber->stream;
+       IMFStreamSink_AddRef(*stream);
+    }
+
+    LeaveCriticalSection(&grabber->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI sample_grabber_sink_GetStreamSinkById(IMFMediaSink *iface, DWORD stream_sink_id,
+        IMFStreamSink **stream)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+    HRESULT hr = S_OK;
+
+    TRACE("%p, %#x, %p.\n", iface, stream_sink_id, stream);
+
+    EnterCriticalSection(&grabber->cs);
+
+    if (grabber->is_shut_down)
+        hr = MF_E_SHUTDOWN;
+    else if (stream_sink_id > 0)
+        hr = MF_E_INVALIDSTREAMNUMBER;
+    else
+    {
+        *stream = grabber->stream;
+        IMFStreamSink_AddRef(*stream);
+    }
+
+    LeaveCriticalSection(&grabber->cs);
+
+    return hr;
+}
+
+static HRESULT WINAPI sample_grabber_sink_SetPresentationClock(IMFMediaSink *iface, IMFPresentationClock *clock)
+{
+    FIXME("%p, %p.\n", iface, clock);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_sink_GetPresentationClock(IMFMediaSink *iface, IMFPresentationClock **clock)
+{
+    FIXME("%p, %p.\n", iface, clock);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI sample_grabber_sink_Shutdown(IMFMediaSink *iface)
+{
+    struct sample_grabber *grabber = impl_from_IMFMediaSink(iface);
+
+    TRACE("%p.\n", iface);
+
+    if (grabber->is_shut_down)
+        return MF_E_SHUTDOWN;
+
+    EnterCriticalSection(&grabber->cs);
+    grabber->is_shut_down = TRUE;
+    IMFStreamSink_Release(grabber->stream);
+    grabber->stream = NULL;
+    EnterCriticalSection(&grabber->cs);
+
+    return E_NOTIMPL;
+}
+
+static const IMFMediaSinkVtbl sample_grabber_sink_vtbl =
+{
+    sample_grabber_sink_QueryInterface,
+    sample_grabber_sink_AddRef,
+    sample_grabber_sink_Release,
+    sample_grabber_sink_GetCharacteristics,
+    sample_grabber_sink_AddStreamSink,
+    sample_grabber_sink_RemoveStreamSink,
+    sample_grabber_sink_GetStreamSinkCount,
+    sample_grabber_sink_GetStreamSinkByIndex,
+    sample_grabber_sink_GetStreamSinkById,
+    sample_grabber_sink_SetPresentationClock,
+    sample_grabber_sink_GetPresentationClock,
+    sample_grabber_sink_Shutdown,
+};
+
+static HRESULT sample_grabber_create_stream(IMFMediaSink *sink, IMFStreamSink **stream)
+{
+    struct sample_grabber_stream *object;
+
+    object = heap_alloc_zero(sizeof(*object));
+    if (!object)
+        return E_OUTOFMEMORY;
+
+    object->IMFStreamSink_iface.lpVtbl = &sample_grabber_stream_vtbl;
+    object->refcount = 1;
+    object->sink = sink;
+    IMFMediaSink_AddRef(object->sink);
+
+    *stream = &object->IMFStreamSink_iface;
+
+    return S_OK;
+}
+
+static HRESULT sample_grabber_create_object(IMFAttributes *attributes, void *user_context, IUnknown **obj)
+{
+    struct sample_grabber_activate_context *context = user_context;
+    struct sample_grabber *object;
+    HRESULT hr;
+
+    TRACE("%p, %p, %p.\n", attributes, user_context, obj);
+
+    object = heap_alloc_zero(sizeof(*object));
+    if (!object)
+        return E_OUTOFMEMORY;
+
+    object->IMFMediaSink_iface.lpVtbl = &sample_grabber_sink_vtbl;
+    object->refcount = 1;
+    object->callback = context->callback;
+    IMFSampleGrabberSinkCallback_AddRef(object->callback);
+    object->media_type = context->media_type;
+    IMFMediaType_AddRef(object->media_type);
+    InitializeCriticalSection(&object->cs);
+    if (FAILED(hr = sample_grabber_create_stream(&object->IMFMediaSink_iface, &object->stream)))
+    {
+        IMFMediaSink_Release(&object->IMFMediaSink_iface);
+        return hr;
+    }
+
+    *obj = (IUnknown *)&object->IMFMediaSink_iface;
+
+    TRACE("Created %p.\n", *obj);
+
+    return S_OK;
+}
+
 static const struct activate_funcs sample_grabber_activate_funcs =
 {
     sample_grabber_create_object,
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c
index 37bcb8d029..f2895d5bd3 100644
--- a/dlls/mf/tests/mf.c
+++ b/dlls/mf/tests/mf.c
@@ -1230,7 +1230,6 @@ todo_wine
     ok(hr == MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED, "Unexpected hr %#x.\n", hr);
 
     hr = IMFActivate_ActivateObject(sink_activate, &IID_IMFMediaSink, (void **)&sink);
-todo_wine
     ok(hr == S_OK, "Failed to activate, hr %#x.\n", hr);
 
     hr = IMFTopologyNode_SetObject(sink_node, (IUnknown *)sink);
@@ -1639,9 +1638,16 @@ static IMFSampleGrabberSinkCallback grabber_callback = { &grabber_callback_vtbl
 
 static void test_sample_grabber(void)
 {
+    IMFMediaTypeHandler *handler, *handler2;
+    IMFStreamSink *stream, *stream2;
+    IMFClockStateSink *clocksink;
+    IMFMediaEventGenerator *eg;
+    IMFMediaSink *sink, *sink2;
     IMFMediaType *media_type;
+    DWORD flags, count, id;
     IMFActivate *activate;
     ULONG refcount;
+    IUnknown *unk;
     HRESULT hr;
 
     hr = MFCreateMediaType(&media_type);
@@ -1656,10 +1662,139 @@ static void test_sample_grabber(void)
     hr = MFCreateSampleGrabberSinkActivate(media_type, &grabber_callback, &activate);
     ok(hr == S_OK, "Failed to create grabber activate, hr %#x.\n", hr);
 
-    refcount = IMFMediaType_Release(media_type);
-    ok(refcount == 1, "Unexpected refcount %u.\n", refcount);
+    hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+    ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+    hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
+    ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+
+    hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink);
+    ok(hr == S_OK, "Failed to activate object, hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetCharacteristics(sink, &flags);
+    ok(hr == S_OK, "Failed to get sink flags, hr %#x.\n", hr);
+    ok(flags & MEDIASINK_FIXED_STREAMS, "Unexpected flags %#x.\n", flags);
+
+    hr = IMFMediaSink_GetStreamSinkCount(sink, &count);
+    ok(hr == S_OK, "Failed to get stream count, hr %#x.\n", hr);
+    ok(count == 1, "Unexpected stream count %u.\n", count);
+
+    EXPECT_REF(sink, 3);
+    hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream);
+    ok(hr == S_OK, "Failed to get sink stream, hr %#x.\n", hr);
+    EXPECT_REF(sink, 3);
+    EXPECT_REF(stream, 2);
+
+    hr = IMFStreamSink_GetIdentifier(stream, &id);
+    ok(hr == S_OK, "Failed to get stream id, hr %#x.\n", hr);
+    ok(id == 0, "Unexpected id %#x.\n", id);
+
+    hr = IMFStreamSink_GetMediaSink(stream, &sink2);
+    ok(hr == S_OK, "Failed to get media sink, hr %x.\n", hr);
+    ok(sink2 == sink, "Unexpected sink.\n");
+    IMFMediaSink_Release(sink2);
+
+    hr = IMFMediaSink_GetStreamSinkByIndex(sink, 1, &stream2);
+    ok(hr == MF_E_INVALIDINDEX, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetStreamSinkById(sink, 1, &stream2);
+    ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_AddStreamSink(sink, 1, NULL, &stream2);
+    ok(hr == MF_E_STREAMSINKS_FIXED, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_RemoveStreamSink(sink, 0);
+    ok(hr == MF_E_STREAMSINKS_FIXED, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_RemoveStreamSink(sink, 1);
+    ok(hr == MF_E_STREAMSINKS_FIXED, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_QueryInterface(sink, &IID_IMFClockStateSink, (void **)&clocksink);
+todo_wine
+    ok(hr == S_OK, "Failed to get interface, hr %#x.\n", hr);
+    if (SUCCEEDED(hr))
+        IMFClockStateSink_Release(clocksink);
+
+    hr = IMFMediaSink_QueryInterface(sink, &IID_IMFMediaEventGenerator, (void **)&eg);
+todo_wine
+    ok(hr == S_OK, "Failed to get interface, hr %#x.\n", hr);
+    if (SUCCEEDED(hr))
+        IMFMediaEventGenerator_Release(eg);
+
+    hr = IMFMediaSink_QueryInterface(sink, &IID_IMFPresentationTimeSource, (void **)&unk);
+    ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFStreamSink_QueryInterface(stream, &IID_IMFMediaTypeHandler, (void **)&handler2);
+todo_wine
+    ok(hr == S_OK, "Failed to get handler interface, hr %#x.\n", hr);
+
+    if (SUCCEEDED(hr))
+    {
+        hr = IMFStreamSink_GetMediaTypeHandler(stream, &handler);
+        ok(hr == S_OK, "Failed to get type handler, hr %#x.\n", hr);
+        hr = IMFMediaTypeHandler_GetMediaTypeCount(handler, &count);
+        ok(hr == S_OK, "Failed to get media type count, hr %#x.\n", hr);
+        ok(count == 0, "Unexpected count %u.\n", count);
 
-    IMFActivate_Release(activate);
+        ok(handler == handler2, "Unexpected handler.\n");
+
+        IMFMediaTypeHandler_Release(handler);
+        IMFMediaTypeHandler_Release(handler2);
+    }
+
+    hr = IMFActivate_ShutdownObject(activate);
+todo_wine
+    ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
+
+    hr = IMFMediaSink_Shutdown(sink);
+    ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_Shutdown(sink);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetCharacteristics(sink, &flags);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_AddStreamSink(sink, 1, NULL, &stream2);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetStreamSinkCount(sink, &count);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream2);
+    ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
+
+    hr = IMFStreamSink_GetMediaSink(stream, &sink2);
+    ok(hr == S_OK, "Failed to get media sink, hr %x.\n", hr);
+    ok(sink2 == sink, "Unexpected sink.\n");
+    IMFMediaSink_Release(sink2);
+
+    hr = IMFStreamSink_GetIdentifier(stream, &id);
+    ok(hr == S_OK, "Failed to get stream id, hr %#x.\n", hr);
+    ok(id == 0, "Unexpected id %#x.\n", id);
+
+    hr = IMFStreamSink_GetMediaTypeHandler(stream, &handler);
+todo_wine
+    ok(hr == S_OK, "Failed to get type handler, hr %#x.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IMFMediaTypeHandler_GetMediaTypeCount(handler, &count);
+        ok(hr == S_OK, "Failed to get media type count, hr %#x.\n", hr);
+        ok(count == 0, "Unexpected count %u.\n", count);
+
+        IMFMediaTypeHandler_Release(handler);
+    }
+
+    hr = IMFStreamSink_Flush(stream);
+todo_wine
+    ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
+
+    IMFMediaSink_Release(sink);
+    IMFStreamSink_Release(stream);
+
+    refcount = IMFActivate_Release(activate);
+    ok(!refcount, "Unexpected refcount %u.\n", refcount);
+
+    IMFMediaType_Release(media_type);
 }
 
 START_TEST(mf)
diff --git a/include/mferror.h b/include/mferror.h
index a0acf90f8b..4d3a530734 100644
--- a/include/mferror.h
+++ b/include/mferror.h
@@ -94,6 +94,20 @@
 #define MF_E_MEDIA_SOURCE_NO_STREAMS_SELECTED         _HRESULT_TYPEDEF_(0xc00d3e9c)
 #define MF_E_UNSUPPORTED_CHARACTERISTICS              _HRESULT_TYPEDEF_(0xc00d3e9e)
 
+#define MF_E_STREAMSINK_REMOVED                       _HRESULT_TYPEDEF_(0xc00d4a38)
+#define MF_E_STREAMSINKS_OUT_OF_SYNC                  _HRESULT_TYPEDEF_(0xc00d4a3a)
+#define MF_E_STREAMSINKS_FIXED                        _HRESULT_TYPEDEF_(0xc00d4a3b)
+#define MF_E_STREAMSINK_EXISTS                        _HRESULT_TYPEDEF_(0xc00d4a3c)
+#define MF_E_SAMPLEALLOCATOR_CANCELED                 _HRESULT_TYPEDEF_(0xc00d4a3d)
+#define MF_E_SAMPLEALLOCATOR_EMPTY                    _HRESULT_TYPEDEF_(0xc00d4a3e)
+#define MF_E_SINK_ALREADYSTOPPED                      _HRESULT_TYPEDEF_(0xc00d4a3f)
+#define MF_E_ASF_FILESINK_BITRATE_UNKNOWN             _HRESULT_TYPEDEF_(0xc00d4a40)
+#define MF_E_SINK_NO_STREAMS                          _HRESULT_TYPEDEF_(0xc00d4a41)
+#define MF_S_SINK_NOT_FINALIZED                       _HRESULT_TYPEDEF_(0x000d4a42)
+#define MF_E_METADATA_TOO_LONG                        _HRESULT_TYPEDEF_(0xc00d4a43)
+#define MF_E_SINK_NO_SAMPLES_PROCESSED                _HRESULT_TYPEDEF_(0xc00d4a44)
+#define MF_E_SINK_HEADERS_NOT_FOUND                   _HRESULT_TYPEDEF_(0xc00d4a45)
+
 #define MF_E_TOPO_INVALID_OPTIONAL_NODE               _HRESULT_TYPEDEF_(0xc00d520e)
 #define MF_E_TOPO_CANNOT_FIND_DECRYPTOR               _HRESULT_TYPEDEF_(0xc00d5211)
 #define MF_E_TOPO_CODEC_NOT_FOUND                     _HRESULT_TYPEDEF_(0xc00d5212)
diff --git a/include/mfidl.idl b/include/mfidl.idl
index c024e67378..a5882e98e5 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -83,6 +83,14 @@ typedef enum _MF_TOPOLOGY_RESOLUTION_STATUS_FLAGS
     MF_OPTIONAL_NODE_REJECTED_PROTECTED_PROCESS = 0x00000002,
 } MF_TOPOLOGY_RESOLUTION_STATUS_FLAGS;
 
+typedef enum _MFSTREAMSINK_MARKER_TYPE
+{
+    MFSTREAMSINK_MARKER_DEFAULT,
+    MFSTREAMSINK_MARKER_ENDOFSEGMENT,
+    MFSTREAMSINK_MARKER_TICK,
+    MFSTREAMSINK_MARKER_EVENT,
+} MFSTREAMSINK_MARKER_TYPE;
+
 [
     object,
     uuid(2eb1e945-18b8-4139-9b1a-d5d584818530),
@@ -646,6 +654,13 @@ interface IMFPresentationClock : IMFClock
     HRESULT Pause();
 }
 
+cpp_quote("#define MEDIASINK_FIXED_STREAMS                0x00000001")
+cpp_quote("#define MEDIASINK_CANNOT_MATCH_CLOCK           0x00000002")
+cpp_quote("#define MEDIASINK_RATELESS                     0x00000004")
+cpp_quote("#define MEDIASINK_CLOCK_REQUIRED               0x00000008")
+cpp_quote("#define MEDIASINK_CAN_PREROLL                  0x00000010")
+cpp_quote("#define MEDIASINK_REQUIRE_REFERENCE_MEDIATYPE  0x00000020")
+
 [
     object,
     uuid(6ef2a660-47c0-4666-b13d-cbb717f2fa2c)
@@ -666,6 +681,23 @@ interface IMFMediaSink : IUnknown
     HRESULT Shutdown();
 }
 
+[
+    object,
+    uuid(0a97b3cf-8e7c-4a3d-8f8c-0c843dc247fb),
+]
+interface IMFStreamSink : IMFMediaEventGenerator
+{
+    HRESULT GetMediaSink([out] IMFMediaSink **sink);
+    HRESULT GetIdentifier([out] DWORD *identifier);
+    HRESULT GetMediaTypeHandler([out] IMFMediaTypeHandler **handler);
+    HRESULT ProcessSample([in] IMFSample *sample);
+    HRESULT PlaceMarker(
+        [in] MFSTREAMSINK_MARKER_TYPE marker_type,
+        [in] const PROPVARIANT *marker_value,
+        [in] const PROPVARIANT *context_value);
+   HRESULT Flush();
+}
+
 typedef enum _MFSHUTDOWN_STATUS
 {
     MFSHUTDOWN_INITIATED,
-- 
2.20.1




More information about the wine-devel mailing list