Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/audiostream.c | 50 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index 4e34b2549e..90c177d177 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -27,10 +27,31 @@ WINE_DEFAULT_DEBUG_CHANNEL(amstream);
static const WCHAR sink_id[] = L"I{A35FF56B-9FDA-11D0-8FDF-00C04FD9189D}";
+struct audio_stream +{ + IAMMediaStream IAMMediaStream_iface; + IAudioMediaStream IAudioMediaStream_iface; + IMemInputPin IMemInputPin_iface; + IPin IPin_iface; + LONG ref; + + IMultiMediaStream* parent; + MSPID purpose_id; + STREAM_TYPE stream_type; + CRITICAL_SECTION cs; + IMediaStreamFilter *filter; + + IPin *peer; + IMemAllocator *allocator; + AM_MEDIA_TYPE mt; + WAVEFORMATEX format; + FILTER_STATE state; +}; + typedef struct { IAudioStreamSample IAudioStreamSample_iface; LONG ref; - IMediaStream *parent; + struct audio_stream *parent; IAudioData *audio_data; } IAudioStreamSampleImpl;
@@ -146,7 +167,7 @@ static const struct IAudioStreamSampleVtbl AudioStreamSample_Vtbl = IAudioStreamSampleImpl_GetAudioData };
-static HRESULT audiostreamsample_create(IAudioMediaStream *parent, IAudioData *audio_data, IAudioStreamSample **audio_stream_sample) +static HRESULT audiostreamsample_create(struct audio_stream *parent, IAudioData *audio_data, IAudioStreamSample **audio_stream_sample) { IAudioStreamSampleImpl *object;
@@ -158,7 +179,7 @@ static HRESULT audiostreamsample_create(IAudioMediaStream *parent, IAudioData *a
object->IAudioStreamSample_iface.lpVtbl = &AudioStreamSample_Vtbl; object->ref = 1; - object->parent = (IMediaStream*)parent; + object->parent = parent; object->audio_data = audio_data;
*audio_stream_sample = &object->IAudioStreamSample_iface; @@ -166,27 +187,6 @@ static HRESULT audiostreamsample_create(IAudioMediaStream *parent, IAudioData *a return S_OK; }
-struct audio_stream -{ - IAMMediaStream IAMMediaStream_iface; - IAudioMediaStream IAudioMediaStream_iface; - IMemInputPin IMemInputPin_iface; - IPin IPin_iface; - LONG ref; - - IMultiMediaStream* parent; - MSPID purpose_id; - STREAM_TYPE stream_type; - CRITICAL_SECTION cs; - IMediaStreamFilter *filter; - - IPin *peer; - IMemAllocator *allocator; - AM_MEDIA_TYPE mt; - WAVEFORMATEX format; - FILTER_STATE state; -}; - static inline struct audio_stream *impl_from_IAMMediaStream(IAMMediaStream *iface) { return CONTAINING_RECORD(iface, struct audio_stream, IAMMediaStream_iface); @@ -565,7 +565,7 @@ static HRESULT WINAPI audio_IAudioMediaStream_CreateSample(IAudioMediaStream *if if (!audio_data) return E_POINTER;
- return audiostreamsample_create(iface, audio_data, sample); + return audiostreamsample_create(This, audio_data, sample); }
static const struct IAudioMediaStreamVtbl audio_IAudioMediaStream_vtbl =
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/audiostream.c | 66 ++++++++++++++++++++++- dlls/amstream/tests/amstream.c | 99 ++++++++++++++++++++++++++++++++-- 2 files changed, 159 insertions(+), 6 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index 90c177d177..c018dabf9d 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -27,6 +27,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(amstream);
static const WCHAR sink_id[] = L"I{A35FF56B-9FDA-11D0-8FDF-00C04FD9189D}";
+enum queued_event_type +{ + QET_END_OF_STREAM +}; + +struct queued_event +{ + struct list entry; + enum queued_event_type type; +}; + struct audio_stream { IAMMediaStream IAMMediaStream_iface; @@ -46,6 +57,8 @@ struct audio_stream AM_MEDIA_TYPE mt; WAVEFORMATEX format; FILTER_STATE state; + BOOL eos; + struct list event_queue; };
typedef struct { @@ -55,6 +68,23 @@ typedef struct { IAudioData *audio_data; } IAudioStreamSampleImpl;
+static void remove_queued_event(struct queued_event *event) +{ + list_remove(&event->entry); + HeapFree(GetProcessHeap(), 0, event); +} + +static void flush_event_queue(struct audio_stream *stream) +{ + while (!list_empty(&stream->event_queue)) + { + struct queued_event *event = + LIST_ENTRY(list_head(&stream->event_queue), struct queued_event, entry); + + remove_queued_event(event); + } +} + static inline IAudioStreamSampleImpl *impl_from_IAudioStreamSample(IAudioStreamSample *iface) { return CONTAINING_RECORD(iface, IAudioStreamSampleImpl, IAudioStreamSample_iface); @@ -347,6 +377,11 @@ static HRESULT WINAPI audio_IAMMediaStream_SetState(IAMMediaStream *iface, FILTE
EnterCriticalSection(&stream->cs);
+ if (State_Stopped == state) + flush_event_queue(stream); + if (State_Stopped == stream->state) + stream->eos = FALSE; + stream->state = state;
LeaveCriticalSection(&stream->cs); @@ -919,8 +954,34 @@ static HRESULT WINAPI audio_sink_QueryInternalConnections(IPin *iface, IPin **pi
static HRESULT WINAPI audio_sink_EndOfStream(IPin *iface) { - FIXME("iface %p, stub!\n", iface); - return E_NOTIMPL; + struct audio_stream *stream = impl_from_IPin(iface); + struct queued_event *event; + + TRACE("(%p/%p)->()\n", iface, stream); + + EnterCriticalSection(&stream->cs); + + if (stream->eos) + { + LeaveCriticalSection(&stream->cs); + return E_FAIL; + } + + event = calloc(1, sizeof(*event)); + if (!event) + { + LeaveCriticalSection(&stream->cs); + return E_OUTOFMEMORY; + } + + event->type = QET_END_OF_STREAM; + list_add_tail(&stream->event_queue, &event->entry); + + stream->eos = TRUE; + + LeaveCriticalSection(&stream->cs); + + return S_OK; }
static HRESULT WINAPI audio_sink_BeginFlush(IPin *iface) @@ -1083,6 +1144,7 @@ HRESULT audio_stream_create(IMultiMediaStream *parent, const MSPID *purpose_id, object->parent = parent; object->purpose_id = *purpose_id; object->stream_type = stream_type; + list_init(&object->event_queue);
*media_stream = &object->IAMMediaStream_iface;
diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index 472c6558fc..2ac492bc3c 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -2381,10 +2381,21 @@ static HRESULT testsource_query_accept(struct strmbase_pin *iface, const AM_MEDI return S_OK; }
-static HRESULT WINAPI testsource_DecideAllocator(struct strmbase_source *iface, - IMemInputPin *peer, IMemAllocator **allocator) +static HRESULT WINAPI testsource_decide_buffer_size(struct strmbase_source *iface, + IMemAllocator *alloc, ALLOCATOR_PROPERTIES *requested) { - return S_OK; + ALLOCATOR_PROPERTIES actual; + + if (!requested->cbAlign) + requested->cbAlign = 1; + + if (requested->cbBuffer < 4096) + requested->cbBuffer = 4096; + + if (!requested->cBuffers) + requested->cBuffers = 2; + + return IMemAllocator_SetProperties(alloc, requested, &actual); }
static const struct strmbase_source_ops testsource_ops = @@ -2392,7 +2403,8 @@ static const struct strmbase_source_ops testsource_ops = .base.pin_query_accept = testsource_query_accept, .base.pin_get_media_type = strmbase_pin_get_media_type, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, - .pfnDecideAllocator = testsource_DecideAllocator, + .pfnDecideBufferSize = testsource_decide_buffer_size, + .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, };
static void testfilter_init(struct testfilter *filter) @@ -2814,6 +2826,84 @@ static void test_audiostream_set_state(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+void test_audiostream_end_of_stream(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, + }; + + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + struct testfilter source; + IGraphBuilder *graph; + IMediaStream *stream; + 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_IPin, (void **)&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"); + testfilter_init(&source); + hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); + 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 = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IPin_EndOfStream(pin); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IPin_EndOfStream(pin); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IPin_EndOfStream(pin); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + IGraphBuilder_Disconnect(graph, pin); + IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface); + + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin); + ref = IMediaStream_Release(stream); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + void test_mediastreamfilter_get_state(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); @@ -2986,6 +3076,7 @@ START_TEST(amstream) test_audiostream_set_format(); test_audiostream_receive_connection(); test_audiostream_set_state(); + test_audiostream_end_of_stream();
test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run();
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/audiostream.c | 50 +++++++++++- dlls/amstream/tests/amstream.c | 135 +++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 3 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index c018dabf9d..dbac8c8ee8 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -29,13 +29,18 @@ static const WCHAR sink_id[] = L"I{A35FF56B-9FDA-11D0-8FDF-00C04FD9189D}";
enum queued_event_type { - QET_END_OF_STREAM + QET_END_OF_STREAM, + QET_SAMPLE };
struct queued_event { struct list entry; enum queued_event_type type; + IMediaSample *sample; + DWORD length; + BYTE *pointer; + DWORD position; };
struct audio_stream @@ -71,6 +76,8 @@ typedef struct { static void remove_queued_event(struct queued_event *event) { list_remove(&event->entry); + if (event->sample) + IMediaSample_Release(event->sample); HeapFree(GetProcessHeap(), 0, event); }
@@ -1090,8 +1097,45 @@ static HRESULT WINAPI audio_meminput_GetAllocatorRequirements(IMemInputPin *ifac
static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample *sample) { - FIXME("iface %p, sample %p, stub!\n", iface, sample); - return E_NOTIMPL; + struct audio_stream *stream = impl_from_IMemInputPin(iface); + struct queued_event *event; + BYTE *pointer; + HRESULT hr; + + TRACE("(%p)->(%p)\n", stream, sample); + + EnterCriticalSection(&stream->cs); + + if (stream->state == State_Stopped) + { + LeaveCriticalSection(&stream->cs); + return VFW_E_WRONG_STATE; + } + + hr = IMediaSample_GetPointer(sample, &pointer); + if (FAILED(hr)) + { + LeaveCriticalSection(&stream->cs); + return hr; + } + + event = calloc(1, sizeof(*event)); + if (!event) + { + LeaveCriticalSection(&stream->cs); + return E_OUTOFMEMORY; + } + + event->type = QET_SAMPLE; + event->length = IMediaSample_GetActualDataLength(sample); + event->pointer = pointer; + event->sample = sample; + IMediaSample_AddRef(event->sample); + list_add_tail(&stream->event_queue, &event->entry); + + LeaveCriticalSection(&stream->cs); + + return S_OK; }
static HRESULT WINAPI audio_meminput_ReceiveMultiple(IMemInputPin *iface, diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index 2ac492bc3c..2582d3c69d 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -2904,6 +2904,140 @@ void test_audiostream_end_of_stream(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+static void test_audiostream_receive(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, + }; + + ALLOCATOR_PROPERTIES properties = + { + .cBuffers = 3, + .cbBuffer = 16, + .cbAlign = 1, + }; + + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + ALLOCATOR_PROPERTIES actual; + struct testfilter source; + IMemAllocator *allocator; + IGraphBuilder *graph; + IMediaStream *stream; + IMediaSample *sample1; + IMediaSample *sample2; + IMediaSample *sample3; + 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_IPin, (void **)&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"); + testfilter_init(&source); + hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (void **)&allocator); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemAllocator_SetProperties(allocator, &properties, &actual); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemAllocator_Commit(allocator); + 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 = IMemAllocator_GetBuffer(allocator, &sample1, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, sample1); + ok(hr == VFW_E_WRONG_STATE, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(sample1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMemAllocator_GetBuffer(allocator, &sample1, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, sample1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IMediaSample_AddRef(sample1); + ref = IMediaSample_Release(sample1); + ok(ref == 2, "Got outstanding refcount %d.\n", ref); + + hr = IMemAllocator_GetBuffer(allocator, &sample2, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, sample2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IMediaSample_AddRef(sample2); + ref = IMediaSample_Release(sample2); + ok(ref == 2, "Got outstanding refcount %d.\n", ref); + + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMemAllocator_GetBuffer(allocator, &sample3, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, sample3); + ok(hr == S_OK, "Got hr %#x.\n", hr); + IMediaSample_AddRef(sample3); + ref = IMediaSample_Release(sample3); + ok(ref == 2, "Got outstanding refcount %d.\n", ref); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IMediaSample_Release(sample1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IMediaSample_Release(sample2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IMediaSample_Release(sample3); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IMemAllocator_GetBuffer(allocator, &sample1, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, sample1); + ok(hr == VFW_E_WRONG_STATE, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(sample1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + IGraphBuilder_Disconnect(graph, pin); + IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface); + + hr = IMemAllocator_Decommit(allocator); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin); + ref = IMediaStream_Release(stream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IMemAllocator_Release(allocator); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + void test_mediastreamfilter_get_state(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); @@ -3077,6 +3211,7 @@ START_TEST(amstream) test_audiostream_receive_connection(); test_audiostream_set_state(); test_audiostream_end_of_stream(); + test_audiostream_receive();
test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run();
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/audiostream.c | 138 +++++++++++++- dlls/amstream/tests/amstream.c | 324 +++++++++++++++++++++++++++++++++ 2 files changed, 459 insertions(+), 3 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index dbac8c8ee8..7776bbb002 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -64,6 +64,7 @@ struct audio_stream FILTER_STATE state; BOOL eos; struct list event_queue; + struct list update_queue; };
typedef struct { @@ -71,6 +72,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_event(struct queued_event *event) @@ -81,6 +89,18 @@ static void remove_queued_event(struct queued_event *event) HeapFree(GetProcessHeap(), 0, event); }
+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_event_queue(struct audio_stream *stream) { while (!list_empty(&stream->event_queue)) @@ -92,6 +112,41 @@ static void flush_event_queue(struct audio_stream *stream) } }
+static void process_update(IAudioStreamSampleImpl *sample, struct queued_event *event) +{ + DWORD advance; + + if (event->type == QET_END_OF_STREAM) + { + sample->update_hr = sample->position ? S_OK : MS_S_ENDOFSTREAM; + return; + } + + advance = min(event->length - event->position, sample->length - sample->position); + memcpy(&sample->pointer[sample->position], &event->pointer[event->position], advance); + + event->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->event_queue)) + { + IAudioStreamSampleImpl *sample = LIST_ENTRY(list_head(&stream->update_queue), IAudioStreamSampleImpl, entry); + struct queued_event *event = LIST_ENTRY(list_head(&stream->event_queue), struct queued_event, entry); + + process_update(sample, event); + + if (MS_S_PENDING != sample->update_hr) + remove_queued_update(sample); + if ((event->type != QET_END_OF_STREAM) && (event->position == event->length)) + remove_queued_event(event); + } +} + static inline IAudioStreamSampleImpl *impl_from_IAudioStreamSample(IAudioStreamSample *iface) { return CONTAINING_RECORD(iface, IAudioStreamSampleImpl, IAudioStreamSample_iface); @@ -136,7 +191,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; } @@ -166,11 +224,79 @@ static HRESULT WINAPI IAudioStreamSampleImpl_SetSampleTimes(IAudioStreamSample * }
static HRESULT WINAPI IAudioStreamSampleImpl_Update(IAudioStreamSample *iface, DWORD flags, HANDLE event, - PAPCFUNC func_APC, DWORD APC_data) + PAPCFUNC func_APC, DWORD APC_data) { - FIXME("(%p)->(%x,%p,%p,%u): stub\n", iface, flags, event, func_APC, APC_data); + IAudioStreamSampleImpl *sample = impl_from_IAudioStreamSample(iface); + DWORD length; + BYTE *pointer; + HRESULT hr;
- return E_NOTIMPL; + TRACE("(%p)->(%x,%p,%p,%u)\n", iface, flags, event, func_APC, APC_data); + + hr = IAudioData_GetInfo(sample->audio_data, &length, &pointer, NULL); + if (FAILED(hr)) + return hr; + + if (event && func_APC) + return E_INVALIDARG; + + if (func_APC) + { + 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; + if (hr != MS_S_PENDING || (flags & SSUPDATE_ASYNC)) + { + LeaveCriticalSection(&sample->parent->cs); + return hr; + } + + LeaveCriticalSection(&sample->parent->cs); + + WaitForSingleObject(sample->update_event, INFINITE); + + return sample->update_hr; }
static HRESULT WINAPI IAudioStreamSampleImpl_CompletionStatus(IAudioStreamSample *iface, DWORD flags, DWORD milliseconds) @@ -218,6 +344,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;
@@ -986,6 +1113,8 @@ static HRESULT WINAPI audio_sink_EndOfStream(IPin *iface)
stream->eos = TRUE;
+ process_updates(stream); + LeaveCriticalSection(&stream->cs);
return S_OK; @@ -1133,6 +1262,8 @@ static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample * IMediaSample_AddRef(event->sample); list_add_tail(&stream->event_queue, &event->entry);
+ process_updates(stream); + LeaveCriticalSection(&stream->cs);
return S_OK; @@ -1189,6 +1320,7 @@ HRESULT audio_stream_create(IMultiMediaStream *parent, const MSPID *purpose_id, object->purpose_id = *purpose_id; object->stream_type = stream_type; list_init(&object->event_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 2582d3c69d..8ebe7dd775 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) @@ -3038,6 +3056,310 @@ 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); + IMediaSample_AddRef(media_sample1); + ref = IMediaSample_Release(media_sample1); + ok(ref == 2, "Got outstanding 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"); + + IMediaSample_AddRef(media_sample1); + ref = IMediaSample_Release(media_sample1); + ok(ref == 2, "Got outstanding 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); + IMediaSample_AddRef(media_sample2); + ref = IMediaSample_Release(media_sample2); + ok(ref == 2, "Got outstanding 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); + + 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 = 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_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"); + + WaitForSingleObject(thread, INFINITE); + 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); + + WaitForSingleObject(thread, INFINITE); + 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(); @@ -3213,6 +3535,8 @@ START_TEST(amstream) test_audiostream_end_of_stream(); test_audiostream_receive();
+ test_audiostreamsample_update(); + test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run();
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/audiostream.c | 19 +++- dlls/amstream/tests/amstream.c | 170 +++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 2 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index 7776bbb002..f9265c971a 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -301,9 +301,24 @@ static HRESULT WINAPI IAudioStreamSampleImpl_Update(IAudioStreamSample *iface, D
static HRESULT WINAPI IAudioStreamSampleImpl_CompletionStatus(IAudioStreamSample *iface, DWORD flags, DWORD milliseconds) { - FIXME("(%p)->(%x,%u): stub\n", iface, flags, milliseconds); + IAudioStreamSampleImpl *sample = impl_from_IAudioStreamSample(iface); + HRESULT hr;
- return E_NOTIMPL; + TRACE("(%p)->(%x,%u)\n", iface, flags, milliseconds); + + if (flags) + { + FIXME("Unsupported flags: %x\n", flags); + return E_NOTIMPL; + } + + EnterCriticalSection(&sample->parent->cs); + + hr = sample->update_hr; + + LeaveCriticalSection(&sample->parent->cs); + + return hr; }
/*** IAudioStreamSample methods ***/ diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index 8ebe7dd775..45686e9aff 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -3360,6 +3360,175 @@ static void test_audiostreamsample_update(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+void test_audiostreamsample_completion_status(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, 8, 9, 10, 11 }; + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + IAudioStreamSample *stream_sample1; + IAudioStreamSample *stream_sample2; + IAudioMediaStream *audio_stream; + IMediaSample *media_sample; + struct testfilter source; + IAudioData *audio_data1; + IAudioData *audio_data2; + IGraphBuilder *graph; + IMediaStream *stream; + HANDLE event; + HRESULT hr; + ULONG ref; + IPin *pin; + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(event != NULL, "Expected non-NULL event."); + + 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 = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(graph != NULL, "Expected non-null graph\n"); + 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_data1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER, &IID_IAudioData, (void **)&audio_data2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioData_SetBuffer(audio_data1, 6, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioData_SetBuffer(audio_data2, 6, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioMediaStream_CreateSample(audio_stream, audio_data1, 0, &stream_sample1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioMediaStream_CreateSample(audio_stream, audio_data2, 0, &stream_sample2); + 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); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + media_sample = audiostream_allocate_sample(&source, test_data, 6); + hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample2, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + media_sample = audiostream_allocate_sample(&source, test_data, 12); + hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_CompletionStatus(stream_sample2, 0, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 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 = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_Update(stream_sample1, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + media_sample = audiostream_allocate_sample(&source, test_data, 6); + hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_CompletionStatus(stream_sample1, 0, 0); + ok(hr == S_OK, "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); + + ref = IAudioStreamSample_Release(stream_sample1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAudioStreamSample_Release(stream_sample2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAudioData_Release(audio_data1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAudioData_Release(audio_data2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin); + IAudioMediaStream_Release(audio_stream); + ref = IMediaStream_Release(stream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + CloseHandle(event); +} + void test_mediastreamfilter_get_state(void) { IAMMultiMediaStream *mmstream = create_ammultimediastream(); @@ -3536,6 +3705,7 @@ START_TEST(amstream) test_audiostream_receive();
test_audiostreamsample_update(); + test_audiostreamsample_completion_status();
test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run();