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.
-- v3: 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: Add support for ADTS container format (.aac files).
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/mfreadwrite/writer.c | 2 ++ dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/main.c | 3 +++ dlls/winegstreamer/media_sink.c | 23 ++++++++++++++++++++ dlls/winegstreamer/winegstreamer_classes.idl | 7 ++++++ 5 files changed, 36 insertions(+)
diff --git a/dlls/mfreadwrite/writer.c b/dlls/mfreadwrite/writer.c index c708664d2fc..f4ef3bc12d4 100644 --- a/dlls/mfreadwrite/writer.c +++ b/dlls/mfreadwrite/writer.c @@ -1018,6 +1018,7 @@ static HRESULT sink_writer_get_sink_factory_class(const WCHAR *url, IMFAttribute { L".mp3", &MFTranscodeContainerType_MP3 }, { L".wav", &MFTranscodeContainerType_WAVE }, { L".avi", &MFTranscodeContainerType_AVI }, + { L".aac", &MFTranscodeContainerType_ADTS }, }; static const struct { @@ -1029,6 +1030,7 @@ static HRESULT sink_writer_get_sink_factory_class(const WCHAR *url, IMFAttribute { &MFTranscodeContainerType_MP3, &CLSID_MFMP3SinkClassFactory }, { &MFTranscodeContainerType_WAVE, &CLSID_MFWAVESinkClassFactory }, { &MFTranscodeContainerType_AVI, &CLSID_MFAVISinkClassFactory }, + { &MFTranscodeContainerType_ADTS, &CLSID_MFADTSSinkClassFactory }, }; const WCHAR *extension; GUID container; diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 2374fb6fddd..17cef4b150e 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -115,6 +115,7 @@ HRESULT resampler_create(IUnknown *outer, IUnknown **out); HRESULT color_convert_create(IUnknown *outer, IUnknown **out); HRESULT mp3_sink_class_factory_create(IUnknown *outer, IUnknown **out); HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out); +HRESULT adts_sink_class_factory_create(IUnknown *outer, IUnknown **out);
bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm); bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 74a6eee5b1d..d6ce9349fed 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -759,6 +759,7 @@ static struct class_factory resampler_cf = {{&class_factory_vtbl}, resampler_cre static struct class_factory color_convert_cf = {{&class_factory_vtbl}, color_convert_create}; static struct class_factory mp3_sink_class_factory_cf = {{&class_factory_vtbl}, mp3_sink_class_factory_create}; static struct class_factory mpeg4_sink_class_factory_cf = {{&class_factory_vtbl}, mpeg4_sink_class_factory_create}; +static struct class_factory adts_sink_class_factory_cf = {{&class_factory_vtbl}, adts_sink_class_factory_create};
HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) { @@ -799,6 +800,8 @@ HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **out) factory = &mp3_sink_class_factory_cf; else if (IsEqualGUID(clsid, &CLSID_MFMPEG4SinkClassFactory)) factory = &mpeg4_sink_class_factory_cf; + else if (IsEqualGUID(clsid, &CLSID_MFADTSSinkClassFactory)) + factory = &adts_sink_class_factory_cf; else { FIXME("%s not implemented, returning CLASS_E_CLASSNOTAVAILABLE.\n", debugstr_guid(clsid)); diff --git a/dlls/winegstreamer/media_sink.c b/dlls/winegstreamer/media_sink.c index 6e9727aa5a9..78f8249692d 100644 --- a/dlls/winegstreamer/media_sink.c +++ b/dlls/winegstreamer/media_sink.c @@ -1421,6 +1421,14 @@ static HRESULT WINAPI mpeg4_sink_class_factory_CreateMediaSink(IMFSinkClassFacto return sink_class_factory_create_media_sink(iface, bytestream, format, video_type, audio_type, out); }
+static HRESULT WINAPI adts_sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream, + IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out) +{ + const char *format = "application/x-gst-av-adts"; + + return sink_class_factory_create_media_sink(iface, bytestream, format, video_type, audio_type, out); +} + static const IMFSinkClassFactoryVtbl mp3_sink_class_factory_vtbl = { sink_class_factory_QueryInterface, @@ -1437,8 +1445,17 @@ static const IMFSinkClassFactoryVtbl mpeg4_sink_class_factory_vtbl = mpeg4_sink_class_factory_CreateMediaSink, };
+static const IMFSinkClassFactoryVtbl aac_sink_class_factory_vtbl = +{ + sink_class_factory_QueryInterface, + sink_class_factory_AddRef, + sink_class_factory_Release, + adts_sink_class_factory_CreateMediaSink, +}; + static IMFSinkClassFactory mp3_sink_class_factory = { &mp3_sink_class_factory_vtbl }; static IMFSinkClassFactory mpeg4_sink_class_factory = { &mpeg4_sink_class_factory_vtbl }; +static IMFSinkClassFactory aac_sink_class_factory = { &aac_sink_class_factory_vtbl };
HRESULT mp3_sink_class_factory_create(IUnknown *outer, IUnknown **out) { @@ -1451,3 +1468,9 @@ HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out) *out = (IUnknown *)&mpeg4_sink_class_factory; return S_OK; } + +HRESULT adts_sink_class_factory_create(IUnknown *outer, IUnknown **out) +{ + *out = (IUnknown *)&aac_sink_class_factory; + return S_OK; +} diff --git a/dlls/winegstreamer/winegstreamer_classes.idl b/dlls/winegstreamer/winegstreamer_classes.idl index bb727ca8645..ec2103c5f26 100644 --- a/dlls/winegstreamer/winegstreamer_classes.idl +++ b/dlls/winegstreamer/winegstreamer_classes.idl @@ -131,3 +131,10 @@ coclass MFMP3SinkClassFactory {} uuid(a22c4fc7-6e91-4e1d-89e9-53b2667b72ba) ] coclass MFMPEG4SinkClassFactory {} + +[ + helpstring("MF ADTS Sink Class Factory"), + threading(both), + uuid(d7ca55ab-5022-4db3-a599-abafa358e6f3) +] +coclass MFADTSSinkClassFactory {}
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 78f8249692d..2300046aaeb 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))
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=143973
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 Mar 11 20:27:05 2024 +0000, Torge Matthies wrote:
Correction: It needs an ADTS sink class factory, not a full AAC encoder.
I just added the ADTS support in to this MR too, so that the test will pass
Rémi Bernon (@rbernon) commented about dlls/winegstreamer/mfplat.c:
- 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;
+}
Fwiw I'm worried about this part that seems to come from the MPEG spec and would potentially make the code fall into some "possibly patented" category.
On Tue Mar 12 08:44:02 2024 +0000, Rémi Bernon wrote:
Fwiw I'm worried about this part that seems to come from the MPEG spec and would potentially make the code fall into some "possibly patented" category.
I got this from a "MultimediaWiki" website, and gstreamer also contains implementations (parsers instead of generators) of this in both gst-plugins-base-libs (pbutils/codec-utils.h) and gst-plugins-good (aacparse, rtpmp4apay, rtpmp4gpay). If it's still deemed not safe for inclusion in Wine, then maybe we can get away with just using a static, hardcoded user data, or maybe no user data at all.
Is there a way we can just rely on aacparse instead of adding this code manually?
On Tue Mar 12 17:59:20 2024 +0000, Zebediah Figura wrote:
Is there a way we can just rely on aacparse instead of adding this code manually?
We need to generate the codec data instead of parsing it.
Unless we just give gstreamer some constant codec data, and pray that applications don't rely on `MF_MT_USER_DATA` existing, we need this code. It would also encode in a slightly wrong format (the constant one instead of the application-requested one) in that case.
If possible I'd like to generate the codec data *somehow*. But if we don't want to use this code and we find no alternative, then I guess we can't.
On Tue Mar 12 18:00:18 2024 +0000, Torge Matthies wrote:
We need to generate the codec data instead of parsing it. Unless we just give gstreamer some constant codec data, and pray that applications don't rely on `MF_MT_USER_DATA` existing, we need this code. It would also encode in a slightly wrong format (the constant one instead of the application-requested one) in that case. If possible I'd like to generate the codec data *somehow*. But if we don't want to use this code and we find no alternative, then I guess we can't.
We can get gstreamer (or rather the plugin gstreamer uses for AAC encoding) to generate the codec data for us by initializing an aac encoding element and setting the sink pad caps. I just need to think a bit about how I want to integrate this into Wine.
Sadly, there's no public function in gstreamer that just generates AAC codec data, even though they have some basic codec data generators in some places that are plugin-internal.
On Wed Mar 13 05:48:59 2024 +0000, Torge Matthies wrote:
We can get gstreamer (or rather the plugin gstreamer uses for AAC encoding) to generate the codec data for us by initializing an aac encoding element and setting the sink pad caps. I just need to think a bit about how I want to integrate this into Wine. Sadly, there's no public function in gstreamer that just generates AAC codec data, even though they have some basic codec data generators in some places that are plugin-internal.
Correction to this: Seems like just setting the sink pad caps and reading the src pad caps doesn't work, I need to at least encode a 0-sized buffer and have an aacparse element parse the output.