From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfplat/main.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 1afec10405b..ebab4ed6f1f 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -4089,11 +4089,30 @@ static HRESULT WINAPI bytestream_EndRead(IMFByteStream *iface, IMFAsyncResult *r return bytestream_complete_io_request(stream, ASYNC_STREAM_OP_READ, result, byte_read); }
-static HRESULT WINAPI bytestream_file_Write(IMFByteStream *iface, const BYTE *data, ULONG count, ULONG *written) +static HRESULT WINAPI bytestream_file_Write(IMFByteStream *iface, const BYTE *data, ULONG size, ULONG *written) { - FIXME("%p, %p, %lu, %p\n", iface, data, count, written); + struct bytestream *stream = impl_from_IMFByteStream(iface); + LARGE_INTEGER position; + HRESULT hr = S_OK; + BOOL ret;
- return E_NOTIMPL; + TRACE("%p, %p, %lu, %p\n", iface, data, size, written); + + EnterCriticalSection(&stream->cs); + + position.QuadPart = stream->position; + if ((ret = SetFilePointerEx(stream->hfile, position, NULL, FILE_BEGIN))) + { + if ((ret = WriteFile(stream->hfile, data, size, written, NULL))) + stream->position += *written; + } + + if (!ret) + hr = HRESULT_FROM_WIN32(GetLastError()); + + LeaveCriticalSection(&stream->cs); + + return hr; }
static HRESULT WINAPI bytestream_BeginWrite(IMFByteStream *iface, const BYTE *data, ULONG size,
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- configure | 2 ++ configure.ac | 1 + dlls/mfsrcsnk/Makefile.in | 7 +++++++ dlls/mfsrcsnk/mfsrcsnk.spec | 1 + dlls/mfsrcsnk/wave.c | 33 +++++++++++++++++++++++++++++++++ include/mfidl.idl | 1 + 6 files changed, 45 insertions(+) create mode 100644 dlls/mfsrcsnk/Makefile.in create mode 100644 dlls/mfsrcsnk/mfsrcsnk.spec create mode 100644 dlls/mfsrcsnk/wave.c
diff --git a/configure b/configure index 51946122dae..41c60f76687 100755 --- a/configure +++ b/configure @@ -1170,6 +1170,7 @@ enable_mfmediaengine enable_mfplat enable_mfplay enable_mfreadwrite +enable_mfsrcsnk enable_mgmtapi enable_midimap enable_mlang @@ -21571,6 +21572,7 @@ wine_fn_config_makefile dlls/mfplay enable_mfplay wine_fn_config_makefile dlls/mfplay/tests enable_tests wine_fn_config_makefile dlls/mfreadwrite enable_mfreadwrite wine_fn_config_makefile dlls/mfreadwrite/tests enable_tests +wine_fn_config_makefile dlls/mfsrcsnk enable_mfsrcsnk wine_fn_config_makefile dlls/mgmtapi enable_mgmtapi wine_fn_config_makefile dlls/midimap enable_midimap wine_fn_config_makefile dlls/mlang enable_mlang diff --git a/configure.ac b/configure.ac index dd4abd39e32..44b85d5fe26 100644 --- a/configure.ac +++ b/configure.ac @@ -2738,6 +2738,7 @@ WINE_CONFIG_MAKEFILE(dlls/mfplay) WINE_CONFIG_MAKEFILE(dlls/mfplay/tests) WINE_CONFIG_MAKEFILE(dlls/mfreadwrite) WINE_CONFIG_MAKEFILE(dlls/mfreadwrite/tests) +WINE_CONFIG_MAKEFILE(dlls/mfsrcsnk) WINE_CONFIG_MAKEFILE(dlls/mgmtapi) WINE_CONFIG_MAKEFILE(dlls/midimap) WINE_CONFIG_MAKEFILE(dlls/mlang) diff --git a/dlls/mfsrcsnk/Makefile.in b/dlls/mfsrcsnk/Makefile.in new file mode 100644 index 00000000000..a110a5058f1 --- /dev/null +++ b/dlls/mfsrcsnk/Makefile.in @@ -0,0 +1,7 @@ +MODULE = mfsrcsnk.dll +IMPORTLIB = mfsrcsnk + +EXTRADLLFLAGS = -Wb,--prefer-native + +C_SRCS = \ + wave.c diff --git a/dlls/mfsrcsnk/mfsrcsnk.spec b/dlls/mfsrcsnk/mfsrcsnk.spec new file mode 100644 index 00000000000..9acfef27ae2 --- /dev/null +++ b/dlls/mfsrcsnk/mfsrcsnk.spec @@ -0,0 +1 @@ +@ stdcall MFCreateWAVEMediaSink(ptr ptr ptr) diff --git a/dlls/mfsrcsnk/wave.c b/dlls/mfsrcsnk/wave.c new file mode 100644 index 00000000000..d9d51ac62e2 --- /dev/null +++ b/dlls/mfsrcsnk/wave.c @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "mfidl.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +/*********************************************************************** + * MFCreateWAVEMediaSink (mfplat.@) + */ +HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *bytestream, IMFMediaType *media_type, + IMFMediaSink **sink) +{ + FIXME("%p, %p, %p.\n", bytestream, media_type, sink); + + return E_NOTIMPL; +} diff --git a/include/mfidl.idl b/include/mfidl.idl index 72e96d1c598..c42fba77fbe 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -719,6 +719,7 @@ cpp_quote("HRESULT WINAPI MFCreateTopology(IMFTopology **topology);") cpp_quote("HRESULT WINAPI MFCreateTopologyNode(MF_TOPOLOGY_TYPE node_type, IMFTopologyNode **node);") cpp_quote("HRESULT WINAPI MFCreateTopoLoader(IMFTopoLoader **loader);") cpp_quote("HRESULT WINAPI MFCreateVideoRendererActivate(HWND hwnd, IMFActivate **activate);") +cpp_quote("HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *stream, IMFMediaType *media_type, IMFMediaSink **sink);") cpp_quote("HRESULT WINAPI MFEnumDeviceSources(IMFAttributes *attributes, IMFActivate ***sources, UINT32 *count);") cpp_quote("HRESULT WINAPI MFGetSupportedMimeTypes(PROPVARIANT *array);") cpp_quote("HRESULT WINAPI MFGetSupportedSchemes(PROPVARIANT *array);")
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- configure | 1 + configure.ac | 1 + dlls/mfsrcsnk/Makefile.in | 1 + dlls/mfsrcsnk/tests/Makefile.in | 5 + dlls/mfsrcsnk/tests/mfsrcsnk.c | 163 ++++++++++ dlls/mfsrcsnk/wave.c | 539 +++++++++++++++++++++++++++++++- 6 files changed, 707 insertions(+), 3 deletions(-) create mode 100644 dlls/mfsrcsnk/tests/Makefile.in create mode 100644 dlls/mfsrcsnk/tests/mfsrcsnk.c
diff --git a/configure b/configure index 41c60f76687..b37a2989db3 100755 --- a/configure +++ b/configure @@ -21573,6 +21573,7 @@ wine_fn_config_makefile dlls/mfplay/tests enable_tests wine_fn_config_makefile dlls/mfreadwrite enable_mfreadwrite wine_fn_config_makefile dlls/mfreadwrite/tests enable_tests wine_fn_config_makefile dlls/mfsrcsnk enable_mfsrcsnk +wine_fn_config_makefile dlls/mfsrcsnk/tests enable_tests wine_fn_config_makefile dlls/mgmtapi enable_mgmtapi wine_fn_config_makefile dlls/midimap enable_midimap wine_fn_config_makefile dlls/mlang enable_mlang diff --git a/configure.ac b/configure.ac index 44b85d5fe26..30a123fcb54 100644 --- a/configure.ac +++ b/configure.ac @@ -2739,6 +2739,7 @@ WINE_CONFIG_MAKEFILE(dlls/mfplay/tests) WINE_CONFIG_MAKEFILE(dlls/mfreadwrite) WINE_CONFIG_MAKEFILE(dlls/mfreadwrite/tests) WINE_CONFIG_MAKEFILE(dlls/mfsrcsnk) +WINE_CONFIG_MAKEFILE(dlls/mfsrcsnk/tests) WINE_CONFIG_MAKEFILE(dlls/mgmtapi) WINE_CONFIG_MAKEFILE(dlls/midimap) WINE_CONFIG_MAKEFILE(dlls/mlang) diff --git a/dlls/mfsrcsnk/Makefile.in b/dlls/mfsrcsnk/Makefile.in index a110a5058f1..8482d8f3556 100644 --- a/dlls/mfsrcsnk/Makefile.in +++ b/dlls/mfsrcsnk/Makefile.in @@ -1,5 +1,6 @@ MODULE = mfsrcsnk.dll IMPORTLIB = mfsrcsnk +IMPORTS = mfplat mfuuid uuid
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/mfsrcsnk/tests/Makefile.in b/dlls/mfsrcsnk/tests/Makefile.in new file mode 100644 index 00000000000..59ee4830573 --- /dev/null +++ b/dlls/mfsrcsnk/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = mfsrcsnk.dll +IMPORTS = ole32 mfsrcsnk mfplat uuid mfuuid + +C_SRCS = \ + mfsrcsnk.c diff --git a/dlls/mfsrcsnk/tests/mfsrcsnk.c b/dlls/mfsrcsnk/tests/mfsrcsnk.c new file mode 100644 index 00000000000..df3fcfbee38 --- /dev/null +++ b/dlls/mfsrcsnk/tests/mfsrcsnk.c @@ -0,0 +1,163 @@ +/* + * Copyright 2022 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <string.h> + +#define COBJMACROS + +#include "windef.h" + +#include "mfapi.h" +#include "mfidl.h" +#include "mferror.h" + +#include "wine/test.h" + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + IUnknown *iface = iface_ptr; + HRESULT hr, expected_hr; + IUnknown *unk; + + expected_hr = supported ? S_OK : E_NOINTERFACE; + + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); +} + +static void test_wave_sink(void) +{ + IMFStreamSink *stream_sink; + IMFMediaSink *sink, *sink2; + IMFByteStream *bytestream; + IMFMediaType *media_type; + DWORD id, count, flags; + HRESULT hr; + + hr = MFCreateWAVEMediaSink(NULL, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateWAVEMediaSink(NULL, NULL, &sink); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMediaType(&media_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, 2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, 8); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateTempFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, 0, &bytestream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateWAVEMediaSink(bytestream, NULL, &sink); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateWAVEMediaSink(bytestream, media_type, &sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Sink tests */ + hr = IMFMediaSink_GetCharacteristics(sink, &flags); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(flags == (MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS), "Unexpected flags %#lx.\n", flags); + + hr = IMFMediaSink_GetStreamSinkCount(sink, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSink_GetStreamSinkCount(sink, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 1, "Unexpected count %lu.\n", count); + + hr = IMFMediaSink_AddStreamSink(sink, 123, media_type, &stream_sink); + ok(hr == MF_E_STREAMSINKS_FIXED, "Unexpected hr %#lx.\n", hr); + + check_interface(sink, &IID_IMFMediaEventGenerator, TRUE); + check_interface(sink, &IID_IMFFinalizableMediaSink, TRUE); + + /* Stream tests */ + hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFStreamSink_GetIdentifier(stream_sink, &id); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(id == 1, "Unexpected id %#lx.\n", id); + IMFStreamSink_Release(stream_sink); + + hr = IMFMediaSink_GetStreamSinkById(sink, 0, &stream_sink); + ok(hr == MF_E_INVALIDSTREAMNUMBER, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkById(sink, id, &stream_sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_GetMediaSink(stream_sink, &sink2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMFMediaSink_Release(sink2); + + /* Shutdown state */ + hr = IMFMediaSink_Shutdown(sink); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSink_Shutdown(sink); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_GetMediaSink(stream_sink, &sink2); + ok(hr == MF_E_STREAMSINK_REMOVED, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_GetIdentifier(stream_sink, &id); + ok(hr == MF_E_STREAMSINK_REMOVED, "Unexpected hr %#lx.\n", hr); + + IMFStreamSink_Release(stream_sink); + + hr = IMFMediaSink_AddStreamSink(sink, 123, media_type, &stream_sink); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream_sink); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetStreamSinkById(sink, 0, &stream_sink); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaSink_GetCharacteristics(sink, &flags); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + IMFMediaSink_Release(sink); + + IMFMediaType_Release(media_type); + IMFByteStream_Release(bytestream); +} + +START_TEST(mfsrcsnk) +{ + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + test_wave_sink(); + + hr = MFShutdown(); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} diff --git a/dlls/mfsrcsnk/wave.c b/dlls/mfsrcsnk/wave.c index d9d51ac62e2..9f271375e1e 100644 --- a/dlls/mfsrcsnk/wave.c +++ b/dlls/mfsrcsnk/wave.c @@ -16,18 +16,551 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + +#include "mfapi.h" #include "mfidl.h" +#include "mferror.h" + #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+enum wave_sink_flags +{ + SINK_SHUT_DOWN = 0x1, +}; + +struct wave_sink +{ + IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFStreamSink IMFStreamSink_iface; + LONG refcount; + + IMFMediaEventQueue *event_queue; + IMFMediaEventQueue *stream_event_queue; + + IMFByteStream *bytestream; + + unsigned int flags; + CRITICAL_SECTION cs; +}; + +static struct wave_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface) +{ + return CONTAINING_RECORD(iface, struct wave_sink, IMFFinalizableMediaSink_iface); +} + +static struct wave_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct wave_sink, IMFMediaEventGenerator_iface); +} + +static struct wave_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) +{ + return CONTAINING_RECORD(iface, struct wave_sink, IMFStreamSink_iface); +} + +static HRESULT WINAPI wave_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFFinalizableMediaSink) || + IsEqualIID(riid, &IID_IMFMediaSink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaEventGenerator)) + { + *obj = &sink->IMFMediaEventGenerator_iface; + } + else + { + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*obj); + + return S_OK; +} + +static ULONG WINAPI wave_sink_AddRef(IMFFinalizableMediaSink *iface) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedIncrement(&sink->refcount); + TRACE("%p, refcount %lu.\n", iface, refcount); + return refcount; +} + +static ULONG WINAPI wave_sink_Release(IMFFinalizableMediaSink *iface) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + ULONG refcount = InterlockedDecrement(&sink->refcount); + + TRACE("%p, refcount %lu.\n", iface, refcount); + + if (!refcount) + { + if (sink->event_queue) + IMFMediaEventQueue_Release(sink->event_queue); + if (sink->stream_event_queue) + IMFMediaEventQueue_Release(sink->stream_event_queue); + IMFByteStream_Release(sink->bytestream); + DeleteCriticalSection(&sink->cs); + free(sink); + } + + return refcount; +} + +static HRESULT WINAPI wave_sink_GetCharacteristics(IMFFinalizableMediaSink *iface, DWORD *flags) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("%p, %p.\n", iface, flags); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_SHUTDOWN; + + *flags = MEDIASINK_FIXED_STREAMS | MEDIASINK_RATELESS; + + return S_OK; +} + +static HRESULT WINAPI wave_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFMediaType *media_type, IMFStreamSink **stream_sink) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("%p, %#lx, %p, %p.\n", iface, stream_sink_id, media_type, stream_sink); + + return sink->flags & SINK_SHUT_DOWN ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; +} + +static HRESULT WINAPI wave_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("%p, %#lx.\n", iface, stream_sink_id); + + return sink->flags & SINK_SHUT_DOWN ? MF_E_SHUTDOWN : MF_E_STREAMSINKS_FIXED; +} + +static HRESULT WINAPI wave_sink_GetStreamSinkCount(IMFFinalizableMediaSink *iface, DWORD *count) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + + TRACE("%p, %p.\n", iface, count); + + if (!count) + return E_POINTER; + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_SHUTDOWN; + + *count = 1; + + return S_OK; +} + +static HRESULT WINAPI wave_sink_GetStreamSinkByIndex(IMFFinalizableMediaSink *iface, DWORD index, + IMFStreamSink **stream) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %lu, %p.\n", iface, index, stream); + + EnterCriticalSection(&sink->cs); + + if (sink->flags & SINK_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (index) + hr = MF_E_INVALIDINDEX; + else + { + *stream = &sink->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI wave_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface, DWORD stream_sink_id, + IMFStreamSink **stream) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p, %#lx, %p.\n", iface, stream_sink_id, stream); + + EnterCriticalSection(&sink->cs); + + if (sink->flags & SINK_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (stream_sink_id != 1) + hr = MF_E_INVALIDSTREAMNUMBER; + else + { + *stream = &sink->IMFStreamSink_iface; + IMFStreamSink_AddRef(*stream); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI wave_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) +{ + FIXME("%p, %p.\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI wave_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) +{ + FIXME("%p, %p.\n", iface, clock); + + return E_NOTIMPL; +} + +static HRESULT WINAPI wave_sink_Shutdown(IMFFinalizableMediaSink *iface) +{ + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK; + + TRACE("%p.\n", iface); + + EnterCriticalSection(&sink->cs); + + if (sink->flags & SINK_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + { + sink->flags |= SINK_SHUT_DOWN; + IMFMediaEventQueue_Shutdown(sink->event_queue); + IMFMediaEventQueue_Shutdown(sink->stream_event_queue); + } + + LeaveCriticalSection(&sink->cs); + + return hr; +} + +static HRESULT WINAPI wave_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + FIXME("%p, %p, %p.\n", iface, callback, state); + + return E_NOTIMPL; +} + +static HRESULT WINAPI wave_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result) +{ + FIXME("%p, %p.\n", iface, result); + + return E_NOTIMPL; +} + +static const IMFFinalizableMediaSinkVtbl wave_sink_vtbl = +{ + wave_sink_QueryInterface, + wave_sink_AddRef, + wave_sink_Release, + wave_sink_GetCharacteristics, + wave_sink_AddStreamSink, + wave_sink_RemoveStreamSink, + wave_sink_GetStreamSinkCount, + wave_sink_GetStreamSinkByIndex, + wave_sink_GetStreamSinkById, + wave_sink_SetPresentationClock, + wave_sink_GetPresentationClock, + wave_sink_Shutdown, + wave_sink_BeginFinalize, + wave_sink_EndFinalize, +}; + +static HRESULT WINAPI wave_sink_events_QueryInterface(IMFMediaEventGenerator *iface, REFIID riid, void **obj) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_QueryInterface(&sink->IMFFinalizableMediaSink_iface, riid, obj); +} + +static ULONG WINAPI wave_sink_events_AddRef(IMFMediaEventGenerator *iface) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_AddRef(&sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI wave_sink_events_Release(IMFMediaEventGenerator *iface) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + return IMFFinalizableMediaSink_Release(&sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI wave_sink_events_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(sink->event_queue, flags, event); +} + +static HRESULT WINAPI wave_sink_events_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(sink->event_queue, callback, state); +} + +static HRESULT WINAPI wave_sink_events_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(sink->event_queue, result, event); +} + +static HRESULT WINAPI wave_sink_events_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct wave_sink *sink = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(sink->event_queue, event_type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl wave_sink_events_vtbl = +{ + wave_sink_events_QueryInterface, + wave_sink_events_AddRef, + wave_sink_events_Release, + wave_sink_events_GetEvent, + wave_sink_events_BeginGetEvent, + wave_sink_events_EndGetEvent, + wave_sink_events_QueueEvent, +}; + +static HRESULT WINAPI wave_stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFStreamSink) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = &sink->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 wave_stream_sink_AddRef(IMFStreamSink *iface) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + return IMFFinalizableMediaSink_AddRef(&sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI wave_stream_sink_Release(IMFStreamSink *iface) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + return IMFFinalizableMediaSink_Release(&sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI wave_stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_GetEvent(sink->stream_event_queue, flags, event); +} + +static HRESULT WINAPI wave_stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_BeginGetEvent(sink->stream_event_queue, callback, state); +} + +static HRESULT WINAPI wave_stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result, + IMFMediaEvent **event) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_EndGetEvent(sink->stream_event_queue, result, event); +} + +static HRESULT WINAPI wave_stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type, + REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %lu, %s, %#lx, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + return IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI wave_stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **ret) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, ret); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + *ret = (IMFMediaSink *)&sink->IMFFinalizableMediaSink_iface; + IMFMediaSink_AddRef(*ret); + + return S_OK; +} + +static HRESULT WINAPI wave_stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *identifier) +{ + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + + TRACE("%p, %p.\n", iface, identifier); + + if (sink->flags & SINK_SHUT_DOWN) + return MF_E_STREAMSINK_REMOVED; + + *identifier = 1; + + return S_OK; +} + +static HRESULT WINAPI wave_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler) +{ + FIXME("%p, %p.\n", iface, handler); + + return E_NOTIMPL; +} + +static HRESULT WINAPI wave_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) +{ + FIXME("%p, %p.\n", iface, sample); + + return E_NOTIMPL; +} + +static HRESULT WINAPI wave_stream_sink_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 wave_stream_sink_Flush(IMFStreamSink *iface) +{ + FIXME("%p.\n", iface); + + return E_NOTIMPL; +} + +static const IMFStreamSinkVtbl wave_stream_sink_vtbl = +{ + wave_stream_sink_QueryInterface, + wave_stream_sink_AddRef, + wave_stream_sink_Release, + wave_stream_sink_GetEvent, + wave_stream_sink_BeginGetEvent, + wave_stream_sink_EndGetEvent, + wave_stream_sink_QueueEvent, + wave_stream_sink_GetMediaSink, + wave_stream_sink_GetIdentifier, + wave_stream_sink_GetMediaTypeHandler, + wave_stream_sink_ProcessSample, + wave_stream_sink_PlaceMarker, + wave_stream_sink_Flush, +}; + /*********************************************************************** - * MFCreateWAVEMediaSink (mfplat.@) + * MFCreateWAVEMediaSink (mfsrcsnk.@) */ HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *bytestream, IMFMediaType *media_type, IMFMediaSink **sink) { - FIXME("%p, %p, %p.\n", bytestream, media_type, sink); + struct wave_sink *object; + DWORD flags = 0; + HRESULT hr;
- return E_NOTIMPL; + TRACE("%p, %p, %p.\n", bytestream, media_type, sink); + + if (!bytestream || !media_type || !sink) + return E_POINTER; + + if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &flags))) return hr; + if (!(flags & MFBYTESTREAM_IS_WRITABLE)) return E_INVALIDARG; + + /* FIXME: do basic media type validation */ + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFFinalizableMediaSink_iface.lpVtbl = &wave_sink_vtbl; + object->IMFMediaEventGenerator_iface.lpVtbl = &wave_sink_events_vtbl; + object->IMFStreamSink_iface.lpVtbl = &wave_stream_sink_vtbl; + object->refcount = 1; + IMFByteStream_AddRef((object->bytestream = bytestream)); + InitializeCriticalSection(&object->cs); + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto failed; + + if (FAILED(hr = MFCreateEventQueue(&object->stream_event_queue))) + goto failed; + + *sink = (IMFMediaSink *)&object->IMFFinalizableMediaSink_iface; + + return S_OK; + +failed: + + IMFFinalizableMediaSink_Release(&object->IMFFinalizableMediaSink_iface); + + return hr; }
From: Nikolay Sivov nsivov@codeweavers.com
Don't bother with state handling for now, it's possible it's ignored entirely.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfsrcsnk/tests/Makefile.in | 2 +- dlls/mfsrcsnk/tests/mfsrcsnk.c | 14 +++ dlls/mfsrcsnk/wave.c | 162 +++++++++++++++++++++++++++++++- 3 files changed, 173 insertions(+), 5 deletions(-)
diff --git a/dlls/mfsrcsnk/tests/Makefile.in b/dlls/mfsrcsnk/tests/Makefile.in index 59ee4830573..32e5637db4f 100644 --- a/dlls/mfsrcsnk/tests/Makefile.in +++ b/dlls/mfsrcsnk/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mfsrcsnk.dll -IMPORTS = ole32 mfsrcsnk mfplat uuid mfuuid +IMPORTS = ole32 mfsrcsnk mfplat mf uuid mfuuid
C_SRCS = \ mfsrcsnk.c diff --git a/dlls/mfsrcsnk/tests/mfsrcsnk.c b/dlls/mfsrcsnk/tests/mfsrcsnk.c index df3fcfbee38..e3d5b100b09 100644 --- a/dlls/mfsrcsnk/tests/mfsrcsnk.c +++ b/dlls/mfsrcsnk/tests/mfsrcsnk.c @@ -46,6 +46,7 @@ static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOO
static void test_wave_sink(void) { + IMFPresentationClock *clock; IMFStreamSink *stream_sink; IMFMediaSink *sink, *sink2; IMFByteStream *bytestream; @@ -101,6 +102,19 @@ static void test_wave_sink(void)
check_interface(sink, &IID_IMFMediaEventGenerator, TRUE); check_interface(sink, &IID_IMFFinalizableMediaSink, TRUE); + check_interface(sink, &IID_IMFClockStateSink, TRUE); + + /* Clock */ + hr = MFCreatePresentationClock(&clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSink_SetPresentationClock(sink, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaSink_SetPresentationClock(sink, clock); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFPresentationClock_Release(clock);
/* Stream tests */ hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream_sink); diff --git a/dlls/mfsrcsnk/wave.c b/dlls/mfsrcsnk/wave.c index 9f271375e1e..b9951b3e2b6 100644 --- a/dlls/mfsrcsnk/wave.c +++ b/dlls/mfsrcsnk/wave.c @@ -26,6 +26,27 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+static inline const char *debugstr_time(LONGLONG time) +{ + ULONGLONG abstime = time >= 0 ? time : -time; + unsigned int i = 0, j = 0; + char buffer[23], rev[23]; + + while (abstime || i <= 8) + { + buffer[i++] = '0' + (abstime % 10); + abstime /= 10; + if (i == 7) buffer[i++] = '.'; + } + if (time < 0) buffer[i++] = '-'; + + while (i--) rev[j++] = buffer[i]; + while (rev[j-1] == '0' && rev[j-2] != '.') --j; + rev[j] = 0; + + return wine_dbg_sprintf("%s", rev); +} + enum wave_sink_flags { SINK_SHUT_DOWN = 0x1, @@ -35,11 +56,13 @@ struct wave_sink { IMFFinalizableMediaSink IMFFinalizableMediaSink_iface; IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFClockStateSink IMFClockStateSink_iface; IMFStreamSink IMFStreamSink_iface; LONG refcount;
IMFMediaEventQueue *event_queue; IMFMediaEventQueue *stream_event_queue; + IMFPresentationClock *clock;
IMFByteStream *bytestream;
@@ -62,6 +85,11 @@ static struct wave_sink *impl_from_IMFStreamSink(IMFStreamSink *iface) return CONTAINING_RECORD(iface, struct wave_sink, IMFStreamSink_iface); }
+static struct wave_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface) +{ + return CONTAINING_RECORD(iface, struct wave_sink, IMFClockStateSink_iface); +} + static HRESULT WINAPI wave_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj) { struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); @@ -78,6 +106,10 @@ static HRESULT WINAPI wave_sink_QueryInterface(IMFFinalizableMediaSink *iface, R { *obj = &sink->IMFMediaEventGenerator_iface; } + else if (IsEqualIID(riid, &IID_IMFClockStateSink)) + { + *obj = &sink->IMFClockStateSink_iface; + } else { WARN("Unsupported %s.\n", debugstr_guid(riid)); @@ -219,18 +251,65 @@ static HRESULT WINAPI wave_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface return hr; }
+static void wave_sink_set_presentation_clock(struct wave_sink *sink, IMFPresentationClock *clock) +{ + if (sink->clock) + { + IMFPresentationClock_RemoveClockStateSink(sink->clock, &sink->IMFClockStateSink_iface); + IMFPresentationClock_Release(sink->clock); + } + sink->clock = clock; + if (sink->clock) + { + IMFPresentationClock_AddRef(sink->clock); + IMFPresentationClock_AddClockStateSink(sink->clock, &sink->IMFClockStateSink_iface); + } +} + static HRESULT WINAPI wave_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) { - FIXME("%p, %p.\n", iface, clock); + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, clock); + + EnterCriticalSection(&sink->cs); + + if (sink->flags & SINK_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else + wave_sink_set_presentation_clock(sink, clock); + + LeaveCriticalSection(&sink->cs); + + return hr; }
static HRESULT WINAPI wave_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) { - FIXME("%p, %p.\n", iface, clock); + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, clock); + + if (!clock) + return E_POINTER; + + EnterCriticalSection(&sink->cs); + + if (sink->flags & SINK_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else if (sink->clock) + { + *clock = sink->clock; + IMFPresentationClock_AddRef(*clock); + } + else + hr = MF_E_NO_CLOCK; + + LeaveCriticalSection(&sink->cs); + + return hr; }
static HRESULT WINAPI wave_sink_Shutdown(IMFFinalizableMediaSink *iface) @@ -249,6 +328,7 @@ static HRESULT WINAPI wave_sink_Shutdown(IMFFinalizableMediaSink *iface) sink->flags |= SINK_SHUT_DOWN; IMFMediaEventQueue_Shutdown(sink->event_queue); IMFMediaEventQueue_Shutdown(sink->stream_event_queue); + wave_sink_set_presentation_clock(sink, NULL); }
LeaveCriticalSection(&sink->cs); @@ -518,6 +598,79 @@ static const IMFStreamSinkVtbl wave_stream_sink_vtbl = wave_stream_sink_Flush, };
+static HRESULT WINAPI wave_sink_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_QueryInterface(&sink->IMFFinalizableMediaSink_iface, riid, obj); +} + +static ULONG WINAPI wave_sink_clock_sink_AddRef(IMFClockStateSink *iface) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_AddRef(&sink->IMFFinalizableMediaSink_iface); +} + +static ULONG WINAPI wave_sink_clock_sink_Release(IMFClockStateSink *iface) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + return IMFFinalizableMediaSink_Release(&sink->IMFFinalizableMediaSink_iface); +} + +static HRESULT WINAPI wave_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s, %s.\n", iface, debugstr_time(systime), debugstr_time(offset)); + + return IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); +} + +static HRESULT WINAPI wave_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + return IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, MEStreamSinkStopped, &GUID_NULL, S_OK, NULL); +} + +static HRESULT WINAPI wave_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + return IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, MEStreamSinkPaused, &GUID_NULL, S_OK, NULL); +} + +static HRESULT WINAPI wave_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime) +{ + struct wave_sink *sink = impl_from_IMFClockStateSink(iface); + + TRACE("%p, %s.\n", iface, debugstr_time(systime)); + + return IMFMediaEventQueue_QueueEventParamVar(sink->stream_event_queue, MEStreamSinkStarted, &GUID_NULL, S_OK, NULL); +} + +static HRESULT WINAPI wave_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate) +{ + FIXME("%p, %s, %f.\n", iface, debugstr_time(systime), rate); + + return E_NOTIMPL; +} + +static const IMFClockStateSinkVtbl wave_sink_clock_sink_vtbl = +{ + wave_sink_clock_sink_QueryInterface, + wave_sink_clock_sink_AddRef, + wave_sink_clock_sink_Release, + wave_sink_clock_sink_OnClockStart, + wave_sink_clock_sink_OnClockStop, + wave_sink_clock_sink_OnClockPause, + wave_sink_clock_sink_OnClockRestart, + wave_sink_clock_sink_OnClockSetRate, +}; + /*********************************************************************** * MFCreateWAVEMediaSink (mfsrcsnk.@) */ @@ -544,6 +697,7 @@ HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *bytestream, IMFMediaType *me object->IMFFinalizableMediaSink_iface.lpVtbl = &wave_sink_vtbl; object->IMFMediaEventGenerator_iface.lpVtbl = &wave_sink_events_vtbl; object->IMFStreamSink_iface.lpVtbl = &wave_stream_sink_vtbl; + object->IMFClockStateSink_iface.lpVtbl = &wave_sink_clock_sink_vtbl; object->refcount = 1; IMFByteStream_AddRef((object->bytestream = bytestream)); InitializeCriticalSection(&object->cs);
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfsrcsnk/Makefile.in | 2 +- dlls/mfsrcsnk/wave.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/dlls/mfsrcsnk/Makefile.in b/dlls/mfsrcsnk/Makefile.in index 8482d8f3556..a63ebaf60eb 100644 --- a/dlls/mfsrcsnk/Makefile.in +++ b/dlls/mfsrcsnk/Makefile.in @@ -1,6 +1,6 @@ MODULE = mfsrcsnk.dll IMPORTLIB = mfsrcsnk -IMPORTS = mfplat mfuuid uuid +IMPORTS = ole32 mfplat mfuuid uuid
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/mfsrcsnk/wave.c b/dlls/mfsrcsnk/wave.c index b9951b3e2b6..583eefb98b0 100644 --- a/dlls/mfsrcsnk/wave.c +++ b/dlls/mfsrcsnk/wave.c @@ -64,6 +64,7 @@ struct wave_sink IMFMediaEventQueue *stream_event_queue; IMFPresentationClock *clock;
+ WAVEFORMATEX *fmt; IMFByteStream *bytestream;
unsigned int flags; @@ -144,6 +145,7 @@ static ULONG WINAPI wave_sink_Release(IMFFinalizableMediaSink *iface) if (sink->stream_event_queue) IMFMediaEventQueue_Release(sink->stream_event_queue); IMFByteStream_Release(sink->bytestream); + CoTaskMemFree(sink->fmt); DeleteCriticalSection(&sink->cs); free(sink); } @@ -679,6 +681,7 @@ HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *bytestream, IMFMediaType *me { struct wave_sink *object; DWORD flags = 0; + UINT32 size; HRESULT hr;
TRACE("%p, %p, %p.\n", bytestream, media_type, sink); @@ -689,11 +692,17 @@ HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *bytestream, IMFMediaType *me if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &flags))) return hr; if (!(flags & MFBYTESTREAM_IS_WRITABLE)) return E_INVALIDARG;
- /* FIXME: do basic media type validation */ - if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY;
+ /* FIXME: do basic media type validation */ + + if (FAILED(hr = MFCreateWaveFormatExFromMFMediaType(media_type, &object->fmt, &size, 0))) + { + WARN("Failed to produce WAVEFORMATEX representation, hr %#lx.\n", hr); + goto failed; + } + object->IMFFinalizableMediaSink_iface.lpVtbl = &wave_sink_vtbl; object->IMFMediaEventGenerator_iface.lpVtbl = &wave_sink_events_vtbl; object->IMFStreamSink_iface.lpVtbl = &wave_stream_sink_vtbl;
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfsrcsnk/wave.c | 174 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 168 insertions(+), 6 deletions(-)
diff --git a/dlls/mfsrcsnk/wave.c b/dlls/mfsrcsnk/wave.c index 583eefb98b0..ce57865d093 100644 --- a/dlls/mfsrcsnk/wave.c +++ b/dlls/mfsrcsnk/wave.c @@ -50,6 +50,9 @@ static inline const char *debugstr_time(LONGLONG time) enum wave_sink_flags { SINK_SHUT_DOWN = 0x1, + SINK_HEADER_WRITTEN = 0x2, + SINK_DATA_CHUNK_STARTED = 0x4, + SINK_DATA_FINALIZED = 0x8, };
struct wave_sink @@ -66,6 +69,10 @@ struct wave_sink
WAVEFORMATEX *fmt; IMFByteStream *bytestream; + QWORD data_size_offset; + QWORD riff_size_offset; + DWORD data_length; + DWORD full_length;
unsigned int flags; CRITICAL_SECTION cs; @@ -338,18 +345,133 @@ static HRESULT WINAPI wave_sink_Shutdown(IMFFinalizableMediaSink *iface) return hr; }
+static void wave_sink_write_raw(struct wave_sink *sink, const void *data, DWORD length, HRESULT *hr) +{ + DWORD written_length; + + if (FAILED(*hr)) return; + if (SUCCEEDED(*hr = IMFByteStream_Write(sink->bytestream, data, length, &written_length))) + sink->full_length += length; +} + +static void wave_sink_write_pad(struct wave_sink *sink, DWORD size, HRESULT *hr) +{ + DWORD i, len = size / 4, zero = 0; + + for (i = 0; i < len; ++i) + wave_sink_write_raw(sink, &zero, 4, hr); + if ((len = size % 4)) + wave_sink_write_raw(sink, &zero, len, hr); +} + +static void wave_sink_write_junk(struct wave_sink *sink, DWORD size, HRESULT *hr) +{ + wave_sink_write_raw(sink, "JUNK", 4, hr); + wave_sink_write_raw(sink, &size, 4, hr); + wave_sink_write_pad(sink, size, hr); +} + +static HRESULT wave_sink_write_header(struct wave_sink *sink) +{ + HRESULT hr = S_OK; + DWORD size = 0; + + wave_sink_write_raw(sink, "RIFF", 4, &hr); + if (SUCCEEDED(hr)) + hr = IMFByteStream_GetCurrentPosition(sink->bytestream, &sink->riff_size_offset); + wave_sink_write_raw(sink, &size, sizeof(size), &hr); + wave_sink_write_raw(sink, "WAVE", 4, &hr); + wave_sink_write_junk(sink, 28, &hr); + + /* Format chunk */ + wave_sink_write_raw(sink, "fmt ", 4, &hr); + size = sizeof(*sink->fmt); + wave_sink_write_raw(sink, &size, sizeof(size), &hr); + wave_sink_write_raw(sink, sink->fmt, size, &hr); + + sink->flags |= SINK_HEADER_WRITTEN; + + return hr; +} + +static HRESULT wave_sink_start_data_chunk(struct wave_sink *sink) +{ + HRESULT hr = S_OK; + + wave_sink_write_raw(sink, "data", 4, &hr); + if (SUCCEEDED(hr)) + hr = IMFByteStream_GetCurrentPosition(sink->bytestream, &sink->data_size_offset); + wave_sink_write_pad(sink, 4, &hr); + sink->flags |= SINK_DATA_CHUNK_STARTED; + + return hr; +} + +static HRESULT wave_sink_write_data(struct wave_sink *sink, const BYTE *data, DWORD length) +{ + HRESULT hr = S_OK; + + wave_sink_write_raw(sink, data, length, &hr); + if (SUCCEEDED(hr)) + sink->data_length += length; + + return hr; +} + +static void wave_sink_write_at(struct wave_sink *sink, const void *data, DWORD length, QWORD offset, HRESULT *hr) +{ + QWORD position; + + if (FAILED(*hr)) return; + + if (FAILED(*hr = IMFByteStream_GetCurrentPosition(sink->bytestream, &position))) return; + if (FAILED(*hr = IMFByteStream_SetCurrentPosition(sink->bytestream, offset))) return; + wave_sink_write_raw(sink, data, length, hr); + IMFByteStream_SetCurrentPosition(sink->bytestream, position); +} + static HRESULT WINAPI wave_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state) { - FIXME("%p, %p, %p.\n", iface, callback, state); + struct wave_sink *sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK, status; + IMFAsyncResult *result; + DWORD size;
- return E_NOTIMPL; + TRACE("%p, %p, %p.\n", iface, callback, state); + + EnterCriticalSection(&sink->cs); + + if (!(sink->flags & SINK_DATA_FINALIZED)) + { + size = sink->full_length - 8 /* RIFF chunk header size */; + wave_sink_write_at(sink, &size, 4, sink->riff_size_offset, &hr); + wave_sink_write_at(sink, &sink->data_length, 4, sink->data_size_offset, &hr); + sink->flags |= SINK_DATA_FINALIZED; + status = hr; + } + else + status = E_INVALIDARG; + + if (callback) + { + if (SUCCEEDED(hr = MFCreateAsyncResult(NULL, callback, state, &result))) + { + IMFAsyncResult_SetStatus(result, status); + hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_STANDARD, result); + IMFAsyncResult_Release(result); + } + } + + LeaveCriticalSection(&sink->cs); + + return hr; }
static HRESULT WINAPI wave_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result) { - FIXME("%p, %p.\n", iface, result); + TRACE("%p, %p.\n", iface, result);
- return E_NOTIMPL; + return result ? IMFAsyncResult_GetStatus(result) : E_INVALIDARG; }
static const IMFFinalizableMediaSinkVtbl wave_sink_vtbl = @@ -563,9 +685,45 @@ static HRESULT WINAPI wave_stream_sink_GetMediaTypeHandler(IMFStreamSink *iface,
static HRESULT WINAPI wave_stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample) { - FIXME("%p, %p.\n", iface, sample); + struct wave_sink *sink = impl_from_IMFStreamSink(iface); + DWORD max_length, length; + IMFMediaBuffer *buffer; + HRESULT hr = S_OK; + BYTE *data;
- return E_NOTIMPL; + TRACE("%p, %p.\n", iface, sample); + + EnterCriticalSection(&sink->cs); + + if (sink->flags & SINK_SHUT_DOWN) + hr = MF_E_STREAMSINK_REMOVED; + else + { + if (!(sink->flags & SINK_HEADER_WRITTEN)) + hr = wave_sink_write_header(sink); + + if (SUCCEEDED(hr) && !(sink->flags & SINK_DATA_CHUNK_STARTED)) + hr = wave_sink_start_data_chunk(sink); + + if (SUCCEEDED(hr)) + hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer); + + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = IMFMediaBuffer_Lock(buffer, &data, &max_length, &length))) + { + hr = wave_sink_write_data(sink, data, length); + IMFMediaBuffer_Unlock(buffer); + } + } + + if (buffer) + IMFMediaBuffer_Release(buffer); + } + + LeaveCriticalSection(&sink->cs); + + return hr; }
static HRESULT WINAPI wave_stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type, @@ -703,6 +861,10 @@ HRESULT WINAPI MFCreateWAVEMediaSink(IMFByteStream *bytestream, IMFMediaType *me goto failed; }
+ /* Update derived fields. */ + object->fmt->nAvgBytesPerSec = object->fmt->nSamplesPerSec * object->fmt->nChannels * object->fmt->wBitsPerSample / 8; + object->fmt->nBlockAlign = object->fmt->nChannels * object->fmt->wBitsPerSample / 8; + object->IMFFinalizableMediaSink_iface.lpVtbl = &wave_sink_vtbl; object->IMFMediaEventGenerator_iface.lpVtbl = &wave_sink_events_vtbl; object->IMFStreamSink_iface.lpVtbl = &wave_stream_sink_vtbl;
From: Nikolay Sivov nsivov@codeweavers.com
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfsrcsnk/Makefile.in | 3 + dlls/mfsrcsnk/factory.c | 115 +++++++++++++++++++++++++++++++ dlls/mfsrcsnk/mfsrcsnk.idl | 26 +++++++ dlls/mfsrcsnk/mfsrcsnk.spec | 2 + dlls/mfsrcsnk/mfsrcsnk_private.h | 21 ++++++ dlls/mfsrcsnk/wave.c | 49 +++++++++++++ 6 files changed, 216 insertions(+) create mode 100644 dlls/mfsrcsnk/factory.c create mode 100644 dlls/mfsrcsnk/mfsrcsnk.idl create mode 100644 dlls/mfsrcsnk/mfsrcsnk_private.h
diff --git a/dlls/mfsrcsnk/Makefile.in b/dlls/mfsrcsnk/Makefile.in index a63ebaf60eb..5f586dcc83f 100644 --- a/dlls/mfsrcsnk/Makefile.in +++ b/dlls/mfsrcsnk/Makefile.in @@ -5,4 +5,7 @@ IMPORTS = ole32 mfplat mfuuid uuid EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \ + factory.c \ wave.c + +IDL_SRCS = mfsrcsnk.idl diff --git a/dlls/mfsrcsnk/factory.c b/dlls/mfsrcsnk/factory.c new file mode 100644 index 00000000000..2e03451a523 --- /dev/null +++ b/dlls/mfsrcsnk/factory.c @@ -0,0 +1,115 @@ +/* + * Copyright 2022 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include "mfidl.h" +#include "wine/mfinternal.h" + +#include "mfsrcsnk_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct class_factory +{ + IClassFactory IClassFactory_iface; + HRESULT (*create_instance)(REFIID riid, void **out); +}; + +static inline struct class_factory *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface); +} + +static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID riid, void **out) +{ + if (IsEqualGUID(riid, &IID_IClassFactory) || + IsEqualGUID(riid, &IID_IUnknown)) + { + IClassFactory_AddRef(iface); + *out = iface; + return S_OK; + } + + *out = NULL; + WARN("Interface %s is not supported.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI class_factory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI class_factory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface, IUnknown *outer, + REFIID riid, void **out) +{ + struct class_factory *factory = impl_from_IClassFactory(iface); + + TRACE("%p, %s, %p.\n", outer, debugstr_guid(riid), out); + + *out = NULL; + + if (outer) + return CLASS_E_NOAGGREGATION; + + return factory->create_instance(riid, out); +} + +static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL dolock) +{ + FIXME("%p, %d.\n", iface, dolock); + + return S_OK; +} + +static const IClassFactoryVtbl class_factory_vtbl = +{ + class_factory_QueryInterface, + class_factory_AddRef, + class_factory_Release, + class_factory_CreateInstance, + class_factory_LockServer, +}; + +static struct class_factory wave_sink_factory = { { &class_factory_vtbl }, wave_sink_factory_create }; + +/*********************************************************************** + * DllGetClassObject (mfsrcsnk.@) + */ +HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) +{ + *out = NULL; + + if (IsEqualGUID(clsid, &CLSID_MFWAVESinkClassFactory)) + { + return IClassFactory_QueryInterface(&wave_sink_factory.IClassFactory_iface, riid, out); + } + else + { + FIXME("Unknown clsid %s.\n", debugstr_guid(clsid)); + return CLASS_E_CLASSNOTAVAILABLE; + } +} diff --git a/dlls/mfsrcsnk/mfsrcsnk.idl b/dlls/mfsrcsnk/mfsrcsnk.idl new file mode 100644 index 00000000000..13bdac2977e --- /dev/null +++ b/dlls/mfsrcsnk/mfsrcsnk.idl @@ -0,0 +1,26 @@ +/* + * Copyright 2022 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep register + +[ + helpstring("MF WAVE Sink Factory"), + threading(both), + uuid(36f99745-23c9-4c9c-8dd5-cc31ce964390) +] +coclass MFWAVESinkClassFactory { } diff --git a/dlls/mfsrcsnk/mfsrcsnk.spec b/dlls/mfsrcsnk/mfsrcsnk.spec index 9acfef27ae2..ab9f2e4c207 100644 --- a/dlls/mfsrcsnk/mfsrcsnk.spec +++ b/dlls/mfsrcsnk/mfsrcsnk.spec @@ -1 +1,3 @@ +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() @ stdcall MFCreateWAVEMediaSink(ptr ptr ptr) diff --git a/dlls/mfsrcsnk/mfsrcsnk_private.h b/dlls/mfsrcsnk/mfsrcsnk_private.h new file mode 100644 index 00000000000..29249d640f8 --- /dev/null +++ b/dlls/mfsrcsnk/mfsrcsnk_private.h @@ -0,0 +1,21 @@ +/* + * Copyright 2022 Nikolay Sivov for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/mfinternal.h" + +extern HRESULT wave_sink_factory_create(REFIID riid, void **out) DECLSPEC_HIDDEN; diff --git a/dlls/mfsrcsnk/wave.c b/dlls/mfsrcsnk/wave.c index ce57865d093..cf74368d8f7 100644 --- a/dlls/mfsrcsnk/wave.c +++ b/dlls/mfsrcsnk/wave.c @@ -22,6 +22,8 @@ #include "mfidl.h" #include "mferror.h"
+#include "mfsrcsnk_private.h" + #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat); @@ -889,3 +891,50 @@ failed:
return hr; } + +static HRESULT WINAPI sink_class_factory_QueryInterface(IMFSinkClassFactory *iface, REFIID riid, void **out) +{ + if (IsEqualIID(riid, &IID_IMFSinkClassFactory) + || IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFSinkClassFactory_AddRef(iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI sink_class_factory_AddRef(IMFSinkClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *stream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **sink) +{ + TRACE("%p, %p, %p, %p.\n", stream, video_type, audio_type, sink); + + return MFCreateWAVEMediaSink(stream, audio_type, sink); +} + +static const IMFSinkClassFactoryVtbl wave_sink_factory_vtbl = +{ + sink_class_factory_QueryInterface, + sink_class_factory_AddRef, + sink_class_factory_Release, + sink_class_factory_CreateMediaSink, +}; + +static IMFSinkClassFactory wave_sink_factory = { &wave_sink_factory_vtbl }; + +HRESULT wave_sink_factory_create(REFIID riid, void **out) +{ + return IMFSinkClassFactory_QueryInterface(&wave_sink_factory, riid, out); +}