1370 lines
41 KiB
C
1370 lines
41 KiB
C
|
/* Gstreamer Media Sink
|
||
|
*
|
||
|
* Copyright 2023 Ziqing Hui for CodeWeavers
|
||
|
*
|
||
|
* 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 "gst_private.h"
|
||
|
#include "mferror.h"
|
||
|
#include "mfapi.h"
|
||
|
|
||
|
#include "wine/debug.h"
|
||
|
#include "wine/list.h"
|
||
|
|
||
|
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
|
||
|
|
||
|
enum async_op
|
||
|
{
|
||
|
ASYNC_START,
|
||
|
ASYNC_STOP,
|
||
|
ASYNC_PAUSE,
|
||
|
ASYNC_PROCESS,
|
||
|
ASYNC_FINALIZE,
|
||
|
};
|
||
|
|
||
|
struct async_command
|
||
|
{
|
||
|
IUnknown IUnknown_iface;
|
||
|
LONG refcount;
|
||
|
|
||
|
enum async_op op;
|
||
|
|
||
|
union
|
||
|
{
|
||
|
struct
|
||
|
{
|
||
|
IMFSample *sample;
|
||
|
UINT32 stream_id;
|
||
|
} process;
|
||
|
struct
|
||
|
{
|
||
|
IMFAsyncResult *result;
|
||
|
} finalize;
|
||
|
} u;
|
||
|
};
|
||
|
|
||
|
struct stream_sink
|
||
|
{
|
||
|
IMFStreamSink IMFStreamSink_iface;
|
||
|
IMFMediaTypeHandler IMFMediaTypeHandler_iface;
|
||
|
LONG refcount;
|
||
|
DWORD id;
|
||
|
|
||
|
IMFMediaType *type;
|
||
|
IMFFinalizableMediaSink *media_sink;
|
||
|
IMFMediaEventQueue *event_queue;
|
||
|
|
||
|
struct list entry;
|
||
|
};
|
||
|
|
||
|
struct media_sink
|
||
|
{
|
||
|
IMFFinalizableMediaSink IMFFinalizableMediaSink_iface;
|
||
|
IMFMediaEventGenerator IMFMediaEventGenerator_iface;
|
||
|
IMFClockStateSink IMFClockStateSink_iface;
|
||
|
IMFAsyncCallback async_callback;
|
||
|
LONG refcount;
|
||
|
CRITICAL_SECTION cs;
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
STATE_OPENED,
|
||
|
STATE_STARTED,
|
||
|
STATE_STOPPED,
|
||
|
STATE_PAUSED,
|
||
|
STATE_FINALIZED,
|
||
|
STATE_SHUTDOWN,
|
||
|
} state;
|
||
|
|
||
|
IMFByteStream *bytestream;
|
||
|
IMFMediaEventQueue *event_queue;
|
||
|
|
||
|
struct list stream_sinks;
|
||
|
|
||
|
wg_muxer_t muxer;
|
||
|
};
|
||
|
|
||
|
static struct stream_sink *impl_from_IMFStreamSink(IMFStreamSink *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct stream_sink, IMFStreamSink_iface);
|
||
|
}
|
||
|
|
||
|
static struct stream_sink *impl_from_IMFMediaTypeHandler(IMFMediaTypeHandler *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct stream_sink, IMFMediaTypeHandler_iface);
|
||
|
}
|
||
|
|
||
|
static struct media_sink *impl_from_IMFFinalizableMediaSink(IMFFinalizableMediaSink *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct media_sink, IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static struct media_sink *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct media_sink, IMFMediaEventGenerator_iface);
|
||
|
}
|
||
|
|
||
|
static struct media_sink *impl_from_IMFClockStateSink(IMFClockStateSink *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct media_sink, IMFClockStateSink_iface);
|
||
|
}
|
||
|
|
||
|
static struct media_sink *impl_from_async_callback(IMFAsyncCallback *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct media_sink, async_callback);
|
||
|
}
|
||
|
|
||
|
static struct async_command *impl_from_async_command_IUnknown(IUnknown *iface)
|
||
|
{
|
||
|
return CONTAINING_RECORD(iface, struct async_command, IUnknown_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
if (IsEqualIID(riid, &IID_IUnknown))
|
||
|
{
|
||
|
*obj = iface;
|
||
|
IUnknown_AddRef(iface);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
WARN("Unsupported interface %s.\n", debugstr_guid(riid));
|
||
|
*obj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI async_command_AddRef(IUnknown *iface)
|
||
|
{
|
||
|
struct async_command *command = impl_from_async_command_IUnknown(iface);
|
||
|
return InterlockedIncrement(&command->refcount);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI async_command_Release(IUnknown *iface)
|
||
|
{
|
||
|
struct async_command *command = impl_from_async_command_IUnknown(iface);
|
||
|
ULONG refcount = InterlockedDecrement(&command->refcount);
|
||
|
|
||
|
if (!refcount)
|
||
|
{
|
||
|
if (command->op == ASYNC_PROCESS && command->u.process.sample)
|
||
|
IMFSample_Release(command->u.process.sample);
|
||
|
else if (command->op == ASYNC_FINALIZE && command->u.finalize.result)
|
||
|
IMFAsyncResult_Release(command->u.finalize.result);
|
||
|
free(command);
|
||
|
}
|
||
|
|
||
|
return refcount;
|
||
|
}
|
||
|
|
||
|
static const IUnknownVtbl async_command_vtbl =
|
||
|
{
|
||
|
async_command_QueryInterface,
|
||
|
async_command_AddRef,
|
||
|
async_command_Release,
|
||
|
};
|
||
|
|
||
|
static HRESULT async_command_create(enum async_op op, struct async_command **out)
|
||
|
{
|
||
|
struct async_command *command;
|
||
|
|
||
|
if (!(command = calloc(1, sizeof(*command))))
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
command->IUnknown_iface.lpVtbl = &async_command_vtbl;
|
||
|
command->refcount = 1;
|
||
|
command->op = op;
|
||
|
|
||
|
TRACE("Created async command %p.\n", command);
|
||
|
*out = command;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_QueryInterface(IMFStreamSink *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj);
|
||
|
|
||
|
if (IsEqualIID(riid, &IID_IMFStreamSink) ||
|
||
|
IsEqualGUID(riid, &IID_IMFMediaEventGenerator) ||
|
||
|
IsEqualIID(riid, &IID_IUnknown))
|
||
|
{
|
||
|
*obj = &stream_sink->IMFStreamSink_iface;
|
||
|
}
|
||
|
else if (IsEqualIID(riid, &IID_IMFMediaTypeHandler))
|
||
|
{
|
||
|
*obj = &stream_sink->IMFMediaTypeHandler_iface;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WARN("Unsupported interface %s.\n", debugstr_guid(riid));
|
||
|
*obj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
IUnknown_AddRef((IUnknown *)*obj);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI stream_sink_AddRef(IMFStreamSink *iface)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
ULONG refcount = InterlockedIncrement(&stream_sink->refcount);
|
||
|
TRACE("iface %p, refcount %lu.\n", iface, refcount);
|
||
|
return refcount;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI stream_sink_Release(IMFStreamSink *iface)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
ULONG refcount = InterlockedDecrement(&stream_sink->refcount);
|
||
|
|
||
|
TRACE("iface %p, refcount %lu.\n", iface, refcount);
|
||
|
|
||
|
if (!refcount)
|
||
|
{
|
||
|
IMFMediaEventQueue_Release(stream_sink->event_queue);
|
||
|
IMFFinalizableMediaSink_Release(stream_sink->media_sink);
|
||
|
if (stream_sink->type)
|
||
|
IMFMediaType_Release(stream_sink->type);
|
||
|
free(stream_sink);
|
||
|
}
|
||
|
|
||
|
return refcount;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_GetEvent(IMFStreamSink *iface, DWORD flags, IMFMediaEvent **event)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, flags %#lx, event %p.\n", iface, flags, event);
|
||
|
|
||
|
return IMFMediaEventQueue_GetEvent(stream_sink->event_queue, flags, event);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_BeginGetEvent(IMFStreamSink *iface, IMFAsyncCallback *callback,
|
||
|
IUnknown *state)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, callback %p, state %p.\n", iface, callback, state);
|
||
|
|
||
|
return IMFMediaEventQueue_BeginGetEvent(stream_sink->event_queue, callback, state);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_EndGetEvent(IMFStreamSink *iface, IMFAsyncResult *result,
|
||
|
IMFMediaEvent **event)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, result %p, event %p.\n", iface, result, event);
|
||
|
|
||
|
return IMFMediaEventQueue_EndGetEvent(stream_sink->event_queue, result, event);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_QueueEvent(IMFStreamSink *iface, MediaEventType event_type,
|
||
|
REFGUID ext_type, HRESULT hr, const PROPVARIANT *value)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n",
|
||
|
iface, event_type, debugstr_guid(ext_type), hr, value);
|
||
|
|
||
|
return IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, event_type, ext_type, hr, value);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_GetMediaSink(IMFStreamSink *iface, IMFMediaSink **ret)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, ret %p.\n", iface, ret);
|
||
|
|
||
|
IMFMediaSink_AddRef((*ret = (IMFMediaSink *)stream_sink->media_sink));
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_GetIdentifier(IMFStreamSink *iface, DWORD *identifier)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, identifier %p.\n", iface, identifier);
|
||
|
|
||
|
*identifier = stream_sink->id;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_GetMediaTypeHandler(IMFStreamSink *iface, IMFMediaTypeHandler **handler)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
|
||
|
TRACE("iface %p, handler %p.\n", iface, handler);
|
||
|
|
||
|
IMFMediaTypeHandler_AddRef((*handler = &stream_sink->IMFMediaTypeHandler_iface));
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_ProcessSample(IMFStreamSink *iface, IMFSample *sample)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFStreamSink(iface);
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(stream_sink->media_sink);
|
||
|
struct async_command *command;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, sample %p.\n", iface, sample);
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
if (media_sink->state == STATE_SHUTDOWN)
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return MF_E_SHUTDOWN;
|
||
|
}
|
||
|
|
||
|
if (media_sink->state != STATE_STARTED && media_sink->state != STATE_PAUSED)
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return MF_E_INVALIDREQUEST;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr = (async_command_create(ASYNC_PROCESS, &command))))
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
IMFSample_AddRef((command->u.process.sample = sample));
|
||
|
command->u.process.stream_id = stream_sink->id;
|
||
|
|
||
|
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD,
|
||
|
&media_sink->async_callback, &command->IUnknown_iface);
|
||
|
IUnknown_Release(&command->IUnknown_iface);
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_PlaceMarker(IMFStreamSink *iface, MFSTREAMSINK_MARKER_TYPE marker_type,
|
||
|
const PROPVARIANT *marker_value, const PROPVARIANT *context_value)
|
||
|
{
|
||
|
FIXME("iface %p, marker_type %d, marker_value %p, context_value %p stub!\n",
|
||
|
iface, marker_type, marker_value, context_value);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_Flush(IMFStreamSink *iface)
|
||
|
{
|
||
|
FIXME("iface %p stub!\n", iface);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static const IMFStreamSinkVtbl stream_sink_vtbl =
|
||
|
{
|
||
|
stream_sink_QueryInterface,
|
||
|
stream_sink_AddRef,
|
||
|
stream_sink_Release,
|
||
|
stream_sink_GetEvent,
|
||
|
stream_sink_BeginGetEvent,
|
||
|
stream_sink_EndGetEvent,
|
||
|
stream_sink_QueueEvent,
|
||
|
stream_sink_GetMediaSink,
|
||
|
stream_sink_GetIdentifier,
|
||
|
stream_sink_GetMediaTypeHandler,
|
||
|
stream_sink_ProcessSample,
|
||
|
stream_sink_PlaceMarker,
|
||
|
stream_sink_Flush,
|
||
|
};
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_QueryInterface(IMFMediaTypeHandler *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface);
|
||
|
return IMFStreamSink_QueryInterface(&stream_sink->IMFStreamSink_iface, riid, obj);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI stream_sink_type_handler_AddRef(IMFMediaTypeHandler *iface)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface);
|
||
|
return IMFStreamSink_AddRef(&stream_sink->IMFStreamSink_iface);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI stream_sink_type_handler_Release(IMFMediaTypeHandler *iface)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface);
|
||
|
return IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_IsMediaTypeSupported(IMFMediaTypeHandler *iface,
|
||
|
IMFMediaType *in_type, IMFMediaType **out_type)
|
||
|
{
|
||
|
FIXME("iface %p, in_type %p, out_type %p.\n", iface, in_type, out_type);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_GetMediaTypeCount(IMFMediaTypeHandler *iface, DWORD *count)
|
||
|
{
|
||
|
FIXME("iface %p, count %p.\n", iface, count);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_GetMediaTypeByIndex(IMFMediaTypeHandler *iface, DWORD index,
|
||
|
IMFMediaType **type)
|
||
|
{
|
||
|
FIXME("iface %p, index %lu, type %p.\n", iface, index, type);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_SetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType *type)
|
||
|
{
|
||
|
FIXME("iface %p, type %p.\n", iface, type);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_GetCurrentMediaType(IMFMediaTypeHandler *iface, IMFMediaType **type)
|
||
|
{
|
||
|
struct stream_sink *stream_sink = impl_from_IMFMediaTypeHandler(iface);
|
||
|
|
||
|
TRACE("iface %p, type %p.\n", iface, type);
|
||
|
|
||
|
if (!type)
|
||
|
return E_POINTER;
|
||
|
if (!stream_sink->type)
|
||
|
return MF_E_NOT_INITIALIZED;
|
||
|
|
||
|
IMFMediaType_AddRef((*type = stream_sink->type));
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI stream_sink_type_handler_GetMajorType(IMFMediaTypeHandler *iface, GUID *type)
|
||
|
{
|
||
|
FIXME("iface %p, type %p.\n", iface, type);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static const IMFMediaTypeHandlerVtbl stream_sink_type_handler_vtbl =
|
||
|
{
|
||
|
stream_sink_type_handler_QueryInterface,
|
||
|
stream_sink_type_handler_AddRef,
|
||
|
stream_sink_type_handler_Release,
|
||
|
stream_sink_type_handler_IsMediaTypeSupported,
|
||
|
stream_sink_type_handler_GetMediaTypeCount,
|
||
|
stream_sink_type_handler_GetMediaTypeByIndex,
|
||
|
stream_sink_type_handler_SetCurrentMediaType,
|
||
|
stream_sink_type_handler_GetCurrentMediaType,
|
||
|
stream_sink_type_handler_GetMajorType,
|
||
|
};
|
||
|
|
||
|
static HRESULT stream_sink_create(DWORD stream_sink_id, IMFMediaType *media_type, struct media_sink *media_sink,
|
||
|
struct stream_sink **out)
|
||
|
{
|
||
|
struct stream_sink *stream_sink;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("stream_sink_id %#lx, media_type %p, media_sink %p, out %p.\n",
|
||
|
stream_sink_id, media_type, media_sink, out);
|
||
|
|
||
|
if (!(stream_sink = calloc(1, sizeof(*stream_sink))))
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
if (FAILED(hr = MFCreateEventQueue(&stream_sink->event_queue)))
|
||
|
{
|
||
|
free(stream_sink);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
stream_sink->IMFStreamSink_iface.lpVtbl = &stream_sink_vtbl;
|
||
|
stream_sink->IMFMediaTypeHandler_iface.lpVtbl = &stream_sink_type_handler_vtbl;
|
||
|
stream_sink->refcount = 1;
|
||
|
stream_sink->id = stream_sink_id;
|
||
|
if (media_type)
|
||
|
IMFMediaType_AddRef((stream_sink->type = media_type));
|
||
|
IMFFinalizableMediaSink_AddRef((stream_sink->media_sink = &media_sink->IMFFinalizableMediaSink_iface));
|
||
|
|
||
|
TRACE("Created stream sink %p.\n", stream_sink);
|
||
|
*out = stream_sink;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static struct stream_sink *media_sink_get_stream_sink_by_id(struct media_sink *media_sink, DWORD id)
|
||
|
{
|
||
|
struct stream_sink *stream_sink;
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry)
|
||
|
{
|
||
|
if (stream_sink->id == id)
|
||
|
return stream_sink;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_queue_command(struct media_sink *media_sink, enum async_op op)
|
||
|
{
|
||
|
struct async_command *command;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (media_sink->state == STATE_SHUTDOWN)
|
||
|
return MF_E_SHUTDOWN;
|
||
|
|
||
|
if (FAILED(hr = async_command_create(op, &command)))
|
||
|
return hr;
|
||
|
|
||
|
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD,
|
||
|
&media_sink->async_callback, &command->IUnknown_iface);
|
||
|
IUnknown_Release(&command->IUnknown_iface);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_queue_stream_event(struct media_sink *media_sink, MediaEventType type)
|
||
|
{
|
||
|
struct stream_sink *stream_sink;
|
||
|
HRESULT hr;
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry)
|
||
|
{
|
||
|
if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(stream_sink->event_queue, type, &GUID_NULL, S_OK, NULL)))
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_write_stream(struct media_sink *media_sink)
|
||
|
{
|
||
|
BYTE buffer[1024];
|
||
|
UINT32 size = sizeof(buffer);
|
||
|
ULONG written;
|
||
|
QWORD offset;
|
||
|
HRESULT hr;
|
||
|
|
||
|
while (SUCCEEDED(hr = wg_muxer_read_data(media_sink->muxer, buffer, &size, &offset)))
|
||
|
{
|
||
|
if (offset != UINT64_MAX && FAILED(hr = IMFByteStream_SetCurrentPosition(media_sink->bytestream, offset)))
|
||
|
return hr;
|
||
|
|
||
|
if (FAILED(hr = IMFByteStream_Write(media_sink->bytestream, buffer, size, &written)))
|
||
|
return hr;
|
||
|
|
||
|
size = sizeof(buffer);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_start(struct media_sink *media_sink)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (FAILED(hr = wg_muxer_start(media_sink->muxer)))
|
||
|
return hr;
|
||
|
|
||
|
media_sink->state = STATE_STARTED;
|
||
|
|
||
|
return media_sink_queue_stream_event(media_sink, MEStreamSinkStarted);
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_stop(struct media_sink *media_sink)
|
||
|
{
|
||
|
media_sink->state = STATE_STOPPED;
|
||
|
return media_sink_queue_stream_event(media_sink, MEStreamSinkStopped);
|
||
|
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_pause(struct media_sink *media_sink)
|
||
|
{
|
||
|
media_sink->state = STATE_PAUSED;
|
||
|
return media_sink_queue_stream_event(media_sink, MEStreamSinkPaused);
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_process(struct media_sink *media_sink, IMFSample *sample, UINT32 stream_id)
|
||
|
{
|
||
|
wg_muxer_t muxer = media_sink->muxer;
|
||
|
struct wg_sample *wg_sample;
|
||
|
LONGLONG time, duration;
|
||
|
UINT32 value;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("media_sink %p, sample %p, stream_id %u.\n", media_sink, sample, stream_id);
|
||
|
|
||
|
if (FAILED(hr = media_sink_write_stream(media_sink)))
|
||
|
WARN("Failed to write output samples to stream, hr %#lx.\n", hr);
|
||
|
|
||
|
if (FAILED(hr = wg_sample_create_mf(sample, &wg_sample)))
|
||
|
return hr;
|
||
|
|
||
|
if (SUCCEEDED(IMFSample_GetSampleTime(sample, &time)))
|
||
|
{
|
||
|
wg_sample->flags |= WG_SAMPLE_FLAG_HAS_PTS;
|
||
|
wg_sample->pts = time;
|
||
|
}
|
||
|
if (SUCCEEDED(IMFSample_GetSampleDuration(sample, &duration)))
|
||
|
{
|
||
|
wg_sample->flags |= WG_SAMPLE_FLAG_HAS_DURATION;
|
||
|
wg_sample->duration = duration;
|
||
|
}
|
||
|
if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_CleanPoint, &value)) && value)
|
||
|
wg_sample->flags |= WG_SAMPLE_FLAG_SYNC_POINT;
|
||
|
if (SUCCEEDED(IMFSample_GetUINT32(sample, &MFSampleExtension_Discontinuity, &value)) && value)
|
||
|
wg_sample->flags |= WG_SAMPLE_FLAG_DISCONTINUITY;
|
||
|
|
||
|
hr = wg_muxer_push_sample(muxer, wg_sample, stream_id);
|
||
|
wg_sample_release(wg_sample);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_begin_finalize(struct media_sink *media_sink, IMFAsyncCallback *callback, IUnknown *state)
|
||
|
{
|
||
|
struct async_command *command;
|
||
|
IMFAsyncResult *result;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (media_sink->state == STATE_SHUTDOWN)
|
||
|
return MF_E_SHUTDOWN;
|
||
|
if (!callback)
|
||
|
return S_OK;
|
||
|
|
||
|
if (FAILED(hr = async_command_create(ASYNC_FINALIZE, &command)))
|
||
|
return hr;
|
||
|
|
||
|
if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &result)))
|
||
|
{
|
||
|
IUnknown_Release(&command->IUnknown_iface);
|
||
|
return hr;
|
||
|
}
|
||
|
IMFAsyncResult_AddRef((command->u.finalize.result = result));
|
||
|
|
||
|
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD,
|
||
|
&media_sink->async_callback, &command->IUnknown_iface);
|
||
|
IUnknown_Release(&command->IUnknown_iface);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT media_sink_finalize(struct media_sink *media_sink, IMFAsyncResult *result)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
media_sink->state = STATE_FINALIZED;
|
||
|
|
||
|
hr = wg_muxer_finalize(media_sink->muxer);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = media_sink_write_stream(media_sink);
|
||
|
|
||
|
IMFAsyncResult_SetStatus(result, hr);
|
||
|
MFInvokeCallback(result);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_QueryInterface(IMFFinalizableMediaSink *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
|
||
|
TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj);
|
||
|
|
||
|
if (IsEqualIID(riid, &IID_IMFFinalizableMediaSink) ||
|
||
|
IsEqualIID(riid, &IID_IMFMediaSink) ||
|
||
|
IsEqualIID(riid, &IID_IUnknown))
|
||
|
{
|
||
|
*obj = iface;
|
||
|
}
|
||
|
else if (IsEqualGUID(riid, &IID_IMFMediaEventGenerator))
|
||
|
{
|
||
|
*obj = &media_sink->IMFMediaEventGenerator_iface;
|
||
|
}
|
||
|
else if (IsEqualIID(riid, &IID_IMFClockStateSink))
|
||
|
{
|
||
|
*obj = &media_sink->IMFClockStateSink_iface;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
WARN("Unsupported interface %s.\n", debugstr_guid(riid));
|
||
|
*obj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
IUnknown_AddRef((IUnknown *)*obj);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_AddRef(IMFFinalizableMediaSink *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
ULONG refcount = InterlockedIncrement(&media_sink->refcount);
|
||
|
TRACE("iface %p, refcount %lu.\n", iface, refcount);
|
||
|
return refcount;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_Release(IMFFinalizableMediaSink *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
ULONG refcount = InterlockedDecrement(&media_sink->refcount);
|
||
|
|
||
|
TRACE("iface %p, refcount %lu.\n", iface, refcount);
|
||
|
|
||
|
if (!refcount)
|
||
|
{
|
||
|
IMFFinalizableMediaSink_Shutdown(iface);
|
||
|
IMFMediaEventQueue_Release(media_sink->event_queue);
|
||
|
IMFByteStream_Release(media_sink->bytestream);
|
||
|
media_sink->cs.DebugInfo->Spare[0] = 0;
|
||
|
DeleteCriticalSection(&media_sink->cs);
|
||
|
wg_muxer_destroy(media_sink->muxer);
|
||
|
free(media_sink);
|
||
|
}
|
||
|
|
||
|
return refcount;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_GetCharacteristics(IMFFinalizableMediaSink *iface, DWORD *flags)
|
||
|
{
|
||
|
FIXME("iface %p, flags %p stub!\n", iface, flags);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_AddStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id,
|
||
|
IMFMediaType *media_type, IMFStreamSink **stream_sink)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
struct stream_sink *object;
|
||
|
struct wg_format format;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, stream_sink_id %#lx, media_type %p, stream_sink %p.\n",
|
||
|
iface, stream_sink_id, media_type, stream_sink);
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
if (media_sink_get_stream_sink_by_id(media_sink, stream_sink_id))
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return MF_E_STREAMSINK_EXISTS;
|
||
|
}
|
||
|
|
||
|
if (FAILED(hr = stream_sink_create(stream_sink_id, media_type, media_sink, &object)))
|
||
|
{
|
||
|
WARN("Failed to create stream sink, hr %#lx.\n", hr);
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
mf_media_type_to_wg_format(media_type, &format);
|
||
|
if (FAILED(hr = wg_muxer_add_stream(media_sink->muxer, stream_sink_id, &format)))
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
IMFStreamSink_Release(&object->IMFStreamSink_iface);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
list_add_tail(&media_sink->stream_sinks, &object->entry);
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
if (stream_sink)
|
||
|
IMFStreamSink_AddRef((*stream_sink = &object->IMFStreamSink_iface));
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_RemoveStreamSink(IMFFinalizableMediaSink *iface, DWORD stream_sink_id)
|
||
|
{
|
||
|
FIXME("iface %p, stream_sink_id %#lx stub!\n", iface, stream_sink_id);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_GetStreamSinkCount(IMFFinalizableMediaSink *iface, DWORD *count)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
|
||
|
TRACE("iface %p, count %p.\n", iface, count);
|
||
|
|
||
|
if (!count)
|
||
|
return E_POINTER;
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
*count = list_count(&media_sink->stream_sinks);
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_GetStreamSinkByIndex(IMFFinalizableMediaSink *iface, DWORD index,
|
||
|
IMFStreamSink **stream)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
struct stream_sink *stream_sink;
|
||
|
HRESULT hr = MF_E_INVALIDINDEX;
|
||
|
DWORD entry_index = 0;
|
||
|
|
||
|
TRACE("iface %p, index %lu, stream %p stub!\n", iface, index, stream);
|
||
|
|
||
|
if (!stream)
|
||
|
return E_POINTER;
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY(stream_sink, &media_sink->stream_sinks, struct stream_sink, entry)
|
||
|
{
|
||
|
if (entry_index++ == index)
|
||
|
{
|
||
|
IMFStreamSink_AddRef((*stream = &stream_sink->IMFStreamSink_iface));
|
||
|
hr = S_OK;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_GetStreamSinkById(IMFFinalizableMediaSink *iface, DWORD stream_sink_id,
|
||
|
IMFStreamSink **stream)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
struct stream_sink *stream_sink;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, stream_sink_id %#lx, stream %p.\n", iface, stream_sink_id, stream);
|
||
|
|
||
|
if (!stream)
|
||
|
return E_POINTER;
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
hr = MF_E_INVALIDSTREAMNUMBER;
|
||
|
if ((stream_sink = media_sink_get_stream_sink_by_id(media_sink, stream_sink_id)))
|
||
|
{
|
||
|
IMFStreamSink_AddRef((*stream = &stream_sink->IMFStreamSink_iface));
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_SetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock *clock)
|
||
|
{
|
||
|
FIXME("iface %p, clock %p stub!\n", iface, clock);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_GetPresentationClock(IMFFinalizableMediaSink *iface, IMFPresentationClock **clock)
|
||
|
{
|
||
|
FIXME("iface %p, clock %p stub!\n", iface, clock);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_Shutdown(IMFFinalizableMediaSink *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
struct stream_sink *stream_sink, *next;
|
||
|
|
||
|
TRACE("iface %p.\n", iface);
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
if (media_sink->state == STATE_SHUTDOWN)
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return MF_E_SHUTDOWN;
|
||
|
}
|
||
|
|
||
|
LIST_FOR_EACH_ENTRY_SAFE(stream_sink, next, &media_sink->stream_sinks, struct stream_sink, entry)
|
||
|
{
|
||
|
list_remove(&stream_sink->entry);
|
||
|
IMFMediaEventQueue_Shutdown(stream_sink->event_queue);
|
||
|
IMFStreamSink_Release(&stream_sink->IMFStreamSink_iface);
|
||
|
}
|
||
|
|
||
|
IMFMediaEventQueue_Shutdown(media_sink->event_queue);
|
||
|
IMFByteStream_Close(media_sink->bytestream);
|
||
|
|
||
|
media_sink->state = STATE_SHUTDOWN;
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_BeginFinalize(IMFFinalizableMediaSink *iface, IMFAsyncCallback *callback, IUnknown *state)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFFinalizableMediaSink(iface);
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, callback %p, state %p.\n", iface, callback, state);
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
hr = media_sink_begin_finalize(media_sink, callback, state);
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_EndFinalize(IMFFinalizableMediaSink *iface, IMFAsyncResult *result)
|
||
|
{
|
||
|
TRACE("iface %p, result %p.\n", iface, result);
|
||
|
|
||
|
return result ? IMFAsyncResult_GetStatus(result) : E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
static const IMFFinalizableMediaSinkVtbl media_sink_vtbl =
|
||
|
{
|
||
|
media_sink_QueryInterface,
|
||
|
media_sink_AddRef,
|
||
|
media_sink_Release,
|
||
|
media_sink_GetCharacteristics,
|
||
|
media_sink_AddStreamSink,
|
||
|
media_sink_RemoveStreamSink,
|
||
|
media_sink_GetStreamSinkCount,
|
||
|
media_sink_GetStreamSinkByIndex,
|
||
|
media_sink_GetStreamSinkById,
|
||
|
media_sink_SetPresentationClock,
|
||
|
media_sink_GetPresentationClock,
|
||
|
media_sink_Shutdown,
|
||
|
media_sink_BeginFinalize,
|
||
|
media_sink_EndFinalize,
|
||
|
};
|
||
|
|
||
|
static HRESULT WINAPI media_sink_event_QueryInterface(IMFMediaEventGenerator *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
return IMFFinalizableMediaSink_QueryInterface(&media_sink->IMFFinalizableMediaSink_iface, riid, obj);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_event_AddRef(IMFMediaEventGenerator *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
return IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_event_Release(IMFMediaEventGenerator *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
return IMFFinalizableMediaSink_Release(&media_sink->IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_event_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, IMFMediaEvent **event)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
|
||
|
TRACE("iface %p, flags %#lx, event %p.\n", iface, flags, event);
|
||
|
|
||
|
return IMFMediaEventQueue_GetEvent(media_sink->event_queue, flags, event);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_event_BeginGetEvent(IMFMediaEventGenerator *iface, IMFAsyncCallback *callback,
|
||
|
IUnknown *state)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
|
||
|
TRACE("iface %p, callback %p, state %p.\n", iface, callback, state);
|
||
|
|
||
|
return IMFMediaEventQueue_BeginGetEvent(media_sink->event_queue, callback, state);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_event_EndGetEvent(IMFMediaEventGenerator *iface, IMFAsyncResult *result,
|
||
|
IMFMediaEvent **event)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
|
||
|
TRACE("iface %p, result %p, event %p.\n", iface, result, event);
|
||
|
|
||
|
return IMFMediaEventQueue_EndGetEvent(media_sink->event_queue, result, event);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_event_QueueEvent(IMFMediaEventGenerator *iface, MediaEventType event_type,
|
||
|
REFGUID ext_type, HRESULT hr, const PROPVARIANT *value)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFMediaEventGenerator(iface);
|
||
|
|
||
|
TRACE("iface %p, event_type %lu, ext_type %s, hr %#lx, value %p.\n",
|
||
|
iface, event_type, debugstr_guid(ext_type), hr, value);
|
||
|
|
||
|
return IMFMediaEventQueue_QueueEventParamVar(media_sink->event_queue, event_type, ext_type, hr, value);
|
||
|
}
|
||
|
|
||
|
static const IMFMediaEventGeneratorVtbl media_sink_event_vtbl =
|
||
|
{
|
||
|
media_sink_event_QueryInterface,
|
||
|
media_sink_event_AddRef,
|
||
|
media_sink_event_Release,
|
||
|
media_sink_event_GetEvent,
|
||
|
media_sink_event_BeginGetEvent,
|
||
|
media_sink_event_EndGetEvent,
|
||
|
media_sink_event_QueueEvent,
|
||
|
};
|
||
|
|
||
|
static HRESULT WINAPI media_sink_clock_sink_QueryInterface(IMFClockStateSink *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
return IMFFinalizableMediaSink_QueryInterface(&media_sink->IMFFinalizableMediaSink_iface, riid, obj);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_clock_sink_AddRef(IMFClockStateSink *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
return IMFFinalizableMediaSink_AddRef(&media_sink->IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_clock_sink_Release(IMFClockStateSink *iface)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
return IMFFinalizableMediaSink_Release(&media_sink->IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_clock_sink_OnClockStart(IMFClockStateSink *iface, MFTIME systime, LONGLONG offset)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, systime %s, offset %s.\n", iface, debugstr_time(systime), debugstr_time(offset));
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
hr = media_sink_queue_command(media_sink, ASYNC_START);
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_clock_sink_OnClockStop(IMFClockStateSink *iface, MFTIME systime)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime));
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
hr = media_sink_queue_command(media_sink, ASYNC_STOP);
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_clock_sink_OnClockPause(IMFClockStateSink *iface, MFTIME systime)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime));
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
hr = media_sink_queue_command(media_sink, ASYNC_PAUSE);
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_clock_sink_OnClockRestart(IMFClockStateSink *iface, MFTIME systime)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_IMFClockStateSink(iface);
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, systime %s.\n", iface, debugstr_time(systime));
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
hr = media_sink_queue_command(media_sink, ASYNC_START);
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_clock_sink_OnClockSetRate(IMFClockStateSink *iface, MFTIME systime, float rate)
|
||
|
{
|
||
|
FIXME("iface %p, systime %s, rate %f stub!\n", iface, debugstr_time(systime), rate);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static const IMFClockStateSinkVtbl media_sink_clock_sink_vtbl =
|
||
|
{
|
||
|
media_sink_clock_sink_QueryInterface,
|
||
|
media_sink_clock_sink_AddRef,
|
||
|
media_sink_clock_sink_Release,
|
||
|
media_sink_clock_sink_OnClockStart,
|
||
|
media_sink_clock_sink_OnClockStop,
|
||
|
media_sink_clock_sink_OnClockPause,
|
||
|
media_sink_clock_sink_OnClockRestart,
|
||
|
media_sink_clock_sink_OnClockSetRate,
|
||
|
};
|
||
|
|
||
|
static HRESULT WINAPI media_sink_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
|
||
|
{
|
||
|
TRACE("iface %p, riid %s, obj %p.\n", iface, debugstr_guid(riid), obj);
|
||
|
|
||
|
if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
|
||
|
IsEqualIID(riid, &IID_IUnknown))
|
||
|
{
|
||
|
*obj = iface;
|
||
|
IMFAsyncCallback_AddRef(iface);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
WARN("Unsupported interface %s.\n", debugstr_guid(riid));
|
||
|
*obj = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_callback_AddRef(IMFAsyncCallback *iface)
|
||
|
{
|
||
|
struct media_sink *sink = impl_from_async_callback(iface);
|
||
|
return IMFFinalizableMediaSink_AddRef(&sink->IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI media_sink_callback_Release(IMFAsyncCallback *iface)
|
||
|
{
|
||
|
struct media_sink *sink = impl_from_async_callback(iface);
|
||
|
return IMFFinalizableMediaSink_Release(&sink->IMFFinalizableMediaSink_iface);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
|
||
|
{
|
||
|
TRACE("iface %p, flags %p, queue %p.\n", iface, flags, queue);
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI media_sink_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *async_result)
|
||
|
{
|
||
|
struct media_sink *media_sink = impl_from_async_callback(iface);
|
||
|
struct async_command *command;
|
||
|
HRESULT hr = E_FAIL;
|
||
|
IUnknown *state;
|
||
|
|
||
|
TRACE("iface %p, async_result %p.\n", iface, async_result);
|
||
|
|
||
|
EnterCriticalSection(&media_sink->cs);
|
||
|
|
||
|
if (!(state = IMFAsyncResult_GetStateNoAddRef(async_result)))
|
||
|
{
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
command = impl_from_async_command_IUnknown(state);
|
||
|
switch (command->op)
|
||
|
{
|
||
|
case ASYNC_START:
|
||
|
if (FAILED(hr = media_sink_start(media_sink)))
|
||
|
WARN("Failed to start media sink.\n");
|
||
|
break;
|
||
|
case ASYNC_STOP:
|
||
|
hr = media_sink_stop(media_sink);
|
||
|
break;
|
||
|
case ASYNC_PAUSE:
|
||
|
hr = media_sink_pause(media_sink);
|
||
|
break;
|
||
|
case ASYNC_PROCESS:
|
||
|
if (FAILED(hr = media_sink_process(media_sink, command->u.process.sample, command->u.process.stream_id)))
|
||
|
WARN("Failed to process sample, hr %#lx.\n", hr);
|
||
|
break;
|
||
|
case ASYNC_FINALIZE:
|
||
|
if (FAILED(hr = media_sink_finalize(media_sink, command->u.finalize.result)))
|
||
|
WARN("Failed to finalize, hr %#lx.\n", hr);
|
||
|
break;
|
||
|
default:
|
||
|
WARN("Unsupported op %u.\n", command->op);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LeaveCriticalSection(&media_sink->cs);
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static const IMFAsyncCallbackVtbl media_sink_callback_vtbl =
|
||
|
{
|
||
|
media_sink_callback_QueryInterface,
|
||
|
media_sink_callback_AddRef,
|
||
|
media_sink_callback_Release,
|
||
|
media_sink_callback_GetParameters,
|
||
|
media_sink_callback_Invoke,
|
||
|
};
|
||
|
|
||
|
static HRESULT media_sink_create(IMFByteStream *bytestream, const char *format, struct media_sink **out)
|
||
|
{
|
||
|
struct media_sink *media_sink;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("bytestream %p, out %p.\n", bytestream, out);
|
||
|
|
||
|
if (!bytestream)
|
||
|
return E_POINTER;
|
||
|
|
||
|
if (!(media_sink = calloc(1, sizeof(*media_sink))))
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
if (FAILED(hr = wg_muxer_create(format, &media_sink->muxer)))
|
||
|
goto fail;
|
||
|
if (FAILED(hr = MFCreateEventQueue(&media_sink->event_queue)))
|
||
|
goto fail;
|
||
|
|
||
|
media_sink->IMFFinalizableMediaSink_iface.lpVtbl = &media_sink_vtbl;
|
||
|
media_sink->IMFMediaEventGenerator_iface.lpVtbl = &media_sink_event_vtbl;
|
||
|
media_sink->IMFClockStateSink_iface.lpVtbl = &media_sink_clock_sink_vtbl;
|
||
|
media_sink->async_callback.lpVtbl = &media_sink_callback_vtbl;
|
||
|
media_sink->refcount = 1;
|
||
|
media_sink->state = STATE_OPENED;
|
||
|
InitializeCriticalSection(&media_sink->cs);
|
||
|
media_sink->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
|
||
|
IMFByteStream_AddRef((media_sink->bytestream = bytestream));
|
||
|
list_init(&media_sink->stream_sinks);
|
||
|
|
||
|
*out = media_sink;
|
||
|
TRACE("Created media sink %p.\n", media_sink);
|
||
|
|
||
|
return S_OK;
|
||
|
|
||
|
fail:
|
||
|
if (media_sink->muxer)
|
||
|
wg_muxer_destroy(media_sink->muxer);
|
||
|
free(media_sink);
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI sink_class_factory_QueryInterface(IMFSinkClassFactory *iface, REFIID riid, void **out)
|
||
|
{
|
||
|
if (IsEqualIID(riid, &IID_IMFSinkClassFactory)
|
||
|
|| IsEqualIID(riid, &IID_IUnknown))
|
||
|
{
|
||
|
*out = iface;
|
||
|
IMFSinkClassFactory_AddRef(iface);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
*out = NULL;
|
||
|
return E_NOINTERFACE;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI sink_class_factory_AddRef(IMFSinkClassFactory *iface)
|
||
|
{
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
static ULONG WINAPI sink_class_factory_Release(IMFSinkClassFactory *iface)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI sink_class_factory_create_media_sink(IMFSinkClassFactory *iface, IMFByteStream *bytestream,
|
||
|
const char *format, IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out)
|
||
|
{
|
||
|
IMFFinalizableMediaSink *media_sink_iface;
|
||
|
struct media_sink *media_sink;
|
||
|
HRESULT hr;
|
||
|
|
||
|
TRACE("iface %p, bytestream %p, video_type %p, audio_type %p, out %p.\n",
|
||
|
iface, bytestream, video_type, audio_type, out);
|
||
|
|
||
|
if (FAILED(hr = media_sink_create(bytestream, format, &media_sink)))
|
||
|
return hr;
|
||
|
media_sink_iface = &media_sink->IMFFinalizableMediaSink_iface;
|
||
|
|
||
|
if (video_type)
|
||
|
{
|
||
|
if (FAILED(hr = IMFFinalizableMediaSink_AddStreamSink(media_sink_iface, 1, video_type, NULL)))
|
||
|
{
|
||
|
IMFFinalizableMediaSink_Shutdown(media_sink_iface);
|
||
|
IMFFinalizableMediaSink_Release(media_sink_iface);
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
if (audio_type)
|
||
|
{
|
||
|
if (FAILED(hr = IMFFinalizableMediaSink_AddStreamSink(media_sink_iface, 2, audio_type, NULL)))
|
||
|
{
|
||
|
IMFFinalizableMediaSink_Shutdown(media_sink_iface);
|
||
|
IMFFinalizableMediaSink_Release(media_sink_iface);
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*out = (IMFMediaSink *)media_sink_iface;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI mp3_sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream,
|
||
|
IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out)
|
||
|
{
|
||
|
const char *format = "application/x-id3";
|
||
|
|
||
|
return sink_class_factory_create_media_sink(iface, bytestream, format, video_type, audio_type, out);
|
||
|
}
|
||
|
|
||
|
static HRESULT WINAPI mpeg4_sink_class_factory_CreateMediaSink(IMFSinkClassFactory *iface, IMFByteStream *bytestream,
|
||
|
IMFMediaType *video_type, IMFMediaType *audio_type, IMFMediaSink **out)
|
||
|
{
|
||
|
const char *format = "video/quicktime, variant=iso";
|
||
|
|
||
|
return sink_class_factory_create_media_sink(iface, bytestream, format, video_type, audio_type, out);
|
||
|
}
|
||
|
|
||
|
static const IMFSinkClassFactoryVtbl mp3_sink_class_factory_vtbl =
|
||
|
{
|
||
|
sink_class_factory_QueryInterface,
|
||
|
sink_class_factory_AddRef,
|
||
|
sink_class_factory_Release,
|
||
|
mp3_sink_class_factory_CreateMediaSink,
|
||
|
};
|
||
|
|
||
|
static const IMFSinkClassFactoryVtbl mpeg4_sink_class_factory_vtbl =
|
||
|
{
|
||
|
sink_class_factory_QueryInterface,
|
||
|
sink_class_factory_AddRef,
|
||
|
sink_class_factory_Release,
|
||
|
mpeg4_sink_class_factory_CreateMediaSink,
|
||
|
};
|
||
|
|
||
|
static IMFSinkClassFactory mp3_sink_class_factory = { &mp3_sink_class_factory_vtbl };
|
||
|
static IMFSinkClassFactory mpeg4_sink_class_factory = { &mpeg4_sink_class_factory_vtbl };
|
||
|
|
||
|
HRESULT mp3_sink_class_factory_create(IUnknown *outer, IUnknown **out)
|
||
|
{
|
||
|
*out = (IUnknown *)&mp3_sink_class_factory;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT mpeg4_sink_class_factory_create(IUnknown *outer, IUnknown **out)
|
||
|
{
|
||
|
*out = (IUnknown *)&mpeg4_sink_class_factory;
|
||
|
return S_OK;
|
||
|
}
|