amstream: Implement AMAudioStream::Receive().
Signed-off-by: Anton Baskanov <baskanov@gmail.com> Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
b90cd8b0a4
commit
1a20f9b06b
|
@ -27,6 +27,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(amstream);
|
||||||
|
|
||||||
static const WCHAR sink_id[] = L"I{A35FF56B-9FDA-11D0-8FDF-00C04FD9189D}";
|
static const WCHAR sink_id[] = L"I{A35FF56B-9FDA-11D0-8FDF-00C04FD9189D}";
|
||||||
|
|
||||||
|
struct queued_receive
|
||||||
|
{
|
||||||
|
struct list entry;
|
||||||
|
IMediaSample *sample;
|
||||||
|
DWORD length;
|
||||||
|
BYTE *pointer;
|
||||||
|
DWORD position;
|
||||||
|
};
|
||||||
|
|
||||||
struct audio_stream
|
struct audio_stream
|
||||||
{
|
{
|
||||||
IAMMediaStream IAMMediaStream_iface;
|
IAMMediaStream IAMMediaStream_iface;
|
||||||
|
@ -47,6 +56,7 @@ struct audio_stream
|
||||||
WAVEFORMATEX format;
|
WAVEFORMATEX format;
|
||||||
FILTER_STATE state;
|
FILTER_STATE state;
|
||||||
BOOL eos;
|
BOOL eos;
|
||||||
|
struct list receive_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -56,6 +66,24 @@ typedef struct {
|
||||||
IAudioData *audio_data;
|
IAudioData *audio_data;
|
||||||
} IAudioStreamSampleImpl;
|
} IAudioStreamSampleImpl;
|
||||||
|
|
||||||
|
static void remove_queued_receive(struct queued_receive *receive)
|
||||||
|
{
|
||||||
|
list_remove(&receive->entry);
|
||||||
|
IMediaSample_Release(receive->sample);
|
||||||
|
free(receive);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_receive_queue(struct audio_stream *stream)
|
||||||
|
{
|
||||||
|
while (!list_empty(&stream->receive_queue))
|
||||||
|
{
|
||||||
|
struct queued_receive *receive =
|
||||||
|
LIST_ENTRY(list_head(&stream->receive_queue), struct queued_receive, entry);
|
||||||
|
|
||||||
|
remove_queued_receive(receive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline IAudioStreamSampleImpl *impl_from_IAudioStreamSample(IAudioStreamSample *iface)
|
static inline IAudioStreamSampleImpl *impl_from_IAudioStreamSample(IAudioStreamSample *iface)
|
||||||
{
|
{
|
||||||
return CONTAINING_RECORD(iface, IAudioStreamSampleImpl, IAudioStreamSample_iface);
|
return CONTAINING_RECORD(iface, IAudioStreamSampleImpl, IAudioStreamSample_iface);
|
||||||
|
@ -348,6 +376,8 @@ static HRESULT WINAPI audio_IAMMediaStream_SetState(IAMMediaStream *iface, FILTE
|
||||||
|
|
||||||
EnterCriticalSection(&stream->cs);
|
EnterCriticalSection(&stream->cs);
|
||||||
|
|
||||||
|
if (state == State_Stopped)
|
||||||
|
flush_receive_queue(stream);
|
||||||
if (stream->state == State_Stopped)
|
if (stream->state == State_Stopped)
|
||||||
stream->eos = FALSE;
|
stream->eos = FALSE;
|
||||||
|
|
||||||
|
@ -1048,8 +1078,44 @@ static HRESULT WINAPI audio_meminput_GetAllocatorRequirements(IMemInputPin *ifac
|
||||||
|
|
||||||
static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample *sample)
|
static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample *sample)
|
||||||
{
|
{
|
||||||
FIXME("iface %p, sample %p, stub!\n", iface, sample);
|
struct audio_stream *stream = impl_from_IMemInputPin(iface);
|
||||||
return E_NOTIMPL;
|
struct queued_receive *receive;
|
||||||
|
BYTE *pointer;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
TRACE("stream %p, sample %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;
|
||||||
|
}
|
||||||
|
|
||||||
|
receive = calloc(1, sizeof(*receive));
|
||||||
|
if (!receive)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&stream->cs);
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
receive->length = IMediaSample_GetActualDataLength(sample);
|
||||||
|
receive->pointer = pointer;
|
||||||
|
receive->sample = sample;
|
||||||
|
IMediaSample_AddRef(receive->sample);
|
||||||
|
list_add_tail(&stream->receive_queue, &receive->entry);
|
||||||
|
|
||||||
|
LeaveCriticalSection(&stream->cs);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI audio_meminput_ReceiveMultiple(IMemInputPin *iface,
|
static HRESULT WINAPI audio_meminput_ReceiveMultiple(IMemInputPin *iface,
|
||||||
|
@ -1102,6 +1168,7 @@ HRESULT audio_stream_create(IMultiMediaStream *parent, const MSPID *purpose_id,
|
||||||
object->parent = parent;
|
object->parent = parent;
|
||||||
object->purpose_id = *purpose_id;
|
object->purpose_id = *purpose_id;
|
||||||
object->stream_type = stream_type;
|
object->stream_type = stream_type;
|
||||||
|
list_init(&object->receive_queue);
|
||||||
|
|
||||||
*media_stream = &object->IAMMediaStream_iface;
|
*media_stream = &object->IAMMediaStream_iface;
|
||||||
|
|
||||||
|
|
|
@ -2904,6 +2904,137 @@ void test_audiostream_end_of_stream(void)
|
||||||
ok(!ref, "Got outstanding refcount %d.\n", ref);
|
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);
|
||||||
|
ref = get_refcount(sample1);
|
||||||
|
ok(ref == 2, "Got unexpected 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);
|
||||||
|
ref = get_refcount(sample2);
|
||||||
|
ok(ref == 2, "Got unexpected 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);
|
||||||
|
ref = get_refcount(sample3);
|
||||||
|
ok(ref == 2, "Got unexpected 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)
|
void test_mediastreamfilter_get_state(void)
|
||||||
{
|
{
|
||||||
IAMMultiMediaStream *mmstream = create_ammultimediastream();
|
IAMMultiMediaStream *mmstream = create_ammultimediastream();
|
||||||
|
@ -3077,6 +3208,7 @@ START_TEST(amstream)
|
||||||
test_audiostream_receive_connection();
|
test_audiostream_receive_connection();
|
||||||
test_audiostream_set_state();
|
test_audiostream_set_state();
|
||||||
test_audiostream_end_of_stream();
|
test_audiostream_end_of_stream();
|
||||||
|
test_audiostream_receive();
|
||||||
|
|
||||||
test_mediastreamfilter_get_state();
|
test_mediastreamfilter_get_state();
|
||||||
test_mediastreamfilter_stop_pause_run();
|
test_mediastreamfilter_stop_pause_run();
|
||||||
|
|
Loading…
Reference in New Issue