Part 1 of a set of changes for making the recording of highlights and "Play of the Game"s to mp4 in Overwatch work.
This MR contains most of the required IMFMediaSink changes.
-- v2: winegstreamer: Avoid an assert in Gstreamer code. mfplat: Return NULL for *stream_sink on error in IMFMediaSink::AddStreamSink. mfplat: Accept any media types without a MF_MT_USER_DATA attribute in MFCreateWaveFormatExFromMFMediaType. mf/tests: Add test for MEStreamSinkStarted and MEStreamSinkRequestSample events. winegstreamer: Request new samples after starting media sink and after processing a sample. mf/tests: Test media sink creation with a media type without MF_MT_USER_DATA attribute. winegstreamer: Generate AAC codec data if not provided in a media type. winegstreamer: Implement IMFStreamSink::IMFMediaTypeHandler.GetMajorType. winegstreamer: Copy media type instead of returning a reference in IMFStreamSink::IMFMediaTypeHandler.GetCurrentMediaType. winegstreamer: Implement IMFMediaSink::GetPresentationClock. winegstreamer: Implement IMFMediaSink::SetPresentationClock.
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mf/tests/mf.c | 3 --- dlls/winegstreamer/media_sink.c | 41 +++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index c05482778df..878a5680e64 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7798,11 +7798,8 @@ static void test_mpeg4_media_sink(void) hr = MFCreatePresentationClock(&clock); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFMediaSink_SetPresentationClock(sink, NULL); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine hr = IMFMediaSink_SetPresentationClock(sink, clock); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); IMFPresentationClock_Release(clock);
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 5ee2c44dc70..0d76c6f30b9 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -91,6 +91,7 @@ struct media_sink
IMFByteStream *bytestream; IMFMediaEventQueue *event_queue; + IMFPresentationClock *clock;
struct list stream_sinks;
@@ -877,9 +878,40 @@ static HRESULT WINAPI media_sink_GetStreamSinkById(IMFFinalizableMediaSink *ifac
static HRESULT WINAPI media_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock) { - FIXME("iface %p, clock %p stub!\n", iface, clock); + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("iface %p, clock %p\n", iface, clock); + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->state == STATE_SHUTDOWN) + { + hr = MF_E_SHUTDOWN; + goto done; + } + + if (media_sink->clock) + { + hr = IMFPresentationClock_RemoveClockStateSink(media_sink->clock, &media_sink->IMFClockStateSink_iface); + if (FAILED(hr)) + goto done; + IMFPresentationClock_Release(media_sink->clock); + media_sink->clock = NULL; + } + + if (clock) + { + hr = IMFPresentationClock_AddClockStateSink(clock, &media_sink->IMFClockStateSink_iface); + if (FAILED(hr)) + goto done; + IMFPresentationClock_AddRef(clock); + media_sink->clock = clock; + } + +done: + LeaveCriticalSection(&media_sink->cs); + return hr; }
static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) @@ -913,6 +945,11 @@ static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface)
IMFMediaEventQueue_Shutdown(media_sink->event_queue); IMFByteStream_Close(media_sink->bytestream); + if (media_sink->clock) + { + IMFPresentationClock_RemoveClockStateSink(media_sink->clock, &media_sink->IMFClockStateSink_iface); + IMFPresentationClock_Release(media_sink->clock); + }
media_sink->state = STATE_SHUTDOWN;
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 0d76c6f30b9..db8fa2fe0c4 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -916,9 +916,26 @@ done:
static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock) { - FIXME("iface %p, clock %p stub!\n", iface, clock); + struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface); + HRESULT hr = S_OK;
- return E_NOTIMPL; + TRACE("iface %p, clock %p\n", iface, clock); + + if (!clock) + return E_POINTER; + + EnterCriticalSection(&media_sink->cs); + + if (media_sink->state == STATE_SHUTDOWN) + hr = MF_E_SHUTDOWN; + else if (media_sink->clock) + IMFPresentationClock_AddRef(*clock = media_sink->clock); + else + hr = MF_E_NO_CLOCK; + + LeaveCriticalSection(&media_sink->cs); + + return hr; }
static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface)
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index db8fa2fe0c4..039acf81232 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -446,6 +446,7 @@ static HRESULT WINAPI stream_sink_type_handler_SetCurrentMediaType(IMFMediaTypeH static HRESULT WINAPI stream_sink_type_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType **type) { struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface); + HRESULT hr;
TRACE("iface %p, type %p.\n", iface, type);
@@ -454,9 +455,13 @@ static HRESULT WINAPI stream_sink_type_handler_GetCurrentMediaType(IMFMediaTypeH if (!stream_sink->type) return MF_E_NOT_INITIALIZED;
- IMFMediaType_AddRef((*type = stream_sink->type)); + if (FAILED(hr = MFCreateMediaType(type))) + return hr;
- return S_OK; + if (FAILED(hr = IMFMediaType_CopyAllItems(stream_sink->type, (IMFAttributes *)*type))) + IMFMediaType_Release(*type); + + return hr; }
static HRESULT WINAPI stream_sink_type_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type)
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mf/tests/mf.c | 5 ----- dlls/winegstreamer/media_sink.c | 11 +++++++++-- 2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 878a5680e64..f0e808e6523 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7818,12 +7818,9 @@ static void test_mpeg4_media_sink(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IMFMediaTypeHandler_GetMajorType(type_handler, NULL); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); hr = IMFMediaTypeHandler_GetMajorType(type_handler, &guid); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(IsEqualGUID(&guid, &MFMediaType_Audio), "Unexpected major type.\n");
hr = IMFMediaTypeHandler_GetMediaTypeCount(type_handler, &count); @@ -7861,10 +7858,8 @@ static void test_mpeg4_media_sink(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IMFMediaTypeHandler_GetMajorType(type_handler, NULL); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); hr = IMFMediaTypeHandler_GetMajorType(type_handler, &guid); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
IMFStreamSink_Release(stream_sink); diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 039acf81232..eba0d527741 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -466,9 +466,16 @@ static HRESULT WINAPI stream_sink_type_handler_GetCurrentMediaType(IMFMediaTypeH
static HRESULT WINAPI stream_sink_type_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type) { - FIXME("iface %p, type %p.\n", iface, type); + struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface);
- return E_NOTIMPL; + TRACE("iface %p, type %p.\n", iface, type); + + if (!type) + return E_POINTER; + if (!stream_sink->type) + return MF_E_NOT_INITIALIZED; + + return IMFMediaType_GetGUID(stream_sink->type, &MF_MT_MAJOR_TYPE, type); }
static const IMFMediaTypeHandlerVtbl stream_sink_type_handler_vtbl =
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/winegstreamer/mfplat.c | 99 +++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index e6d9fb9fd2c..44422d1c12c 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -25,6 +25,7 @@ #include "initguid.h" #include "d3d9types.h" #include "mfapi.h" +#include "mferror.h" #include "mmreg.h"
#include "wine/debug.h" @@ -632,22 +633,112 @@ static void mf_media_type_to_wg_format_audio(IMFMediaType *type, const GUID *sub FIXME("Unrecognized audio subtype %s, depth %u.\n", debugstr_guid(subtype), depth); }
+static inline int min_int(int a, int b) +{ + return a < b ? a : b; +} + +static inline void write_bits(BYTE *dst, int *bit_pos, UINT32 val, int bits) +{ + BYTE *start_ptr = dst + *bit_pos / 8; + int used = *bit_pos % 8; + *bit_pos += bits; + + do + { + int c = min_int(8 - used, bits); + BYTE m = ((1U << c) - 1); + BYTE v = val >> (bits - c); + *start_ptr = (*start_ptr & ~(m << (8 - c - used))) | ((v & m) << (8 - c - used)); + bits -= c; + start_ptr++; + used = 0; + } while (bits); +} + +static HRESULT generate_aac_user_data(IMFMediaType *type, BOOL raw, BYTE *out, size_t *len) +{ + UINT32 frequency, channels, payload_type, profile_level; + HEAACWAVEFORMAT *user_data; + int bits = 0; + + if (!raw) + { + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &payload_type))) + payload_type = 0; + else if (payload_type > 3) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &profile_level))) + profile_level = 0x29; + else if (profile_level > 0xFFFF) + return MF_E_INVALIDMEDIATYPE; + + user_data = (HEAACWAVEFORMAT *)(out - FIELD_OFFSET(HEAACWAVEFORMAT, wfInfo.wPayloadType)); + user_data->wfInfo.wPayloadType = payload_type; + user_data->wfInfo.wAudioProfileLevelIndication = profile_level; + out = user_data->pbAudioSpecificConfig; + } + + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &frequency)) || + frequency > 0xFFFFFF) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)) || + channels < 1 || channels > 8 || channels == 7) + return MF_E_INVALIDMEDIATYPE; + + if (channels == 8) + channels = 7; + + write_bits(out, &bits, 2, 5); + switch (frequency) + { + case 96000: write_bits(out, &bits, 0, 4); break; + case 88200: write_bits(out, &bits, 1, 4); break; + case 64000: write_bits(out, &bits, 2, 4); break; + case 48000: write_bits(out, &bits, 3, 4); break; + case 44100: write_bits(out, &bits, 4, 4); break; + case 32000: write_bits(out, &bits, 5, 4); break; + case 24000: write_bits(out, &bits, 6, 4); break; + case 22050: write_bits(out, &bits, 7, 4); break; + case 16000: write_bits(out, &bits, 8, 4); break; + case 12000: write_bits(out, &bits, 9, 4); break; + case 11025: write_bits(out, &bits, 10, 4); break; + case 8000: write_bits(out, &bits, 11, 4); break; + case 7350: write_bits(out, &bits, 12, 4); break; + default: + write_bits(out, &bits, 15, 4); + write_bits(out, &bits, frequency, 24); + break; + } + write_bits(out, &bits, channels, 4); + + if (raw) + *len = (bits + 7) / 8; + else + *len = FIELD_OFFSET(HEAACWAVEFORMAT, pbAudioSpecificConfig[(bits + 7) / 8]) - sizeof(user_data->wfInfo.wfx); + return S_OK; +} + static void mf_media_type_to_wg_format_audio_mpeg4(IMFMediaType *type, const GUID *subtype, struct wg_format *format) { BYTE buffer[sizeof(HEAACWAVEFORMAT) + 64]; HEAACWAVEFORMAT *wfx = (HEAACWAVEFORMAT *)buffer; UINT32 codec_data_size; - BOOL raw_aac; + BOOL raw_aac = IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC); + size_t len;
wfx->wfInfo.wfx.cbSize = sizeof(buffer) - sizeof(wfx->wfInfo.wfx); if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, (BYTE *)(&wfx->wfInfo.wfx + 1), wfx->wfInfo.wfx.cbSize, &codec_data_size))) { - FIXME("Codec data is not set.\n"); - return; + if (FAILED(generate_aac_user_data(type, raw_aac, (BYTE *)(&wfx->wfInfo.wfx + 1), &len))) + { + FIXME("Codec data is not set.\n"); + return; + } + codec_data_size = len; }
- raw_aac = IsEqualGUID(subtype, &MFAudioFormat_RAW_AAC); if (!raw_aac) codec_data_size -= min(codec_data_size, sizeof(HEAACWAVEINFO) - sizeof(WAVEFORMATEX)); if (codec_data_size > sizeof(format->u.audio_mpeg4.codec_data))
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mf/tests/mf.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index f0e808e6523..f05ec623d26 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -7594,8 +7594,9 @@ static void test_MFRequireProtectedEnvironment(void)
static void test_mpeg4_media_sink(void) { - IMFMediaSink *sink = NULL, *sink2 = NULL, *sink_audio = NULL, *sink_video = NULL, *sink_empty = NULL; - IMFByteStream *bytestream, *bytestream_audio, *bytestream_video, *bytestream_empty; + IMFMediaSink *sink = NULL, *sink2 = NULL, *sink_audio = NULL, *sink_video = NULL, *sink_empty = NULL, + *sink_no_user_data = NULL; + IMFByteStream *bytestream, *bytestream_audio, *bytestream_video, *bytestream_empty, *bytestream_no_user_data; DWORD id, count, flags, width = 96, height = 96; IMFMediaType *audio_type, *video_type, *media_type; IMFMediaTypeHandler *type_handler = NULL; @@ -7626,8 +7627,6 @@ static void test_mpeg4_media_sink(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AAC_PAYLOAD_TYPE, 0); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaType_SetBlob(audio_type, &MF_MT_USER_DATA, test_aac_codec_data, sizeof(test_aac_codec_data)); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IMFMediaType_SetGUID(video_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -7641,6 +7640,8 @@ static void test_mpeg4_media_sink(void) test_h264_sequence_header, sizeof(test_h264_sequence_header)); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
+ hr = MFCreateTempFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, 0, &bytestream_no_user_data); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = MFCreateTempFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, 0, &bytestream_audio); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = MFCreateTempFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, 0, &bytestream_video); @@ -7662,6 +7663,15 @@ static void test_mpeg4_media_sink(void) hr = MFCreateMPEG4MediaSink(bytestream_empty, NULL, NULL, &sink_empty); ok(hr == S_OK || broken(hr == E_INVALIDARG), "Unexpected hr %#lx.\n", hr);
+ hr = MFCreateMPEG4MediaSink(bytestream_no_user_data, NULL, audio_type, &sink_no_user_data); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IMFMediaSink_Release(sink_no_user_data); + IMFByteStream_Release(bytestream_no_user_data); + + hr = IMFMediaType_SetBlob(audio_type, &MF_MT_USER_DATA, test_aac_codec_data, sizeof(test_aac_codec_data)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = MFCreateMPEG4MediaSink(bytestream_audio, NULL, audio_type, &sink_audio); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/winegstreamer/media_sink.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index eba0d527741..6e9727aa5a9 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -599,7 +599,12 @@ static HRESULT media_sink_start(struct media_sink *media_sink)
media_sink->state = STATE_STARTED;
- return media_sink_queue_stream_event(media_sink, MEStreamSinkStarted); + if (FAILED(hr = media_sink_queue_stream_event(media_sink, MEStreamSinkStarted))) + TRACE("Queueing MEStreamSinkStarted failed\n"); + else if (FAILED(hr = media_sink_queue_stream_event(media_sink, MEStreamSinkRequestSample))) + TRACE("Queueing MEStreamSinkRequestSample failed\n"); + + return hr; }
static HRESULT media_sink_stop(struct media_sink *media_sink) @@ -617,6 +622,7 @@ static HRESULT media_sink_pause(struct media_sink *media_sink)
static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *sample, UINT32 stream_id) { + struct stream_sink *stream_sink = NULL; wg_muxer_t muxer = media_sink->muxer; struct wg_sample *wg_sample; LONGLONG time, duration; @@ -649,6 +655,18 @@ static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *samp hr = wg_muxer_push_sample(muxer, wg_sample, stream_id); wg_sample_release(wg_sample);
+ if (SUCCEEDED(hr)) + { + LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry) + { + if (stream_sink->id == stream_id) + { + hr = IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL); + break; + } + } + } + return hr; }
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mf/tests/mf.c | 181 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index f05ec623d26..5e45cfb5a3c 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -5778,6 +5778,186 @@ done: ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+#define wait_media_event_generator(a, b, c, d, e) wait_media_event_generator_(__LINE__, a, b, c, d, e) +static HRESULT wait_media_event_generator_(int line, IMFMediaEventGenerator *generator, IMFAsyncCallback *callback, + MediaEventType expect_type, DWORD timeout, PROPVARIANT *value) +{ + struct test_callback *impl = impl_from_IMFAsyncCallback(callback); + MediaEventType type; + HRESULT hr, status; + DWORD ret; + GUID guid; + + do + { + hr = IMFMediaEventGenerator_BeginGetEvent(generator, &impl->IMFAsyncCallback_iface, (IUnknown *)generator); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ret = WaitForSingleObject(impl->event, timeout); + ok_(__FILE__, line)(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", ret); + hr = IMFMediaEvent_GetType(impl->media_event, &type); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } while (type != expect_type); + + ok_(__FILE__, line)(type == expect_type, "got type %lu\n", type); + + hr = IMFMediaEvent_GetExtendedType(impl->media_event, &guid); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(__FILE__, line)(IsEqualGUID(&guid, &GUID_NULL), "got extended type %s\n", debugstr_guid(&guid)); + + hr = IMFMediaEvent_GetValue(impl->media_event, value); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaEvent_GetStatus(impl->media_event, &status); + ok_(__FILE__, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + return status; +} + +static void load_resource(const WCHAR *filename, const BYTE **data, DWORD *length) +{ + HRSRC resource = FindResourceW(NULL, filename, (const WCHAR *)RT_RCDATA); + ok(resource != 0, "FindResourceW failed, error %lu\n", GetLastError()); + *data = LockResource(LoadResource(GetModuleHandleW(NULL), resource)); + *length = SizeofResource(GetModuleHandleW(NULL), resource); +} + +static void test_media_sink_events(void) +{ + IMFPresentationTimeSource *time_source; + IMFPresentationClock *clock, *clock2; + IMFMediaEventGenerator *eg; + IMFAsyncCallback *callback; + IMFByteStream *bytestream; + IMFMediaType *audio_type; + const BYTE *aacenc_data; + IMFSample *input_sample; + IMFStreamSink *stream; + ULONG aacenc_data_len; + IMFMediaEvent *event; + PROPVARIANT propvar; + IMFMediaSink *sink; + HRESULT hr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Failed to start up, hr %#lx.\n", hr); + + callback = create_test_callback(TRUE); + + hr = MFCreateTempFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, 0, &bytestream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateMediaType(&audio_type); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFMediaType_SetGUID(audio_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetGUID(audio_type, &MF_MT_SUBTYPE, &MFAudioFormat_AAC); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AUDIO_NUM_CHANNELS, 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, 16); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, 44100); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 12000); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 41); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaType_SetUINT32(audio_type, &MF_MT_AAC_PAYLOAD_TYPE, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreateADTSMediaSink(bytestream, audio_type, &sink); + IMFMediaType_Release(audio_type); + if (hr == REGDB_E_CLASSNOTREG) + { + skip("ADTS media sink is not supported, skipping tests.\n"); + goto done; + } + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = MFCreatePresentationClock(&clock); + ok(hr == S_OK, "Failed to create clock object, hr %#lx.\n", hr); + + hr = IMFMediaSink_SetPresentationClock(sink, clock); + ok(hr == S_OK, "Failed to set presentation clock, hr %#lx.\n", hr); + + hr = MFCreateSystemTimeSource(&time_source); + ok(hr == S_OK, "Failed to create time source, hr %#lx.\n", hr); + + hr = IMFPresentationClock_SetTimeSource(clock, time_source); + ok(hr == S_OK, "Failed to set time source, hr %#lx.\n", hr); + IMFPresentationTimeSource_Release(time_source); + + hr = IMFMediaSink_GetStreamSinkByIndex(sink, 0, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMFStreamSink_QueryInterface(stream, &IID_IMFMediaEventGenerator, (void **)&eg); + ok(hr == S_OK, "Failed to get interface, hr %#lx.\n", hr); + + hr = IMFMediaEventGenerator_GetEvent(eg, MF_EVENT_FLAG_NO_WAIT, &event); + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr); + + hr = IMFPresentationClock_Start(clock, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event_generator(eg, callback, MEStreamSinkStarted, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + PropVariantClear(&propvar); + + hr = wait_media_event_generator(eg, callback, MEStreamSinkRequestSample, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + PropVariantClear(&propvar); + + hr = IMFMediaEventGenerator_GetEvent(eg, MF_EVENT_FLAG_NO_WAIT, &event); + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr); + + load_resource(L"aacencdata.bin", &aacenc_data, &aacenc_data_len); + ok(aacenc_data_len == 24861, "got length %lu\n", aacenc_data_len); + + input_sample = create_sample(aacenc_data + sizeof(DWORD), *(DWORD *)aacenc_data); + + hr = IMFStreamSink_ProcessSample(stream, input_sample); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = wait_media_event_generator(eg, callback, MEStreamSinkRequestSample, 1000, &propvar); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_EMPTY, "got vt %u\n", propvar.vt); + PropVariantClear(&propvar); + + hr = IMFMediaEventGenerator_GetEvent(eg, MF_EVENT_FLAG_NO_WAIT, &event); + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Unexpected hr %#lx.\n", hr); + + /* Shutdown */ + EXPECT_REF(clock, 2); + hr = IMFMediaSink_Shutdown(sink); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + EXPECT_REF(clock, 1); + + hr = IMFMediaSink_Shutdown(sink); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + clock2 = NULL; + hr = IMFMediaSink_GetPresentationClock(sink, &clock2); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(!clock2, "Unexpected clock %p.\n", clock2); + + hr = IMFMediaSink_SetPresentationClock(sink, clock); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + + IMFMediaEventGenerator_Release(eg); + IMFStreamSink_Release(stream); + IMFPresentationClock_Release(clock); + IMFMediaSink_Release(sink); +done: + IMFAsyncCallback_Release(callback); + IMFByteStream_Release(bytestream); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); +} + static void test_quality_manager(void) { IMFPresentationClock *clock; @@ -8259,6 +8439,7 @@ START_TEST(mf) test_sample_grabber_is_mediatype_supported(); test_sample_grabber_orientation(MFVideoFormat_RGB32); test_sample_grabber_orientation(MFVideoFormat_NV12); + test_media_sink_events(); test_quality_manager(); test_sar(); test_evr();
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mfplat/mediatype.c | 4 ---- dlls/mfplat/tests/mfplat.c | 10 ++++++++-- 2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index 66c0aaa2c62..13bf7b41b0e 100644 --- a/dlls/mfplat/mediatype.c +++ b/dlls/mfplat/mediatype.c @@ -2981,11 +2981,7 @@ HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *mediatype, WAVE return E_INVALIDARG;
if (FAILED(hr = IMFMediaType_GetBlobSize(mediatype, &MF_MT_USER_DATA, &user_size))) - { - if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float)) - return hr; user_size = 0; - }
if (flags == MFWaveFormatExConvertFlag_ForceExtensible) extra_size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(*format); diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 6897036e9dd..06cf7e81406 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -83,6 +83,8 @@ DEFINE_MEDIATYPE_GUID(MFVideoFormat_IMC2, MAKEFOURCC('I','M','C','2')); DEFINE_MEDIATYPE_GUID(MFVideoFormat_IMC3, MAKEFOURCC('I','M','C','3')); DEFINE_MEDIATYPE_GUID(MFVideoFormat_IMC4, MAKEFOURCC('I','M','C','4'));
+DEFINE_MEDIATYPE_GUID(MFAudioFormat_RAW_AAC,WAVE_FORMAT_RAW_AAC1); + DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_AVC1,MAKEFOURCC('A','V','C','1')); DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_MP42,MAKEFOURCC('M','P','4','2')); DEFINE_MEDIATYPE_GUID(MEDIASUBTYPE_mp42,MAKEFOURCC('m','p','4','2')); @@ -5132,8 +5134,12 @@ static void test_MFCreateWaveFormatExFromMFMediaType(void) } wave_fmt_tests[] = { - { &MFAudioFormat_PCM, WAVE_FORMAT_PCM, }, - { &MFAudioFormat_Float, WAVE_FORMAT_IEEE_FLOAT, }, + { &MFAudioFormat_PCM, WAVE_FORMAT_PCM, }, + { &MFAudioFormat_Float, WAVE_FORMAT_IEEE_FLOAT, }, + { &MFAudioFormat_AAC, WAVE_FORMAT_MPEG_HEAAC, }, + { &MFAudioFormat_RAW_AAC, WAVE_FORMAT_RAW_AAC1, }, + { &MFAudioFormat_MP3, WAVE_FORMAT_MPEGLAYER3, }, + { &MFAudioFormat_Opus, WAVE_FORMAT_OPUS, }, }; WAVEFORMATEXTENSIBLE *format_ext; IMFMediaType *mediatype;
From: Torge Matthies tmatthies@codeweavers.com
There are tests for this in dlls/mf, however they are gated behind IMFMediaSink::GetCharacteristics succeeding and the characteristics not containing MEDIASINK_FIXED_STREAMS. --- dlls/winegstreamer/media_sink.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 6e9727aa5a9..e5b97d83884 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -794,6 +794,9 @@ static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, D TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n", iface, stream_sink_id, media_type, stream_sink);
+ if (stream_sink) + *stream_sink = NULL; + EnterCriticalSection(&media_sink->cs);
if (media_sink_get_stream_sink_by_id(media_sink, stream_sink_id))
From: Torge Matthies tmatthies@codeweavers.com
I have seen pipelines with a vaapipostproc element, which makes the caps non-fixed. wg_format_from_caps calls gst_audio_info_from_caps, which expects fixed caps and asserts if they are not fixed. This does not result in an error in release builds, but we should still avoid it. --- dlls/winegstreamer/wg_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index b0a344d5186..31d534b9ec2 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -976,7 +976,7 @@ static void pad_added_cb(GstElement *element, GstPad *pad, gpointer user) if (!(stream = create_stream(parser))) return;
- caps = gst_pad_query_caps(pad, NULL); + caps = gst_caps_fixate(gst_pad_query_caps(pad, NULL)); wg_format_from_caps(&stream->codec_format, caps); gst_caps_unref(caps);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=143936
Your paranoid android.
=== debian11b (64 bit WoW report) ===
mf: transform.c:2385: Test failed: aacdec: "MF_MT_USER_DATA": SetInputType returned 0. transform.c:2385: Test failed: aacdec: "MF_MT_USER_DATA": SetInputType returned 0.
secur32: schannel.c:538: Test failed: cert_cnt = 2
On Mon Feb 26 10:34:50 2024 +0000, Rémi Bernon wrote:
Would be nice to have tests to validate that these events are supposed to be queued.
I added a test showing that these events are indeed supposed to be queued once the source is started, and that `MEStreamSinkRequestSample` continues to be queued each time a sample has been processed. The test currently doesn't run on Wine for lack of an AAC encoder, but I have run the test with a commit that will be part of a later MR that implements an AAC encoder, and the test passes.
On Mon Mar 11 09:08:58 2024 +0000, Torge Matthies wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5159/diffs?diff_id=104258&start_sha=c840413e5e72507795f06e29a716156ffb1f5092#c875e7dc303705d10bd40b5c5a5ddfb3010eef98_466_460)
Changed it out for an even shorter version
On Mon Mar 11 09:08:59 2024 +0000, Torge Matthies wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5159/diffs?diff_id=104258&start_sha=c840413e5e72507795f06e29a716156ffb1f5092#c875e7dc303705d10bd40b5c5a5ddfb3010eef98_481_473)
Done, did the same in `stream_sink_type_handler_GetCurrentMediaType` too
On Mon Mar 11 09:08:57 2024 +0000, Torge Matthies wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5159/diffs?diff_id=104258&start_sha=c840413e5e72507795f06e29a716156ffb1f5092#c875e7dc303705d10bd40b5c5a5ddfb3010eef98_817_797)
I moved the generating of the user data to `mf_media_type_to_wg_format_audio_mpeg4`
On Mon Mar 11 09:08:56 2024 +0000, Torge Matthies wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/5159/diffs?diff_id=104258&start_sha=c840413e5e72507795f06e29a716156ffb1f5092#c875e7dc303705d10bd40b5c5a5ddfb3010eef98_973_962)
Done
test-linux-32 has an `mfplay` failure but it looks unrelated (and test-linux-64 is fine). I'm running the test again to confirm.
On Mon Mar 11 09:14:59 2024 +0000, Torge Matthies wrote:
I added a test showing that these events are indeed supposed to be queued once the source is started, and that `MEStreamSinkRequestSample` continues to be queued each time a sample has been processed. The test currently doesn't run on Wine for lack of an AAC encoder, but I have run the test with a commit that will be part of a later MR that implements an AAC encoder, and the test passes.
Correction: It needs an ADTS sink class factory, not a full AAC encoder.