From: Anton Baskanov baskanov@gmail.com
Signed-off-by: Anton Baskanov baskanov@gmail.com Signed-off-by: Zebediah Figura z.figura12@gmail.com --- v3: Tweak some trace messages; simplify critical section handling in IAudioStreamSample::Update() a bit; use get_refcount() where possible; avoid WaitForSingleObject(INFINITE) in tests.
This supersedes https://source.winehq.org/patches/data/184022, and depends on https://source.winehq.org/patches/data/183955.
dlls/amstream/audiostream.c | 142 ++++++++++++++- dlls/amstream/tests/amstream.c | 312 +++++++++++++++++++++++++++++++++ 2 files changed, 450 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index 62c82195076..c09a1149aa0 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -57,6 +57,7 @@ struct audio_stream FILTER_STATE state; BOOL eos; struct list receive_queue; + struct list update_queue; };
typedef struct { @@ -64,6 +65,13 @@ typedef struct { LONG ref; struct audio_stream *parent; IAudioData *audio_data; + HANDLE update_event; + + struct list entry; + DWORD length; + BYTE *pointer; + DWORD position; + HRESULT update_hr; } IAudioStreamSampleImpl;
static void remove_queued_receive(struct queued_receive *receive) @@ -73,6 +81,18 @@ static void remove_queued_receive(struct queued_receive *receive) free(receive); }
+static void remove_queued_update(IAudioStreamSampleImpl *sample) +{ + HRESULT hr; + + hr = IAudioData_SetActual(sample->audio_data, sample->position); + if (FAILED(hr)) + sample->update_hr = hr; + + list_remove(&sample->entry); + SetEvent(sample->update_event); +} + static void flush_receive_queue(struct audio_stream *stream) { while (!list_empty(&stream->receive_queue)) @@ -84,6 +104,45 @@ static void flush_receive_queue(struct audio_stream *stream) } }
+static void process_update(IAudioStreamSampleImpl *sample, struct queued_receive *receive) +{ + DWORD advance; + + advance = min(receive->length - receive->position, sample->length - sample->position); + memcpy(&sample->pointer[sample->position], &receive->pointer[receive->position], advance); + + receive->position += advance; + sample->position += advance; + + sample->update_hr = (sample->position == sample->length) ? S_OK : MS_S_PENDING; +} + +static void process_updates(struct audio_stream *stream) +{ + while (!list_empty(&stream->update_queue) && !list_empty(&stream->receive_queue)) + { + IAudioStreamSampleImpl *sample = LIST_ENTRY(list_head(&stream->update_queue), IAudioStreamSampleImpl, entry); + struct queued_receive *receive = LIST_ENTRY(list_head(&stream->receive_queue), struct queued_receive, entry); + + process_update(sample, receive); + + if (sample->update_hr != MS_S_PENDING) + remove_queued_update(sample); + if (receive->position == receive->length) + remove_queued_receive(receive); + } + if (stream->eos) + { + while (!list_empty(&stream->update_queue)) + { + IAudioStreamSampleImpl *sample = LIST_ENTRY(list_head(&stream->update_queue), IAudioStreamSampleImpl, entry); + + sample->update_hr = sample->position ? S_OK : MS_S_ENDOFSTREAM; + remove_queued_update(sample); + } + } +} + static inline IAudioStreamSampleImpl *impl_from_IAudioStreamSample(IAudioStreamSample *iface) { return CONTAINING_RECORD(iface, IAudioStreamSampleImpl, IAudioStreamSample_iface); @@ -128,7 +187,10 @@ static ULONG WINAPI IAudioStreamSampleImpl_Release(IAudioStreamSample *iface) TRACE("(%p)->(): new ref = %u\n", iface, ref);
if (!ref) + { + CloseHandle(This->update_event); HeapFree(GetProcessHeap(), 0, This); + }
return ref; } @@ -157,12 +219,78 @@ static HRESULT WINAPI IAudioStreamSampleImpl_SetSampleTimes(IAudioStreamSample * return E_NOTIMPL; }
-static HRESULT WINAPI IAudioStreamSampleImpl_Update(IAudioStreamSample *iface, DWORD flags, HANDLE event, - PAPCFUNC func_APC, DWORD APC_data) +static HRESULT WINAPI IAudioStreamSampleImpl_Update(IAudioStreamSample *iface, + DWORD flags, HANDLE event, PAPCFUNC apc_func, DWORD apc_data) { - FIXME("(%p)->(%x,%p,%p,%u): stub\n", iface, flags, event, func_APC, APC_data); + IAudioStreamSampleImpl *sample = impl_from_IAudioStreamSample(iface); + BYTE *pointer; + DWORD length; + HRESULT hr;
- return E_NOTIMPL; + TRACE("sample %p, flags %#x, event %p, apc_func %p, apc_data %#x.\n", + sample, flags, event, apc_func, apc_data); + + hr = IAudioData_GetInfo(sample->audio_data, &length, &pointer, NULL); + if (FAILED(hr)) + return hr; + + if (event && apc_func) + return E_INVALIDARG; + + if (apc_func) + { + FIXME("APC support is not implemented!\n"); + return E_NOTIMPL; + } + + if (event) + { + FIXME("Event parameter support is not implemented!\n"); + return E_NOTIMPL; + } + + if (flags & ~SSUPDATE_ASYNC) + { + FIXME("Unsupported flags %#x.\n", flags); + return E_NOTIMPL; + } + + EnterCriticalSection(&sample->parent->cs); + + if (sample->parent->state != State_Running) + { + LeaveCriticalSection(&sample->parent->cs); + return MS_E_NOTRUNNING; + } + if (!sample->parent->peer) + { + LeaveCriticalSection(&sample->parent->cs); + return MS_S_ENDOFSTREAM; + } + if (MS_S_PENDING == sample->update_hr) + { + LeaveCriticalSection(&sample->parent->cs); + return MS_E_BUSY; + } + + sample->length = length; + sample->pointer = pointer; + sample->position = 0; + sample->update_hr = MS_S_PENDING; + ResetEvent(sample->update_event); + list_add_tail(&sample->parent->update_queue, &sample->entry); + + process_updates(sample->parent); + hr = sample->update_hr; + + LeaveCriticalSection(&sample->parent->cs); + + if (hr != MS_S_PENDING || (flags & SSUPDATE_ASYNC)) + return hr; + + WaitForSingleObject(sample->update_event, INFINITE); + + return sample->update_hr; }
static HRESULT WINAPI IAudioStreamSampleImpl_CompletionStatus(IAudioStreamSample *iface, DWORD flags, DWORD milliseconds) @@ -210,6 +338,7 @@ static HRESULT audiostreamsample_create(struct audio_stream *parent, IAudioData object->ref = 1; object->parent = parent; object->audio_data = audio_data; + object->update_event = CreateEventW(NULL, FALSE, FALSE, NULL);
*audio_stream_sample = &object->IAudioStreamSample_iface;
@@ -967,6 +1096,8 @@ static HRESULT WINAPI audio_sink_EndOfStream(IPin *iface)
stream->eos = TRUE;
+ process_updates(stream); + LeaveCriticalSection(&stream->cs);
return S_OK; @@ -1113,6 +1244,8 @@ static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample * IMediaSample_AddRef(receive->sample); list_add_tail(&stream->receive_queue, &receive->entry);
+ process_updates(stream); + LeaveCriticalSection(&stream->cs);
return S_OK; @@ -1169,6 +1302,7 @@ HRESULT audio_stream_create(IMultiMediaStream *parent, const MSPID *purpose_id, object->purpose_id = *purpose_id; object->stream_type = stream_type; list_init(&object->receive_queue); + list_init(&object->update_queue);
*media_stream = &object->IAMMediaStream_iface;
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index ef08993d069..6da8a99fe86 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -2370,10 +2370,28 @@ static void testfilter_destroy(struct strmbase_filter *iface) strmbase_filter_cleanup(&filter->filter); }
+static HRESULT testfilter_init_stream(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_BaseFilter(iface); + + BaseOutputPinImpl_Active(&filter->source); + return S_OK; +} + +static HRESULT testfilter_cleanup_stream(struct strmbase_filter *iface) +{ + struct testfilter *filter = impl_from_BaseFilter(iface); + + BaseOutputPinImpl_Inactive(&filter->source); + return S_OK; +} + static const struct strmbase_filter_ops testfilter_ops = { .filter_get_pin = testfilter_get_pin, .filter_destroy = testfilter_destroy, + .filter_init_stream = testfilter_init_stream, + .filter_cleanup_stream = testfilter_cleanup_stream, };
static HRESULT testsource_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) @@ -3035,6 +3053,298 @@ static void test_audiostream_receive(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+static void CALLBACK apc_func(ULONG_PTR param) +{ +} + +static IMediaSample *audiostream_allocate_sample(struct testfilter *source, const BYTE *input_data, DWORD input_length) +{ + IMediaSample *sample; + BYTE *sample_data; + HRESULT hr; + + hr = BaseOutputPinImpl_GetDeliveryBuffer(&source->source, &sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaSample_GetPointer(sample, &sample_data); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaSample_SetActualDataLength(sample, input_length); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + memcpy(sample_data, input_data, input_length); + + return sample; +} + +static IPin *audiostream_pin; +static IMemInputPin *audiostream_mem_input_pin; +static IMediaSample *audiostream_media_sample; + +static DWORD CALLBACK audiostream_end_of_stream(void *param) +{ + HRESULT hr; + + Sleep(100); + hr = IPin_EndOfStream(audiostream_pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + return 0; +} + +static DWORD CALLBACK audiostream_receive(void *param) +{ + HRESULT hr; + + Sleep(100); + hr = IMemInputPin_Receive(audiostream_mem_input_pin, audiostream_media_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + return 0; +} + +static void test_audiostreamsample_update(void) +{ + static const WAVEFORMATEX format = + { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 11025, + .wBitsPerSample = 16, + .nBlockAlign = 2, + .nAvgBytesPerSec = 2 * 11025, + }; + + const AM_MEDIA_TYPE mt = + { + .majortype = MEDIATYPE_Audio, + .subtype = MEDIASUBTYPE_PCM, + .formattype = FORMAT_WaveFormatEx, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&format, + }; + + static const BYTE test_data[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + IAudioStreamSample *stream_sample; + IAudioMediaStream *audio_stream; + IMediaControl *media_control; + IMemInputPin *mem_input_pin; + IMediaSample *media_sample1; + IMediaSample *media_sample2; + struct testfilter source; + IAudioData *audio_data; + IGraphBuilder *graph; + IMediaStream *stream; + DWORD actual_length; + BYTE buffer[6]; + HANDLE thread; + HANDLE event; + HRESULT hr; + ULONG ref; + IPin *pin; + + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, &stream); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IAudioMediaStream, (void **)&audio_stream); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IMemInputPin, (void **)&mem_input_pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(graph != NULL, "Expected non-NULL graph.\n"); + hr = IGraphBuilder_QueryInterface(graph, &IID_IMediaControl, (void **)&media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + testfilter_init(&source); + hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER, &IID_IAudioData, (void **)&audio_data); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioMediaStream_CreateSample(audio_stream, audio_data, 0, &stream_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(event != NULL, "Expected non-NULL event."); + + hr = IAudioStreamSample_Update(stream_sample, 0, event, apc_func, 0); + ok(hr == MS_E_NOTINIT, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_E_NOTINIT, "Got hr %#x.\n", hr); + + hr = IAudioData_SetBuffer(audio_data, sizeof(buffer), buffer, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, 0, event, apc_func, 0); + ok(hr == E_INVALIDARG, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_E_NOTRUNNING, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_S_ENDOFSTREAM, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_ConnectDirect(graph, &source.source.pin.IPin_iface, pin, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + media_sample1 = audiostream_allocate_sample(&source, test_data, 8); + hr = IMemInputPin_Receive(mem_input_pin, media_sample1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = get_refcount(media_sample1); + ok(ref == 2, "Got unexpected refcount %d.\n", ref); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioData_GetInfo(audio_data, NULL, NULL, &actual_length); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(actual_length == 6, "Got actual length %u.\n", actual_length); + + ok(memcmp(buffer, test_data, 6) == 0, "Sample data didn't match.\n"); + + ref = get_refcount(media_sample1); + ok(ref == 2, "Got unexpected refcount %d.\n", ref); + + media_sample2 = audiostream_allocate_sample(&source, test_data, 8); + hr = IMemInputPin_Receive(mem_input_pin, media_sample2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = get_refcount(media_sample2); + ok(ref == 2, "Got unexpected refcount %d.\n", ref); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioData_GetInfo(audio_data, NULL, NULL, &actual_length); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(actual_length == 6, "Got actual length %u.\n", actual_length); + + ok(memcmp(buffer, &test_data[6], 2) == 0, "Sample data didn't match.\n"); + ok(memcmp(&buffer[2], test_data, 4) == 0, "Sample data didn't match.\n"); + + ref = IMediaSample_Release(media_sample1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioData_GetInfo(audio_data, NULL, NULL, &actual_length); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(actual_length == 4, "Got actual length %u.\n", actual_length); + + ok(memcmp(buffer, &test_data[4], 4) == 0, "Sample data didn't match.\n"); + + ref = IMediaSample_Release(media_sample2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_S_ENDOFSTREAM, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaControl_Pause(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + media_sample1 = audiostream_allocate_sample(&source, test_data, 6); + hr = IMemInputPin_Receive(mem_input_pin, media_sample1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(media_sample1); + ok(ref == 1, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_E_NOTRUNNING, "Got hr %#x.\n", hr); + + hr = IMediaControl_Stop(media_control); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + media_sample1 = audiostream_allocate_sample(&source, test_data, 6); + + audiostream_mem_input_pin = mem_input_pin; + audiostream_media_sample = media_sample1; + thread = CreateThread(NULL, 0, audiostream_receive, NULL, 0, NULL); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioData_GetInfo(audio_data, NULL, NULL, &actual_length); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(actual_length == 6, "Got actual length %u.\n", actual_length); + + ok(memcmp(buffer, test_data, 6) == 0, "Sample data didn't match.\n"); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + ref = IMediaSample_Release(media_sample1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + audiostream_pin = pin; + thread = CreateThread(NULL, 0, audiostream_end_of_stream, NULL, 0, NULL); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_S_ENDOFSTREAM, "Got hr %#x.\n", hr); + + ok(!WaitForSingleObject(thread, 2000), "Wait timed out.\n"); + CloseHandle(thread); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + IAudioStreamSample_AddRef(stream_sample); + ref = IAudioStreamSample_Release(stream_sample); + ok(ref == 1, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_Update(stream_sample, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_E_BUSY, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IGraphBuilder_Disconnect(graph, pin); + IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface); + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample, 0, NULL, NULL, 0); + ok(hr == MS_S_ENDOFSTREAM, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + + CloseHandle(event); + ref = IAudioStreamSample_Release(stream_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAudioData_Release(audio_data); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IMediaControl_Release(media_control); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin); + IMemInputPin_Release(mem_input_pin); + IAudioMediaStream_Release(audio_stream); + ref = IMediaStream_Release(stream); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + void test_mediastreamfilter_get_state(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); @@ -3210,6 +3520,8 @@ START_TEST(amstream) test_audiostream_end_of_stream(); test_audiostream_receive();
+ test_audiostreamsample_update(); + test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run();