wine/dlls/amstream/audiostream.c
Gijs Vermeulen 218409c2d6 amstream: Return correct media type info when enumerating AMAudioStream media types.
Signed-off-by: Gijs Vermeulen <gijsvrm@gmail.com>
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2020-08-01 10:02:16 +02:00

1402 lines
39 KiB
C

/*
* Primary audio stream
*
* Copyright 2012 Christian Costa
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#include "amstream_private.h"
#include "wine/debug.h"
#include "wine/strmbase.h"
WINE_DEFAULT_DEBUG_CHANNEL(amstream);
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;
STREAM_TIME start_time;
};
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;
REFERENCE_TIME segment_start;
BOOL eos;
BOOL flushing;
struct list receive_queue;
struct list update_queue;
};
struct audio_sample
{
IAudioStreamSample IAudioStreamSample_iface;
LONG ref;
struct audio_stream *parent;
IAudioData *audio_data;
STREAM_TIME start_time;
STREAM_TIME end_time;
HANDLE update_event;
struct list entry;
DWORD length;
BYTE *pointer;
DWORD position;
HRESULT update_hr;
};
static void remove_queued_receive(struct queued_receive *receive)
{
list_remove(&receive->entry);
IMediaSample_Release(receive->sample);
free(receive);
}
static void remove_queued_update(struct audio_sample *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)
{
struct list *entry;
while ((entry = list_head(&stream->receive_queue)))
remove_queued_receive(LIST_ENTRY(entry, struct queued_receive, entry));
}
static STREAM_TIME stream_time_from_position(struct audio_stream *stream, struct queued_receive *receive)
{
const WAVEFORMATEX *format = (WAVEFORMATEX *)stream->mt.pbFormat;
return receive->start_time + (receive->position * 10000000 + format->nAvgBytesPerSec / 2) / format->nAvgBytesPerSec;
}
static void process_update(struct audio_sample *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);
if (!sample->position)
sample->start_time = stream_time_from_position(sample->parent, receive);
receive->position += advance;
sample->position += advance;
sample->end_time = stream_time_from_position(sample->parent, receive);
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))
{
struct audio_sample *sample = LIST_ENTRY(list_head(&stream->update_queue), struct audio_sample, 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))
{
struct audio_sample *sample = LIST_ENTRY(list_head(&stream->update_queue), struct audio_sample, entry);
sample->update_hr = sample->position ? S_OK : MS_S_ENDOFSTREAM;
remove_queued_update(sample);
}
}
}
static inline struct audio_sample *impl_from_IAudioStreamSample(IAudioStreamSample *iface)
{
return CONTAINING_RECORD(iface, struct audio_sample, IAudioStreamSample_iface);
}
/*** IUnknown methods ***/
static HRESULT WINAPI audio_sample_QueryInterface(IAudioStreamSample *iface,
REFIID riid, void **ret_iface)
{
TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ret_iface);
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IStreamSample) ||
IsEqualGUID(riid, &IID_IAudioStreamSample))
{
IAudioStreamSample_AddRef(iface);
*ret_iface = iface;
return S_OK;
}
*ret_iface = NULL;
ERR("(%p)->(%s,%p),not found\n", iface, debugstr_guid(riid), ret_iface);
return E_NOINTERFACE;
}
static ULONG WINAPI audio_sample_AddRef(IAudioStreamSample *iface)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
ULONG refcount = InterlockedIncrement(&sample->ref);
TRACE("%p increasing refcount to %u.\n", sample, refcount);
return refcount;
}
static ULONG WINAPI audio_sample_Release(IAudioStreamSample *iface)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
ULONG refcount = InterlockedDecrement(&sample->ref);
TRACE("%p decreasing refcount to %u.\n", sample, refcount);
if (!refcount)
{
IAMMediaStream_Release(&sample->parent->IAMMediaStream_iface);
IAudioData_Release(sample->audio_data);
CloseHandle(sample->update_event);
HeapFree(GetProcessHeap(), 0, sample);
}
return refcount;
}
/*** IStreamSample methods ***/
static HRESULT WINAPI audio_sample_GetMediaStream(IAudioStreamSample *iface, IMediaStream **media_stream)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
TRACE("sample %p, media_stream %p.\n", iface, media_stream);
if (!media_stream)
return E_POINTER;
IAMMediaStream_AddRef(&sample->parent->IAMMediaStream_iface);
*media_stream = (IMediaStream *)&sample->parent->IAMMediaStream_iface;
return S_OK;
}
static HRESULT WINAPI audio_sample_GetSampleTimes(IAudioStreamSample *iface, STREAM_TIME *start_time,
STREAM_TIME *end_time, STREAM_TIME *current_time)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
TRACE("sample %p, start_time %p, end_time %p, current_time %p.\n", sample, start_time, end_time, current_time);
if (current_time)
IMediaStreamFilter_GetCurrentStreamTime(sample->parent->filter, current_time);
if (start_time)
*start_time = sample->start_time;
if (end_time)
*end_time = sample->end_time;
return S_OK;
}
static HRESULT WINAPI audio_sample_SetSampleTimes(IAudioStreamSample *iface, const STREAM_TIME *start_time,
const STREAM_TIME *end_time)
{
FIXME("(%p)->(%p,%p): stub\n", iface, start_time, end_time);
return E_NOTIMPL;
}
static HRESULT WINAPI audio_sample_Update(IAudioStreamSample *iface,
DWORD flags, HANDLE event, PAPCFUNC apc_func, DWORD apc_data)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
BYTE *pointer;
DWORD length;
HRESULT hr;
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 audio_sample_CompletionStatus(IAudioStreamSample *iface, DWORD flags, DWORD milliseconds)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
HRESULT hr;
TRACE("sample %p, flags %#x, milliseconds %u.\n", sample, flags, milliseconds);
if (flags)
{
FIXME("Unhandled flags %#x.\n", flags);
return E_NOTIMPL;
}
EnterCriticalSection(&sample->parent->cs);
hr = sample->update_hr;
LeaveCriticalSection(&sample->parent->cs);
return hr;
}
/*** IAudioStreamSample methods ***/
static HRESULT WINAPI audio_sample_GetAudioData(IAudioStreamSample *iface, IAudioData **audio_data)
{
struct audio_sample *sample = impl_from_IAudioStreamSample(iface);
TRACE("sample %p, audio_data %p.\n", sample, audio_data);
if (!audio_data)
return E_POINTER;
IAudioData_AddRef(sample->audio_data);
*audio_data = sample->audio_data;
return S_OK;
}
static const struct IAudioStreamSampleVtbl AudioStreamSample_Vtbl =
{
/*** IUnknown methods ***/
audio_sample_QueryInterface,
audio_sample_AddRef,
audio_sample_Release,
/*** IStreamSample methods ***/
audio_sample_GetMediaStream,
audio_sample_GetSampleTimes,
audio_sample_SetSampleTimes,
audio_sample_Update,
audio_sample_CompletionStatus,
/*** IAudioStreamSample methods ***/
audio_sample_GetAudioData
};
static HRESULT audiostreamsample_create(struct audio_stream *parent, IAudioData *audio_data, IAudioStreamSample **audio_stream_sample)
{
struct audio_sample *object;
TRACE("(%p)\n", audio_stream_sample);
object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
object->IAudioStreamSample_iface.lpVtbl = &AudioStreamSample_Vtbl;
object->ref = 1;
object->parent = parent;
IAMMediaStream_AddRef(&parent->IAMMediaStream_iface);
object->audio_data = audio_data;
IAudioData_AddRef(audio_data);
object->update_event = CreateEventW(NULL, FALSE, FALSE, NULL);
*audio_stream_sample = &object->IAudioStreamSample_iface;
return S_OK;
}
static inline struct audio_stream *impl_from_IAMMediaStream(IAMMediaStream *iface)
{
return CONTAINING_RECORD(iface, struct audio_stream, IAMMediaStream_iface);
}
/*** IUnknown methods ***/
static HRESULT WINAPI audio_IAMMediaStream_QueryInterface(IAMMediaStream *iface,
REFIID riid, void **ret_iface)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ret_iface);
if (IsEqualGUID(riid, &IID_IUnknown) ||
IsEqualGUID(riid, &IID_IMediaStream) ||
IsEqualGUID(riid, &IID_IAMMediaStream))
{
IAMMediaStream_AddRef(iface);
*ret_iface = iface;
return S_OK;
}
else if (IsEqualGUID(riid, &IID_IAudioMediaStream))
{
IAMMediaStream_AddRef(iface);
*ret_iface = &This->IAudioMediaStream_iface;
return S_OK;
}
else if (IsEqualGUID(riid, &IID_IPin))
{
IAMMediaStream_AddRef(iface);
*ret_iface = &This->IPin_iface;
return S_OK;
}
else if (IsEqualGUID(riid, &IID_IMemInputPin))
{
IAMMediaStream_AddRef(iface);
*ret_iface = &This->IMemInputPin_iface;
return S_OK;
}
ERR("(%p)->(%s,%p),not found\n", This, debugstr_guid(riid), ret_iface);
return E_NOINTERFACE;
}
static ULONG WINAPI audio_IAMMediaStream_AddRef(IAMMediaStream *iface)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p/%p)->(): new ref = %u\n", iface, This, ref);
return ref;
}
static ULONG WINAPI audio_IAMMediaStream_Release(IAMMediaStream *iface)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p/%p)->(): new ref = %u\n", iface, This, ref);
if (!ref)
{
DeleteCriticalSection(&This->cs);
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
/*** IMediaStream methods ***/
static HRESULT WINAPI audio_IAMMediaStream_GetMultiMediaStream(IAMMediaStream *iface,
IMultiMediaStream **mmstream)
{
struct audio_stream *stream = impl_from_IAMMediaStream(iface);
TRACE("stream %p, mmstream %p.\n", stream, mmstream);
if (!mmstream)
return E_POINTER;
if (stream->parent)
IMultiMediaStream_AddRef(stream->parent);
*mmstream = stream->parent;
return S_OK;
}
static HRESULT WINAPI audio_IAMMediaStream_GetInformation(IAMMediaStream *iface,
MSPID *purpose_id, STREAM_TYPE *type)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
TRACE("(%p/%p)->(%p,%p)\n", This, iface, purpose_id, type);
if (purpose_id)
*purpose_id = This->purpose_id;
if (type)
*type = This->stream_type;
return S_OK;
}
static HRESULT WINAPI audio_IAMMediaStream_SetSameFormat(IAMMediaStream *iface,
IMediaStream *pStreamThatHasDesiredFormat, DWORD flags)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
FIXME("(%p/%p)->(%p,%x) stub!\n", This, iface, pStreamThatHasDesiredFormat, flags);
return S_FALSE;
}
static HRESULT WINAPI audio_IAMMediaStream_AllocateSample(IAMMediaStream *iface,
DWORD flags, IStreamSample **sample)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
FIXME("(%p/%p)->(%x,%p) stub!\n", This, iface, flags, sample);
return S_FALSE;
}
static HRESULT WINAPI audio_IAMMediaStream_CreateSharedSample(IAMMediaStream *iface,
IStreamSample *existing_sample, DWORD flags, IStreamSample **sample)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
FIXME("(%p/%p)->(%p,%x,%p) stub!\n", This, iface, existing_sample, flags, sample);
return S_FALSE;
}
static HRESULT WINAPI audio_IAMMediaStream_SendEndOfStream(IAMMediaStream *iface, DWORD flags)
{
struct audio_stream *This = impl_from_IAMMediaStream(iface);
FIXME("(%p/%p)->(%x) stub!\n", This, iface, flags);
return S_FALSE;
}
/*** IAMMediaStream methods ***/
static HRESULT WINAPI audio_IAMMediaStream_Initialize(IAMMediaStream *iface, IUnknown *source_object, DWORD flags,
REFMSPID purpose_id, const STREAM_TYPE stream_type)
{
struct audio_stream *stream = impl_from_IAMMediaStream(iface);
TRACE("stream %p, source_object %p, flags %x, purpose_id %s, stream_type %u.\n", stream, source_object, flags,
debugstr_guid(purpose_id), stream_type);
if (!purpose_id)
return E_POINTER;
if (source_object)
FIXME("Specifying a stream object is not yet supported.\n");
if (flags & AMMSF_CREATEPEER)
FIXME("AMMSF_CREATEPEER is not yet supported.\n");
stream->purpose_id = *purpose_id;
stream->stream_type = stream_type;
return S_OK;
}
static HRESULT WINAPI audio_IAMMediaStream_SetState(IAMMediaStream *iface, FILTER_STATE state)
{
struct audio_stream *stream = impl_from_IAMMediaStream(iface);
TRACE("stream %p, state %u.\n", stream, state);
EnterCriticalSection(&stream->cs);
if (state == State_Stopped)
flush_receive_queue(stream);
if (stream->state == State_Stopped)
stream->eos = FALSE;
stream->state = state;
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_IAMMediaStream_JoinAMMultiMediaStream(IAMMediaStream *iface,
IAMMultiMediaStream *mmstream)
{
struct audio_stream *stream = impl_from_IAMMediaStream(iface);
TRACE("stream %p, mmstream %p.\n", stream, mmstream);
stream->parent = (IMultiMediaStream *)mmstream;
return S_OK;
}
static HRESULT WINAPI audio_IAMMediaStream_JoinFilter(IAMMediaStream *iface, IMediaStreamFilter *filter)
{
struct audio_stream *stream = impl_from_IAMMediaStream(iface);
TRACE("stream %p, filter %p.\n", stream, filter);
stream->filter = filter;
return S_OK;
}
static HRESULT WINAPI audio_IAMMediaStream_JoinFilterGraph(IAMMediaStream *iface, IFilterGraph *filtergraph)
{
struct audio_stream *stream = impl_from_IAMMediaStream(iface);
TRACE("stream %p, filtergraph %p.\n", stream, filtergraph);
return S_OK;
}
static const struct IAMMediaStreamVtbl audio_IAMMediaStream_vtbl =
{
audio_IAMMediaStream_QueryInterface,
audio_IAMMediaStream_AddRef,
audio_IAMMediaStream_Release,
audio_IAMMediaStream_GetMultiMediaStream,
audio_IAMMediaStream_GetInformation,
audio_IAMMediaStream_SetSameFormat,
audio_IAMMediaStream_AllocateSample,
audio_IAMMediaStream_CreateSharedSample,
audio_IAMMediaStream_SendEndOfStream,
audio_IAMMediaStream_Initialize,
audio_IAMMediaStream_SetState,
audio_IAMMediaStream_JoinAMMultiMediaStream,
audio_IAMMediaStream_JoinFilter,
audio_IAMMediaStream_JoinFilterGraph,
};
static inline struct audio_stream *impl_from_IAudioMediaStream(IAudioMediaStream *iface)
{
return CONTAINING_RECORD(iface, struct audio_stream, IAudioMediaStream_iface);
}
/*** IUnknown methods ***/
static HRESULT WINAPI audio_IAudioMediaStream_QueryInterface(IAudioMediaStream *iface,
REFIID riid, void **ret_iface)
{
struct audio_stream *This = impl_from_IAudioMediaStream(iface);
TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ret_iface);
return IAMMediaStream_QueryInterface(&This->IAMMediaStream_iface, riid, ret_iface);
}
static ULONG WINAPI audio_IAudioMediaStream_AddRef(IAudioMediaStream *iface)
{
struct audio_stream *This = impl_from_IAudioMediaStream(iface);
TRACE("(%p/%p)\n", iface, This);
return IAMMediaStream_AddRef(&This->IAMMediaStream_iface);
}
static ULONG WINAPI audio_IAudioMediaStream_Release(IAudioMediaStream *iface)
{
struct audio_stream *This = impl_from_IAudioMediaStream(iface);
TRACE("(%p/%p)\n", iface, This);
return IAMMediaStream_Release(&This->IAMMediaStream_iface);
}
static HRESULT WINAPI audio_IAudioMediaStream_GetMultiMediaStream(IAudioMediaStream *iface,
IMultiMediaStream **mmstream)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
return IAMMediaStream_GetMultiMediaStream(&stream->IAMMediaStream_iface, mmstream);
}
static HRESULT WINAPI audio_IAudioMediaStream_GetInformation(IAudioMediaStream *iface,
MSPID *purpose_id, STREAM_TYPE *type)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
return IAMMediaStream_GetInformation(&stream->IAMMediaStream_iface, purpose_id, type);
}
static HRESULT WINAPI audio_IAudioMediaStream_SetSameFormat(IAudioMediaStream *iface,
IMediaStream *other, DWORD flags)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
return IAMMediaStream_SetSameFormat(&stream->IAMMediaStream_iface, other, flags);
}
static HRESULT WINAPI audio_IAudioMediaStream_AllocateSample(IAudioMediaStream *iface,
DWORD flags, IStreamSample **sample)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
return IAMMediaStream_AllocateSample(&stream->IAMMediaStream_iface, flags, sample);
}
static HRESULT WINAPI audio_IAudioMediaStream_CreateSharedSample(IAudioMediaStream *iface,
IStreamSample *existing_sample, DWORD flags, IStreamSample **sample)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
return IAMMediaStream_CreateSharedSample(&stream->IAMMediaStream_iface, existing_sample, flags, sample);
}
static HRESULT WINAPI audio_IAudioMediaStream_SendEndOfStream(IAudioMediaStream *iface, DWORD flags)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
return IAMMediaStream_SendEndOfStream(&stream->IAMMediaStream_iface, flags);
}
/*** IAudioMediaStream methods ***/
static HRESULT WINAPI audio_IAudioMediaStream_GetFormat(IAudioMediaStream *iface, WAVEFORMATEX *format)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
TRACE("stream %p, format %p.\n", stream, format);
if (!format)
return E_POINTER;
EnterCriticalSection(&stream->cs);
if (!stream->peer)
{
LeaveCriticalSection(&stream->cs);
return MS_E_NOSTREAM;
}
*format = *(WAVEFORMATEX *)stream->mt.pbFormat;
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_IAudioMediaStream_SetFormat(IAudioMediaStream *iface, const WAVEFORMATEX *format)
{
struct audio_stream *stream = impl_from_IAudioMediaStream(iface);
TRACE("stream %p, format %p.\n", stream, format);
if (!format)
return E_POINTER;
if (format->wFormatTag != WAVE_FORMAT_PCM)
return E_INVALIDARG;
EnterCriticalSection(&stream->cs);
if ((stream->peer && memcmp(format, stream->mt.pbFormat, sizeof(WAVEFORMATEX)))
|| (stream->format.wFormatTag && memcmp(format, &stream->format, sizeof(WAVEFORMATEX))))
{
LeaveCriticalSection(&stream->cs);
return E_INVALIDARG;
}
stream->format = *format;
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_IAudioMediaStream_CreateSample(IAudioMediaStream *iface, IAudioData *audio_data,
DWORD flags, IAudioStreamSample **sample)
{
struct audio_stream *This = impl_from_IAudioMediaStream(iface);
TRACE("(%p/%p)->(%p,%u,%p)\n", iface, This, audio_data, flags, sample);
if (!audio_data)
return E_POINTER;
return audiostreamsample_create(This, audio_data, sample);
}
static const struct IAudioMediaStreamVtbl audio_IAudioMediaStream_vtbl =
{
audio_IAudioMediaStream_QueryInterface,
audio_IAudioMediaStream_AddRef,
audio_IAudioMediaStream_Release,
audio_IAudioMediaStream_GetMultiMediaStream,
audio_IAudioMediaStream_GetInformation,
audio_IAudioMediaStream_SetSameFormat,
audio_IAudioMediaStream_AllocateSample,
audio_IAudioMediaStream_CreateSharedSample,
audio_IAudioMediaStream_SendEndOfStream,
audio_IAudioMediaStream_GetFormat,
audio_IAudioMediaStream_SetFormat,
audio_IAudioMediaStream_CreateSample,
};
struct enum_media_types
{
IEnumMediaTypes IEnumMediaTypes_iface;
LONG refcount;
unsigned int index;
};
static const IEnumMediaTypesVtbl enum_media_types_vtbl;
static struct enum_media_types *impl_from_IEnumMediaTypes(IEnumMediaTypes *iface)
{
return CONTAINING_RECORD(iface, struct enum_media_types, IEnumMediaTypes_iface);
}
static HRESULT WINAPI enum_media_types_QueryInterface(IEnumMediaTypes *iface, REFIID iid, void **out)
{
TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IEnumMediaTypes))
{
IEnumMediaTypes_AddRef(iface);
*out = iface;
return S_OK;
}
WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
*out = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI enum_media_types_AddRef(IEnumMediaTypes *iface)
{
struct enum_media_types *enum_media_types = impl_from_IEnumMediaTypes(iface);
ULONG refcount = InterlockedIncrement(&enum_media_types->refcount);
TRACE("%p increasing refcount to %u.\n", enum_media_types, refcount);
return refcount;
}
static ULONG WINAPI enum_media_types_Release(IEnumMediaTypes *iface)
{
struct enum_media_types *enum_media_types = impl_from_IEnumMediaTypes(iface);
ULONG refcount = InterlockedDecrement(&enum_media_types->refcount);
TRACE("%p decreasing refcount to %u.\n", enum_media_types, refcount);
if (!refcount)
heap_free(enum_media_types);
return refcount;
}
static HRESULT WINAPI enum_media_types_Next(IEnumMediaTypes *iface, ULONG count, AM_MEDIA_TYPE **mts, ULONG *ret_count)
{
struct enum_media_types *enum_media_types = impl_from_IEnumMediaTypes(iface);
static const WAVEFORMATEX wfx =
{
.wFormatTag = WAVE_FORMAT_PCM,
.nChannels = 1,
.nSamplesPerSec = 11025,
.nAvgBytesPerSec = 11025 * 2,
.nBlockAlign = 2,
.wBitsPerSample = 16,
.cbSize = 0,
};
TRACE("iface %p, count %u, mts %p, ret_count %p.\n", iface, count, mts, ret_count);
if (!ret_count)
return E_POINTER;
if (count && !enum_media_types->index)
{
mts[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
memset(mts[0], 0, sizeof(AM_MEDIA_TYPE));
mts[0]->majortype = MEDIATYPE_Audio;
mts[0]->subtype = GUID_NULL;
mts[0]->bFixedSizeSamples = TRUE;
mts[0]->bTemporalCompression = FALSE;
mts[0]->lSampleSize = 2;
mts[0]->formattype = FORMAT_WaveFormatEx;
mts[0]->cbFormat = sizeof(WAVEFORMATEX);
mts[0]->pbFormat = CoTaskMemAlloc(sizeof(WAVEFORMATEX));
memcpy(mts[0]->pbFormat, &wfx, sizeof(WAVEFORMATEX));
++enum_media_types->index;
*ret_count = 1;
return count == 1 ? S_OK : S_FALSE;
}
*ret_count = 0;
return count ? S_FALSE : S_OK;
}
static HRESULT WINAPI enum_media_types_Skip(IEnumMediaTypes *iface, ULONG count)
{
struct enum_media_types *enum_media_types = impl_from_IEnumMediaTypes(iface);
TRACE("iface %p, count %u.\n", iface, count);
enum_media_types->index += count;
return S_OK;
}
static HRESULT WINAPI enum_media_types_Reset(IEnumMediaTypes *iface)
{
struct enum_media_types *enum_media_types = impl_from_IEnumMediaTypes(iface);
TRACE("iface %p.\n", iface);
enum_media_types->index = 0;
return S_OK;
}
static HRESULT WINAPI enum_media_types_Clone(IEnumMediaTypes *iface, IEnumMediaTypes **out)
{
struct enum_media_types *enum_media_types = impl_from_IEnumMediaTypes(iface);
struct enum_media_types *object;
TRACE("iface %p, out %p.\n", iface, out);
if (!(object = heap_alloc(sizeof(*object))))
return E_OUTOFMEMORY;
object->IEnumMediaTypes_iface.lpVtbl = &enum_media_types_vtbl;
object->refcount = 1;
object->index = enum_media_types->index;
*out = &object->IEnumMediaTypes_iface;
return S_OK;
}
static const IEnumMediaTypesVtbl enum_media_types_vtbl =
{
enum_media_types_QueryInterface,
enum_media_types_AddRef,
enum_media_types_Release,
enum_media_types_Next,
enum_media_types_Skip,
enum_media_types_Reset,
enum_media_types_Clone,
};
static inline struct audio_stream *impl_from_IPin(IPin *iface)
{
return CONTAINING_RECORD(iface, struct audio_stream, IPin_iface);
}
static HRESULT WINAPI audio_sink_QueryInterface(IPin *iface, REFIID iid, void **out)
{
struct audio_stream *stream = impl_from_IPin(iface);
return IAMMediaStream_QueryInterface(&stream->IAMMediaStream_iface, iid, out);
}
static ULONG WINAPI audio_sink_AddRef(IPin *iface)
{
struct audio_stream *stream = impl_from_IPin(iface);
return IAMMediaStream_AddRef(&stream->IAMMediaStream_iface);
}
static ULONG WINAPI audio_sink_Release(IPin *iface)
{
struct audio_stream *stream = impl_from_IPin(iface);
return IAMMediaStream_Release(&stream->IAMMediaStream_iface);
}
static HRESULT WINAPI audio_sink_Connect(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
WARN("iface %p, peer %p, mt %p, unexpected call!\n", iface, peer, mt);
return E_UNEXPECTED;
}
static HRESULT WINAPI audio_sink_ReceiveConnection(IPin *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
{
struct audio_stream *stream = impl_from_IPin(iface);
PIN_DIRECTION dir;
TRACE("stream %p, peer %p, mt %p.\n", stream, peer, mt);
if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio)
|| !IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx)
|| mt->cbFormat < sizeof(WAVEFORMATEX))
return VFW_E_TYPE_NOT_ACCEPTED;
if (((const WAVEFORMATEX *)mt->pbFormat)->wFormatTag != WAVE_FORMAT_PCM)
return E_INVALIDARG;
EnterCriticalSection(&stream->cs);
if (stream->peer)
{
LeaveCriticalSection(&stream->cs);
return VFW_E_ALREADY_CONNECTED;
}
IPin_QueryDirection(peer, &dir);
if (dir != PINDIR_OUTPUT)
{
WARN("Rejecting connection from input pin.\n");
LeaveCriticalSection(&stream->cs);
return VFW_E_INVALID_DIRECTION;
}
if (stream->format.wFormatTag && memcmp(mt->pbFormat, &stream->format, sizeof(WAVEFORMATEX)))
{
LeaveCriticalSection(&stream->cs);
return E_INVALIDARG;
}
CopyMediaType(&stream->mt, mt);
IPin_AddRef(stream->peer = peer);
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_sink_Disconnect(IPin *iface)
{
struct audio_stream *stream = impl_from_IPin(iface);
TRACE("stream %p.\n", stream);
EnterCriticalSection(&stream->cs);
if (!stream->peer)
{
LeaveCriticalSection(&stream->cs);
return S_FALSE;
}
IPin_Release(stream->peer);
stream->peer = NULL;
FreeMediaType(&stream->mt);
memset(&stream->mt, 0, sizeof(AM_MEDIA_TYPE));
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_sink_ConnectedTo(IPin *iface, IPin **peer)
{
struct audio_stream *stream = impl_from_IPin(iface);
HRESULT hr;
TRACE("stream %p, peer %p.\n", stream, peer);
EnterCriticalSection(&stream->cs);
if (stream->peer)
{
IPin_AddRef(*peer = stream->peer);
hr = S_OK;
}
else
{
*peer = NULL;
hr = VFW_E_NOT_CONNECTED;
}
LeaveCriticalSection(&stream->cs);
return hr;
}
static HRESULT WINAPI audio_sink_ConnectionMediaType(IPin *iface, AM_MEDIA_TYPE *mt)
{
struct audio_stream *stream = impl_from_IPin(iface);
HRESULT hr;
TRACE("stream %p, mt %p.\n", stream, mt);
EnterCriticalSection(&stream->cs);
if (stream->peer)
{
CopyMediaType(mt, &stream->mt);
hr = S_OK;
}
else
{
memset(mt, 0, sizeof(AM_MEDIA_TYPE));
hr = VFW_E_NOT_CONNECTED;
}
LeaveCriticalSection(&stream->cs);
return hr;
}
static HRESULT WINAPI audio_sink_QueryPinInfo(IPin *iface, PIN_INFO *info)
{
struct audio_stream *stream = impl_from_IPin(iface);
TRACE("stream %p, info %p.\n", stream, info);
IBaseFilter_AddRef(info->pFilter = (IBaseFilter *)stream->filter);
info->dir = PINDIR_INPUT;
wcscpy(info->achName, sink_id);
return S_OK;
}
static HRESULT WINAPI audio_sink_QueryDirection(IPin *iface, PIN_DIRECTION *dir)
{
TRACE("iface %p, dir %p.\n", iface, dir);
*dir = PINDIR_INPUT;
return S_OK;
}
static HRESULT WINAPI audio_sink_QueryId(IPin *iface, WCHAR **id)
{
TRACE("iface %p, id %p.\n", iface, id);
if (!(*id = CoTaskMemAlloc(sizeof(sink_id))))
return E_OUTOFMEMORY;
wcscpy(*id, sink_id);
return S_OK;
}
static HRESULT WINAPI audio_sink_QueryAccept(IPin *iface, const AM_MEDIA_TYPE *mt)
{
TRACE("iface %p, mt %p.\n", iface, mt);
return E_NOTIMPL;
}
static HRESULT WINAPI audio_sink_EnumMediaTypes(IPin *iface, IEnumMediaTypes **enum_media_types)
{
struct enum_media_types *object;
TRACE("iface %p, enum_media_types %p.\n", iface, enum_media_types);
if (!enum_media_types)
return E_POINTER;
if (!(object = heap_alloc(sizeof(*object))))
return E_OUTOFMEMORY;
object->IEnumMediaTypes_iface.lpVtbl = &enum_media_types_vtbl;
object->refcount = 1;
object->index = 0;
*enum_media_types = &object->IEnumMediaTypes_iface;
return S_OK;
}
static HRESULT WINAPI audio_sink_QueryInternalConnections(IPin *iface, IPin **pins, ULONG *count)
{
TRACE("iface %p, pins %p, count %p.\n", iface, pins, count);
return E_NOTIMPL;
}
static HRESULT WINAPI audio_sink_EndOfStream(IPin *iface)
{
struct audio_stream *stream = impl_from_IPin(iface);
TRACE("stream %p.\n", stream);
EnterCriticalSection(&stream->cs);
if (stream->eos || stream->flushing)
{
LeaveCriticalSection(&stream->cs);
return E_FAIL;
}
stream->eos = TRUE;
process_updates(stream);
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_sink_BeginFlush(IPin *iface)
{
struct audio_stream *stream = impl_from_IPin(iface);
TRACE("stream %p.\n", stream);
EnterCriticalSection(&stream->cs);
stream->flushing = TRUE;
stream->eos = FALSE;
flush_receive_queue(stream);
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_sink_EndFlush(IPin *iface)
{
struct audio_stream *stream = impl_from_IPin(iface);
TRACE("stream %p.\n", stream);
EnterCriticalSection(&stream->cs);
stream->flushing = FALSE;
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_sink_NewSegment(IPin *iface, REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
{
struct audio_stream *stream = impl_from_IPin(iface);
TRACE("stream %p, start %s, stop %s, rate %0.16e\n",
stream, wine_dbgstr_longlong(start), wine_dbgstr_longlong(stop), rate);
EnterCriticalSection(&stream->cs);
stream->segment_start = start;
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static const IPinVtbl audio_sink_vtbl =
{
audio_sink_QueryInterface,
audio_sink_AddRef,
audio_sink_Release,
audio_sink_Connect,
audio_sink_ReceiveConnection,
audio_sink_Disconnect,
audio_sink_ConnectedTo,
audio_sink_ConnectionMediaType,
audio_sink_QueryPinInfo,
audio_sink_QueryDirection,
audio_sink_QueryId,
audio_sink_QueryAccept,
audio_sink_EnumMediaTypes,
audio_sink_QueryInternalConnections,
audio_sink_EndOfStream,
audio_sink_BeginFlush,
audio_sink_EndFlush,
audio_sink_NewSegment,
};
static inline struct audio_stream *impl_from_IMemInputPin(IMemInputPin *iface)
{
return CONTAINING_RECORD(iface, struct audio_stream, IMemInputPin_iface);
}
static HRESULT WINAPI audio_meminput_QueryInterface(IMemInputPin *iface, REFIID iid, void **out)
{
struct audio_stream *stream = impl_from_IMemInputPin(iface);
return IAMMediaStream_QueryInterface(&stream->IAMMediaStream_iface, iid, out);
}
static ULONG WINAPI audio_meminput_AddRef(IMemInputPin *iface)
{
struct audio_stream *stream = impl_from_IMemInputPin(iface);
return IAMMediaStream_AddRef(&stream->IAMMediaStream_iface);
}
static ULONG WINAPI audio_meminput_Release(IMemInputPin *iface)
{
struct audio_stream *stream = impl_from_IMemInputPin(iface);
return IAMMediaStream_Release(&stream->IAMMediaStream_iface);
}
static HRESULT WINAPI audio_meminput_GetAllocator(IMemInputPin *iface, IMemAllocator **allocator)
{
struct audio_stream *stream = impl_from_IMemInputPin(iface);
TRACE("stream %p, allocator %p.\n", stream, allocator);
if (stream->allocator)
{
IMemAllocator_AddRef(*allocator = stream->allocator);
return S_OK;
}
*allocator = NULL;
return VFW_E_NO_ALLOCATOR;
}
static HRESULT WINAPI audio_meminput_NotifyAllocator(IMemInputPin *iface, IMemAllocator *allocator, BOOL readonly)
{
struct audio_stream *stream = impl_from_IMemInputPin(iface);
TRACE("stream %p, allocator %p, readonly %d.\n", stream, allocator, readonly);
if (!allocator)
return E_POINTER;
if (allocator)
IMemAllocator_AddRef(allocator);
if (stream->allocator)
IMemAllocator_Release(stream->allocator);
stream->allocator = allocator;
return S_OK;
}
static HRESULT WINAPI audio_meminput_GetAllocatorRequirements(IMemInputPin *iface, ALLOCATOR_PROPERTIES *props)
{
TRACE("iface %p, props %p.\n", iface, props);
return E_NOTIMPL;
}
static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample *sample)
{
struct audio_stream *stream = impl_from_IMemInputPin(iface);
struct queued_receive *receive;
REFERENCE_TIME start_time = 0;
REFERENCE_TIME end_time = 0;
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;
}
if (stream->flushing)
{
LeaveCriticalSection(&stream->cs);
return S_FALSE;
}
hr = IMediaSample_GetPointer(sample, &pointer);
if (FAILED(hr))
{
LeaveCriticalSection(&stream->cs);
return hr;
}
IMediaSample_GetTime(sample, &start_time, &end_time);
receive = calloc(1, sizeof(*receive));
if (!receive)
{
LeaveCriticalSection(&stream->cs);
return E_OUTOFMEMORY;
}
receive->length = IMediaSample_GetActualDataLength(sample);
receive->pointer = pointer;
receive->sample = sample;
receive->start_time = start_time + stream->segment_start;
IMediaSample_AddRef(receive->sample);
list_add_tail(&stream->receive_queue, &receive->entry);
process_updates(stream);
LeaveCriticalSection(&stream->cs);
return S_OK;
}
static HRESULT WINAPI audio_meminput_ReceiveMultiple(IMemInputPin *iface,
IMediaSample **samples, LONG count, LONG *processed)
{
FIXME("iface %p, samples %p, count %u, processed %p, stub!\n", iface, samples, count, processed);
return E_NOTIMPL;
}
static HRESULT WINAPI audio_meminput_ReceiveCanBlock(IMemInputPin *iface)
{
TRACE("iface %p.\n", iface);
return S_OK;
}
static const IMemInputPinVtbl audio_meminput_vtbl =
{
audio_meminput_QueryInterface,
audio_meminput_AddRef,
audio_meminput_Release,
audio_meminput_GetAllocator,
audio_meminput_NotifyAllocator,
audio_meminput_GetAllocatorRequirements,
audio_meminput_Receive,
audio_meminput_ReceiveMultiple,
audio_meminput_ReceiveCanBlock,
};
HRESULT audio_stream_create(IUnknown *outer, void **out)
{
struct audio_stream *object;
if (outer)
return CLASS_E_NOAGGREGATION;
object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
if (!object)
return E_OUTOFMEMORY;
object->IAMMediaStream_iface.lpVtbl = &audio_IAMMediaStream_vtbl;
object->IAudioMediaStream_iface.lpVtbl = &audio_IAudioMediaStream_vtbl;
object->IMemInputPin_iface.lpVtbl = &audio_meminput_vtbl;
object->IPin_iface.lpVtbl = &audio_sink_vtbl;
object->ref = 1;
InitializeCriticalSection(&object->cs);
list_init(&object->receive_queue);
list_init(&object->update_queue);
TRACE("Created audio stream %p.\n", object);
*out = &object->IAMMediaStream_iface;
return S_OK;
}