mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-01 06:06:13 +00:00
a103368917
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>
578 lines
17 KiB
C
578 lines
17 KiB
C
/*
|
|
* Multimedia stream object
|
|
*
|
|
* Copyright 2004, 2012 Christian Costa
|
|
* Copyright 2006 Ivan Leo Puoti
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
|
|
#include "amstream_private.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(amstream);
|
|
|
|
struct multimedia_stream
|
|
{
|
|
IAMMultiMediaStream IAMMultiMediaStream_iface;
|
|
LONG ref;
|
|
IGraphBuilder *graph;
|
|
IMediaSeeking* media_seeking;
|
|
IMediaControl* media_control;
|
|
IMediaStreamFilter *filter;
|
|
IPin* ipin;
|
|
BOOL initialized;
|
|
STREAM_TYPE type;
|
|
OAEVENT event;
|
|
STREAM_STATE state;
|
|
};
|
|
|
|
static inline struct multimedia_stream *impl_from_IAMMultiMediaStream(IAMMultiMediaStream *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct multimedia_stream, IAMMultiMediaStream_iface);
|
|
}
|
|
|
|
/*** IUnknown methods ***/
|
|
static HRESULT WINAPI multimedia_stream_QueryInterface(IAMMultiMediaStream *iface,
|
|
REFIID riid, void **ppvObject)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
|
|
|
|
if (IsEqualGUID(riid, &IID_IUnknown) ||
|
|
IsEqualGUID(riid, &IID_IMultiMediaStream) ||
|
|
IsEqualGUID(riid, &IID_IAMMultiMediaStream))
|
|
{
|
|
IAMMultiMediaStream_AddRef(iface);
|
|
*ppvObject = iface;
|
|
return S_OK;
|
|
}
|
|
|
|
ERR("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI multimedia_stream_AddRef(IAMMultiMediaStream *iface)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("(%p/%p)\n", iface, This);
|
|
|
|
return InterlockedIncrement(&This->ref);
|
|
}
|
|
|
|
static ULONG WINAPI multimedia_stream_Release(IAMMultiMediaStream *iface)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p/%p)\n", iface, This);
|
|
|
|
if (!ref)
|
|
{
|
|
if (This->ipin)
|
|
IPin_Release(This->ipin);
|
|
IMediaStreamFilter_Release(This->filter);
|
|
IMediaStreamFilter_Release(This->filter);
|
|
if (This->media_seeking)
|
|
IMediaSeeking_Release(This->media_seeking);
|
|
if (This->media_control)
|
|
IMediaControl_Release(This->media_control);
|
|
if (This->graph)
|
|
IGraphBuilder_Release(This->graph);
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
/*** IMultiMediaStream methods ***/
|
|
static HRESULT WINAPI multimedia_stream_GetInformation(IAMMultiMediaStream *iface,
|
|
DWORD *pdwFlags, STREAM_TYPE *pStreamType)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
FIXME("(%p/%p)->(%p,%p) stub!\n", This, iface, pdwFlags, pStreamType);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetMediaStream(IAMMultiMediaStream *iface,
|
|
REFMSPID id, IMediaStream **stream)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("mmstream %p, id %s, stream %p.\n", mmstream, debugstr_guid(id), stream);
|
|
|
|
return IMediaStreamFilter_GetMediaStream(mmstream->filter, id, stream);
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_EnumMediaStreams(IAMMultiMediaStream *iface,
|
|
LONG index, IMediaStream **stream)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("mmstream %p, index %d, stream %p.\n", mmstream, index, stream);
|
|
|
|
return IMediaStreamFilter_EnumMediaStreams(mmstream->filter, index, stream);
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetState(IAMMultiMediaStream *iface, STREAM_STATE *state)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("mmstream %p, state %p.\n", mmstream, state);
|
|
|
|
*state = mmstream->state;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_SetState(IAMMultiMediaStream *iface, STREAM_STATE new_state)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
TRACE("(%p/%p)->(%u)\n", This, iface, new_state);
|
|
|
|
if (new_state == STREAMSTATE_RUN)
|
|
{
|
|
hr = IMediaControl_Run(This->media_control);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
FILTER_STATE state;
|
|
IMediaControl_GetState(This->media_control, INFINITE, (OAFilterState *)&state);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else if (new_state == STREAMSTATE_STOP)
|
|
hr = IMediaControl_Stop(This->media_control);
|
|
|
|
if (SUCCEEDED(hr))
|
|
This->state = new_state;
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetTime(IAMMultiMediaStream *iface, STREAM_TIME *time)
|
|
{
|
|
struct multimedia_stream *stream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("stream %p, time %p.\n", stream, time);
|
|
|
|
return IMediaStreamFilter_GetCurrentStreamTime(stream->filter, time);
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetDuration(IAMMultiMediaStream *iface, STREAM_TIME *duration)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("mmstream %p, duration %p.\n", mmstream, duration);
|
|
|
|
if (!mmstream->media_seeking)
|
|
return E_NOINTERFACE;
|
|
|
|
if (IMediaSeeking_GetDuration(mmstream->media_seeking, duration) != S_OK)
|
|
return S_FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_Seek(IAMMultiMediaStream *iface, STREAM_TIME seek_time)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("(%p/%p)->(%s)\n", This, iface, wine_dbgstr_longlong(seek_time));
|
|
|
|
return IMediaSeeking_SetPositions(This->media_seeking, &seek_time, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetEndOfStream(IAMMultiMediaStream *iface, HANDLE *phEOS)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
FIXME("(%p/%p)->(%p) stub!\n", This, iface, phEOS);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT create_graph(struct multimedia_stream *mmstream, IGraphBuilder *graph)
|
|
{
|
|
IMediaEventEx *eventsrc;
|
|
HRESULT hr;
|
|
|
|
if (graph)
|
|
IGraphBuilder_AddRef(mmstream->graph = graph);
|
|
else if (FAILED(hr = CoCreateInstance(&CLSID_FilterGraph, NULL,
|
|
CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **)&mmstream->graph)))
|
|
return hr;
|
|
|
|
hr = IGraphBuilder_QueryInterface(mmstream->graph, &IID_IMediaSeeking, (void **)&mmstream->media_seeking);
|
|
if (SUCCEEDED(hr))
|
|
hr = IGraphBuilder_QueryInterface(mmstream->graph, &IID_IMediaControl, (void **)&mmstream->media_control);
|
|
if (SUCCEEDED(hr))
|
|
hr = IGraphBuilder_AddFilter(mmstream->graph, (IBaseFilter *)mmstream->filter, L"MediaStreamFilter");
|
|
if (SUCCEEDED(hr))
|
|
hr = IGraphBuilder_QueryInterface(mmstream->graph, &IID_IMediaEventEx, (void **)&eventsrc);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = IMediaEventEx_GetEventHandle(eventsrc, &mmstream->event);
|
|
if (SUCCEEDED(hr))
|
|
hr = IMediaEventEx_SetNotifyFlags(eventsrc, AM_MEDIAEVENT_NONOTIFY);
|
|
IMediaEventEx_Release(eventsrc);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (mmstream->media_seeking)
|
|
IMediaSeeking_Release(mmstream->media_seeking);
|
|
mmstream->media_seeking = NULL;
|
|
if (mmstream->media_control)
|
|
IMediaControl_Release(mmstream->media_control);
|
|
mmstream->media_control = NULL;
|
|
if (mmstream->graph)
|
|
IGraphBuilder_Release(mmstream->graph);
|
|
mmstream->graph = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_Initialize(IAMMultiMediaStream *iface,
|
|
STREAM_TYPE type, DWORD flags, IGraphBuilder *graph)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
HRESULT hr;
|
|
|
|
TRACE("mmstream %p, type %u, flags %#x, graph %p.\n", mmstream, type, flags, graph);
|
|
|
|
if (graph && mmstream->graph)
|
|
{
|
|
WARN("Graph already initialized, returning E_INVALIDARG.\n");
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (mmstream->initialized && type != mmstream->type)
|
|
{
|
|
WARN("Attempt to change type from %u, returning E_INVALIDARG.\n", mmstream->type);
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (graph && FAILED(hr = create_graph(mmstream, graph)))
|
|
return hr;
|
|
|
|
mmstream->type = type;
|
|
mmstream->initialized = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetFilterGraph(IAMMultiMediaStream *iface, IGraphBuilder **graph)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("mmstream %p, graph %p.\n", mmstream, graph);
|
|
|
|
if (!graph)
|
|
return E_POINTER;
|
|
|
|
if (mmstream->graph)
|
|
IGraphBuilder_AddRef(*graph = mmstream->graph);
|
|
else
|
|
*graph = NULL;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_GetFilter(IAMMultiMediaStream *iface,
|
|
IMediaStreamFilter **filter)
|
|
{
|
|
struct multimedia_stream *mmstream = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
TRACE("mmstream %p, filter %p.\n", mmstream, filter);
|
|
|
|
if (!filter)
|
|
return E_POINTER;
|
|
|
|
IMediaStreamFilter_AddRef(*filter = mmstream->filter);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static void add_stream(struct multimedia_stream *mmstream, IAMMediaStream *stream, IMediaStream **ret_stream)
|
|
{
|
|
IMediaStreamFilter_AddMediaStream(mmstream->filter, stream);
|
|
IAMMediaStream_JoinAMMultiMediaStream(stream, &mmstream->IAMMultiMediaStream_iface);
|
|
if (ret_stream)
|
|
{
|
|
*ret_stream = (IMediaStream *)stream;
|
|
IMediaStream_AddRef(*ret_stream);
|
|
}
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_AddMediaStream(IAMMultiMediaStream *iface,
|
|
IUnknown *stream_object, const MSPID *PurposeId, DWORD dwFlags, IMediaStream **ret_stream)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
HRESULT hr;
|
|
IAMMediaStream* pStream;
|
|
IMediaStream *stream;
|
|
|
|
TRACE("mmstream %p, stream_object %p, id %s, flags %#x, ret_stream %p.\n",
|
|
This, stream_object, debugstr_guid(PurposeId), dwFlags, ret_stream);
|
|
|
|
if (IMediaStreamFilter_GetMediaStream(This->filter, PurposeId, &stream) == S_OK)
|
|
{
|
|
IMediaStream_Release(stream);
|
|
return MS_E_PURPOSEID;
|
|
}
|
|
|
|
if (!This->graph && FAILED(hr = create_graph(This, NULL)))
|
|
return hr;
|
|
|
|
if (dwFlags & AMMSF_ADDDEFAULTRENDERER)
|
|
{
|
|
IBaseFilter *dsound_render;
|
|
|
|
if (ret_stream)
|
|
return E_INVALIDARG;
|
|
|
|
if (!IsEqualGUID(PurposeId, &MSPID_PrimaryAudio))
|
|
{
|
|
WARN("AMMSF_ADDDEFAULTRENDERER requested with id %s, returning MS_E_PURPOSEID.\n", debugstr_guid(PurposeId));
|
|
return MS_E_PURPOSEID;
|
|
}
|
|
|
|
if (SUCCEEDED(hr = CoCreateInstance(&CLSID_DSoundRender, NULL,
|
|
CLSCTX_INPROC_SERVER, &IID_IBaseFilter, (void **)&dsound_render)))
|
|
{
|
|
hr = IGraphBuilder_AddFilter(This->graph, dsound_render, NULL);
|
|
IBaseFilter_Release(dsound_render);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
if (stream_object)
|
|
{
|
|
hr = IUnknown_QueryInterface(stream_object, &IID_IAMMediaStream, (void **)&pStream);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
MSPID stream_id;
|
|
hr = IAMMediaStream_GetInformation(pStream, &stream_id, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (IsEqualGUID(PurposeId, &stream_id))
|
|
{
|
|
add_stream(This, pStream, ret_stream);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = MS_E_PURPOSEID;
|
|
}
|
|
}
|
|
|
|
IAMMediaStream_Release(pStream);
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if (IsEqualGUID(PurposeId, &MSPID_PrimaryVideo))
|
|
hr = ddraw_stream_create(NULL, (void **)&pStream);
|
|
else if (IsEqualGUID(PurposeId, &MSPID_PrimaryAudio))
|
|
hr = audio_stream_create(NULL, (void **)&pStream);
|
|
else
|
|
return MS_E_PURPOSEID;
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = IAMMediaStream_Initialize(pStream, stream_object, dwFlags, PurposeId, This->type);
|
|
if (FAILED(hr))
|
|
{
|
|
IAMMediaStream_Release(pStream);
|
|
return hr;
|
|
}
|
|
|
|
add_stream(This, pStream, ret_stream);
|
|
IAMMediaStream_Release(pStream);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_OpenFile(IAMMultiMediaStream *iface,
|
|
const WCHAR *filename, DWORD flags)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
HRESULT ret = S_OK;
|
|
IBaseFilter *BaseFilter = NULL;
|
|
IEnumPins *EnumPins = NULL;
|
|
IPin *ipin;
|
|
PIN_DIRECTION pin_direction;
|
|
|
|
TRACE("(%p/%p)->(%s,%x)\n", This, iface, debugstr_w(filename), flags);
|
|
|
|
if (!filename)
|
|
return E_POINTER;
|
|
|
|
/* If Initialize was not called before, we do it here */
|
|
if (!This->graph)
|
|
{
|
|
ret = IAMMultiMediaStream_Initialize(iface, STREAMTYPE_READ, 0, NULL);
|
|
if (SUCCEEDED(ret))
|
|
ret = create_graph(This, NULL);
|
|
}
|
|
|
|
if (SUCCEEDED(ret))
|
|
ret = IGraphBuilder_AddSourceFilter(This->graph, filename, L"Source", &BaseFilter);
|
|
|
|
if (SUCCEEDED(ret))
|
|
ret = IBaseFilter_EnumPins(BaseFilter, &EnumPins);
|
|
|
|
if (SUCCEEDED(ret))
|
|
ret = IEnumPins_Next(EnumPins, 1, &ipin, NULL);
|
|
|
|
if (SUCCEEDED(ret))
|
|
{
|
|
ret = IPin_QueryDirection(ipin, &pin_direction);
|
|
if (ret == S_OK && pin_direction == PINDIR_OUTPUT)
|
|
This->ipin = ipin;
|
|
}
|
|
|
|
if (SUCCEEDED(ret) && !(flags & AMMSF_NORENDER))
|
|
{
|
|
IFilterGraph2 *graph;
|
|
|
|
if (SUCCEEDED(ret = IGraphBuilder_QueryInterface(This->graph, &IID_IFilterGraph2, (void **)&graph)))
|
|
{
|
|
ret = IFilterGraph2_RenderEx(graph, This->ipin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);
|
|
if (ret == VFW_E_CANNOT_RENDER) ret = VFW_E_CANNOT_CONNECT;
|
|
else if (ret == VFW_S_PARTIAL_RENDER) ret = S_OK;
|
|
IFilterGraph2_Release(graph);
|
|
}
|
|
else
|
|
{
|
|
FIXME("Failed to get IFilterGraph2 interface, hr %#x.\n", ret);
|
|
ret = IGraphBuilder_Render(This->graph, This->ipin);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(ret) && (flags & AMMSF_NOCLOCK))
|
|
{
|
|
IMediaFilter *media_filter;
|
|
|
|
if (SUCCEEDED(ret = IGraphBuilder_QueryInterface(This->graph, &IID_IMediaFilter, (void **)&media_filter)))
|
|
{
|
|
ret = IMediaFilter_SetSyncSource(media_filter, NULL);
|
|
IMediaFilter_Release(media_filter);
|
|
}
|
|
}
|
|
|
|
IMediaStreamFilter_SupportSeeking(This->filter, This->type == STREAMTYPE_READ);
|
|
|
|
if (SUCCEEDED(ret) && (flags & AMMSF_RUN))
|
|
ret = IAMMultiMediaStream_SetState(iface, STREAMSTATE_RUN);
|
|
|
|
if (EnumPins)
|
|
IEnumPins_Release(EnumPins);
|
|
if (BaseFilter)
|
|
IBaseFilter_Release(BaseFilter);
|
|
return ret;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_OpenMoniker(IAMMultiMediaStream *iface,
|
|
IBindCtx *pCtx, IMoniker *pMoniker, DWORD dwFlags)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
FIXME("(%p/%p)->(%p,%p,%x) stub!\n", This, iface, pCtx, pMoniker, dwFlags);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI multimedia_stream_Render(IAMMultiMediaStream *iface, DWORD dwFlags)
|
|
{
|
|
struct multimedia_stream *This = impl_from_IAMMultiMediaStream(iface);
|
|
|
|
FIXME("(%p/%p)->(%x) partial stub!\n", This, iface, dwFlags);
|
|
|
|
if(dwFlags != AMMSF_NOCLOCK)
|
|
return E_INVALIDARG;
|
|
|
|
return IGraphBuilder_Render(This->graph, This->ipin);
|
|
}
|
|
|
|
static const IAMMultiMediaStreamVtbl multimedia_stream_vtbl =
|
|
{
|
|
multimedia_stream_QueryInterface,
|
|
multimedia_stream_AddRef,
|
|
multimedia_stream_Release,
|
|
multimedia_stream_GetInformation,
|
|
multimedia_stream_GetMediaStream,
|
|
multimedia_stream_EnumMediaStreams,
|
|
multimedia_stream_GetState,
|
|
multimedia_stream_SetState,
|
|
multimedia_stream_GetTime,
|
|
multimedia_stream_GetDuration,
|
|
multimedia_stream_Seek,
|
|
multimedia_stream_GetEndOfStream,
|
|
multimedia_stream_Initialize,
|
|
multimedia_stream_GetFilterGraph,
|
|
multimedia_stream_GetFilter,
|
|
multimedia_stream_AddMediaStream,
|
|
multimedia_stream_OpenFile,
|
|
multimedia_stream_OpenMoniker,
|
|
multimedia_stream_Render
|
|
};
|
|
|
|
HRESULT multimedia_stream_create(IUnknown *outer, void **out)
|
|
{
|
|
struct multimedia_stream *object;
|
|
HRESULT hr;
|
|
|
|
if (outer)
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
if (!(object = heap_alloc_zero(sizeof(*object))))
|
|
return E_OUTOFMEMORY;
|
|
|
|
object->IAMMultiMediaStream_iface.lpVtbl = &multimedia_stream_vtbl;
|
|
object->ref = 1;
|
|
|
|
if (FAILED(hr = CoCreateInstance(&CLSID_MediaStreamFilter, NULL,
|
|
CLSCTX_INPROC_SERVER, &IID_IMediaStreamFilter, (void **)&object->filter)))
|
|
{
|
|
ERR("Failed to create stream filter, hr %#x.\n", hr);
|
|
heap_free(object);
|
|
return hr;
|
|
}
|
|
|
|
/* The stream takes an additional reference to the filter. */
|
|
IMediaStreamFilter_AddRef(object->filter);
|
|
|
|
TRACE("Created multimedia stream %p.\n", object);
|
|
*out = &object->IAMMultiMediaStream_iface;
|
|
|
|
return S_OK;
|
|
}
|