Sweden-Number/dlls/winegstreamer/media_source.c

1350 lines
40 KiB
C
Raw Normal View History

/* GStreamer Media Source
*
* Copyright 2020 Derek Lesho
*
* 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 "config.h"
#include <gst/gst.h>
#include "gst_private.h"
#include "gst_cbs.h"
#include <assert.h>
#include <stdarg.h>
#include <assert.h>
#define COBJMACROS
#define NONAMELESSUNION
#include "mfapi.h"
#include "mferror.h"
#include "mfidl.h"
#include "mfobjects.h"
#include "wine/debug.h"
#include "wine/heap.h"
#include "wine/list.h"
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
struct media_stream
{
IMFMediaStream IMFMediaStream_iface;
LONG ref;
struct media_source *parent_source;
IMFMediaEventQueue *event_queue;
IMFStreamDescriptor *descriptor;
GstElement *appsink;
GstPad *their_src, *my_sink;
enum
{
STREAM_INACTIVE,
STREAM_SHUTDOWN,
} state;
DWORD stream_id;
};
struct media_source
{
IMFMediaSource IMFMediaSource_iface;
LONG ref;
IMFMediaEventQueue *event_queue;
IMFByteStream *byte_stream;
struct media_stream **streams;
ULONG stream_count;
GstBus *bus;
GstElement *container;
GstElement *decodebin;
GstPad *my_src, *their_sink;
enum
{
SOURCE_OPENING,
SOURCE_STOPPED,
SOURCE_SHUTDOWN,
} state;
HANDLE no_more_pads_event;
};
static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface)
{
return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface);
}
static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface)
{
return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface);
}
static GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
GstBuffer **buf)
{
struct media_source *source = gst_pad_get_element_private(pad);
IMFByteStream *byte_stream = source->byte_stream;
ULONG bytes_read;
GstMapInfo info;
BOOL is_eof;
HRESULT hr;
TRACE("requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), source, *buf);
if (ofs != GST_BUFFER_OFFSET_NONE)
{
if (FAILED(IMFByteStream_SetCurrentPosition(byte_stream, ofs)))
return GST_FLOW_ERROR;
}
if (FAILED(IMFByteStream_IsEndOfStream(byte_stream, &is_eof)))
return GST_FLOW_ERROR;
if (is_eof)
return GST_FLOW_EOS;
if (!(*buf))
*buf = gst_buffer_new_and_alloc(len);
gst_buffer_map(*buf, &info, GST_MAP_WRITE);
hr = IMFByteStream_Read(byte_stream, info.data, len, &bytes_read);
gst_buffer_unmap(*buf, &info);
gst_buffer_set_size(*buf, bytes_read);
if (FAILED(hr))
return GST_FLOW_ERROR;
return GST_FLOW_OK;
}
static gboolean bytestream_query(GstPad *pad, GstObject *parent, GstQuery *query)
{
struct media_source *source = gst_pad_get_element_private(pad);
GstFormat format;
QWORD bytestream_len;
TRACE("GStreamer queries source %p for %s\n", source, GST_QUERY_TYPE_NAME(query));
if (FAILED(IMFByteStream_GetLength(source->byte_stream, &bytestream_len)))
return FALSE;
switch (GST_QUERY_TYPE(query))
{
case GST_QUERY_DURATION:
{
gst_query_parse_duration(query, &format, NULL);
if (format == GST_FORMAT_PERCENT)
{
gst_query_set_duration(query, GST_FORMAT_PERCENT, GST_FORMAT_PERCENT_MAX);
return TRUE;
}
else if (format == GST_FORMAT_BYTES)
{
QWORD length;
IMFByteStream_GetLength(source->byte_stream, &length);
gst_query_set_duration(query, GST_FORMAT_BYTES, length);
return TRUE;
}
return FALSE;
}
case GST_QUERY_SEEKING:
{
gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
if (format != GST_FORMAT_BYTES)
{
WARN("Cannot seek using format \"%s\".\n", gst_format_get_name(format));
return FALSE;
}
gst_query_set_seeking(query, GST_FORMAT_BYTES, 1, 0, bytestream_len);
return TRUE;
}
case GST_QUERY_SCHEDULING:
{
gst_query_set_scheduling(query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0);
gst_query_add_scheduling_mode(query, GST_PAD_MODE_PULL);
return TRUE;
}
default:
{
WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query));
return FALSE;
}
}
}
static gboolean bytestream_pad_mode_activate(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
{
struct media_source *source = gst_pad_get_element_private(pad);
TRACE("%s source pad for mediasource %p in %s mode.\n",
activate ? "Activating" : "Deactivating", source, gst_pad_mode_get_name(mode));
return mode == GST_PAD_MODE_PULL;
}
static gboolean bytestream_pad_event_process(GstPad *pad, GstObject *parent, GstEvent *event)
{
struct media_source *source = gst_pad_get_element_private(pad);
TRACE("source %p, type \"%s\".\n", source, GST_EVENT_TYPE_NAME(event));
switch (event->type) {
/* the seek event should fail in pull mode */
case GST_EVENT_SEEK:
return FALSE;
default:
WARN("Ignoring \"%s\" event.\n", GST_EVENT_TYPE_NAME(event));
case GST_EVENT_TAG:
case GST_EVENT_QOS:
case GST_EVENT_RECONFIGURE:
return gst_pad_event_default(pad, parent, event);
}
return TRUE;
}
GstBusSyncReply bus_watch(GstBus *bus, GstMessage *message, gpointer user)
{
struct media_source *source = user;
gchar *dbg_info = NULL;
GError *err = NULL;
TRACE("source %p message type %s\n", source, GST_MESSAGE_TYPE_NAME(message));
switch (message->type)
{
case GST_MESSAGE_ERROR:
gst_message_parse_error(message, &err, &dbg_info);
ERR("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
ERR("%s\n", dbg_info);
g_error_free(err);
g_free(dbg_info);
break;
case GST_MESSAGE_WARNING:
gst_message_parse_warning(message, &err, &dbg_info);
WARN("%s: %s\n", GST_OBJECT_NAME(message->src), err->message);
WARN("%s\n", dbg_info);
g_error_free(err);
g_free(dbg_info);
break;
default:
break;
}
gst_message_unref(message);
return GST_BUS_DROP;
}
static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%s %p)\n", stream, debugstr_guid(riid), out);
if (IsEqualIID(riid, &IID_IMFMediaStream) ||
IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
IsEqualIID(riid, &IID_IUnknown))
{
*out = &stream->IMFMediaStream_iface;
}
else
{
FIXME("(%s, %p)\n", debugstr_guid(riid), out);
*out = NULL;
return E_NOINTERFACE;
}
IUnknown_AddRef((IUnknown*)*out);
return S_OK;
}
static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
ULONG ref = InterlockedIncrement(&stream->ref);
TRACE("(%p) ref=%u\n", stream, ref);
return ref;
}
static ULONG WINAPI media_stream_Release(IMFMediaStream *iface)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
ULONG ref = InterlockedDecrement(&stream->ref);
TRACE("(%p) ref=%u\n", stream, ref);
if (!ref)
{
if (stream->event_queue)
IMFMediaEventQueue_Release(stream->event_queue);
heap_free(stream);
}
return ref;
}
static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%#x, %p)\n", stream, flags, event);
return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event);
}
static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%p, %p)\n", stream, callback, state);
return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state);
}
static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%p, %p)\n", stream, result, event);
return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event);
}
static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type,
HRESULT hr, const PROPVARIANT *value)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%d, %s, %#x, %p)\n", stream, event_type, debugstr_guid(ext_type), hr, value);
return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value);
}
static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
FIXME("stub (%p)->(%p)\n", stream, source);
if (stream->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%p)\n", stream, descriptor);
if (stream->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
IMFStreamDescriptor_AddRef(stream->descriptor);
*descriptor = stream->descriptor;
return S_OK;
}
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
{
struct media_stream *stream = impl_from_IMFMediaStream(iface);
TRACE("(%p)->(%p)\n", iface, token);
if (stream->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static const IMFMediaStreamVtbl media_stream_vtbl =
{
media_stream_QueryInterface,
media_stream_AddRef,
media_stream_Release,
media_stream_GetEvent,
media_stream_BeginGetEvent,
media_stream_EndGetEvent,
media_stream_QueueEvent,
media_stream_GetMediaSource,
media_stream_GetStreamDescriptor,
media_stream_RequestSample
};
static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
{
struct media_stream *object = heap_alloc_zero(sizeof(*object));
HRESULT hr;
TRACE("(%p %p)->(%p)\n", source, pad, out_stream);
object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl;
object->ref = 1;
IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
object->parent_source = source;
object->their_src = pad;
object->stream_id = stream_id;
object->state = STREAM_INACTIVE;
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
goto fail;
if (!(object->appsink = gst_element_factory_make("appsink", NULL)))
{
hr = E_OUTOFMEMORY;
goto fail;
}
gst_bin_add(GST_BIN(object->parent_source->container), object->appsink);
g_object_set(object->appsink, "sync", FALSE, NULL);
g_object_set(object->appsink, "max-buffers", 5, NULL);
object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
gst_pad_link(object->their_src, object->my_sink);
gst_element_sync_state_with_parent(object->appsink);
TRACE("->(%p)\n", object);
*out_stream = object;
return S_OK;
fail:
WARN("Failed to construct media stream, hr %#x.\n", hr);
IMFMediaStream_Release(&object->IMFMediaStream_iface);
return hr;
}
static HRESULT media_stream_init_desc(struct media_stream *stream)
{
GstCaps *current_caps = gst_pad_get_current_caps(stream->their_src);
IMFMediaTypeHandler *type_handler;
IMFMediaType *stream_type = NULL;
HRESULT hr;
stream_type = mf_media_type_from_caps(current_caps);
gst_caps_unref(current_caps);
if (!stream_type)
return E_FAIL;
hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor);
IMFMediaType_Release(stream_type);
if (FAILED(hr))
return hr;
if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
return hr;
hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type);
IMFMediaTypeHandler_Release(type_handler);
return hr;
}
static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
TRACE("(%p)->(%s %p)\n", source, debugstr_guid(riid), out);
if (IsEqualIID(riid, &IID_IMFMediaSource) ||
IsEqualIID(riid, &IID_IMFMediaEventGenerator) ||
IsEqualIID(riid, &IID_IUnknown))
{
*out = &source->IMFMediaSource_iface;
}
else
{
FIXME("(%s, %p)\n", debugstr_guid(riid), out);
*out = NULL;
return E_NOINTERFACE;
}
IUnknown_AddRef((IUnknown*)*out);
return S_OK;
}
static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
ULONG ref = InterlockedIncrement(&source->ref);
TRACE("(%p) ref=%u\n", source, ref);
return ref;
}
static ULONG WINAPI media_source_Release(IMFMediaSource *iface)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
ULONG ref = InterlockedDecrement(&source->ref);
TRACE("(%p) ref=%u\n", source, ref);
if (!ref)
{
IMFMediaSource_Shutdown(&source->IMFMediaSource_iface);
IMFMediaEventQueue_Release(source->event_queue);
heap_free(source);
}
return ref;
}
static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
TRACE("(%p)->(%#x, %p)\n", source, flags, event);
return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event);
}
static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
TRACE("(%p)->(%p, %p)\n", source, callback, state);
return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state);
}
static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
TRACE("(%p)->(%p, %p)\n", source, result, event);
return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event);
}
static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type,
HRESULT hr, const PROPVARIANT *value)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
TRACE("(%p)->(%d, %s, %#x, %p)\n", source, event_type, debugstr_guid(ext_type), hr, value);
return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value);
}
static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
FIXME("(%p)->(%p): stub\n", source, characteristics);
if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
FIXME("(%p)->(%p): stub\n", source, descriptor);
if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor,
const GUID *time_format, const PROPVARIANT *start_position)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
FIXME("(%p)->(%p, %p, %p): stub\n", source, descriptor, time_format, start_position);
if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
FIXME("(%p): stub\n", source);
if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
FIXME("(%p): stub\n", source);
if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
return E_NOTIMPL;
}
static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
unsigned int i;
TRACE("(%p)\n", source);
if (source->state == SOURCE_SHUTDOWN)
return MF_E_SHUTDOWN;
source->state = SOURCE_SHUTDOWN;
if (source->container)
{
gst_element_set_state(source->container, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(source->container));
}
if (source->my_src)
gst_object_unref(GST_OBJECT(source->my_src));
if (source->their_sink)
gst_object_unref(GST_OBJECT(source->their_sink));
if (source->event_queue)
IMFMediaEventQueue_Shutdown(source->event_queue);
if (source->byte_stream)
IMFByteStream_Release(source->byte_stream);
for (i = 0; i < source->stream_count; i++)
{
struct media_stream *stream = source->streams[i];
stream->state = STREAM_SHUTDOWN;
if (stream->my_sink)
gst_object_unref(GST_OBJECT(stream->my_sink));
if (stream->event_queue)
IMFMediaEventQueue_Shutdown(stream->event_queue);
if (stream->descriptor)
IMFStreamDescriptor_Release(stream->descriptor);
if (stream->parent_source)
IMFMediaSource_Release(&stream->parent_source->IMFMediaSource_iface);
IMFMediaStream_Release(&stream->IMFMediaStream_iface);
}
if (source->stream_count)
heap_free(source->streams);
if (source->no_more_pads_event)
CloseHandle(source->no_more_pads_event);
return S_OK;
}
static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
{
media_source_QueryInterface,
media_source_AddRef,
media_source_Release,
media_source_GetEvent,
media_source_BeginGetEvent,
media_source_EndGetEvent,
media_source_QueueEvent,
media_source_GetCharacteristics,
media_source_CreatePresentationDescriptor,
media_source_Start,
media_source_Stop,
media_source_Pause,
media_source_Shutdown,
};
static void stream_added(GstElement *element, GstPad *pad, gpointer user)
{
struct media_source *source = user;
struct media_stream **new_stream_array;
struct media_stream *stream;
if (gst_pad_get_direction(pad) != GST_PAD_SRC)
return;
if (FAILED(new_media_stream(source, pad, source->stream_count, &stream)))
return;
if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array)))))
{
ERR("Failed to add stream to source\n");
IMFMediaStream_Release(&stream->IMFMediaStream_iface);
return;
}
source->streams = new_stream_array;
source->streams[source->stream_count++] = stream;
}
static void stream_removed(GstElement *element, GstPad *pad, gpointer user)
{
struct media_source *source = user;
unsigned int i;
for (i = 0; i < source->stream_count; i++)
{
struct media_stream *stream = source->streams[i];
if (stream->their_src != pad)
continue;
stream->their_src = NULL;
stream->state = STREAM_INACTIVE;
}
}
static void no_more_pads(GstElement *element, gpointer user)
{
struct media_source *source = user;
SetEvent(source->no_more_pads_event);
}
static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_source **out_media_source)
{
GstStaticPadTemplate src_template =
GST_STATIC_PAD_TEMPLATE("mf_src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS_ANY);
struct media_source *object = heap_alloc_zero(sizeof(*object));
unsigned int i;
HRESULT hr;
int ret;
if (!object)
return E_OUTOFMEMORY;
object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl;
object->ref = 1;
object->byte_stream = bytestream;
IMFByteStream_AddRef(bytestream);
object->no_more_pads_event = CreateEventA(NULL, FALSE, FALSE, NULL);
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
goto fail;
object->container = gst_bin_new(NULL);
object->bus = gst_bus_new();
gst_bus_set_sync_handler(object->bus, mf_src_bus_watch_wrapper, object, NULL);
gst_element_set_bus(object->container, object->bus);
object->my_src = gst_pad_new_from_static_template(&src_template, "mf-src");
gst_pad_set_element_private(object->my_src, object);
gst_pad_set_getrange_function(object->my_src, bytestream_wrapper_pull_wrapper);
gst_pad_set_query_function(object->my_src, bytestream_query_wrapper);
gst_pad_set_activatemode_function(object->my_src, bytestream_pad_mode_activate_wrapper);
gst_pad_set_event_function(object->my_src, bytestream_pad_event_process_wrapper);
if (!(object->decodebin = gst_element_factory_make("decodebin", NULL)))
{
WARN("Failed to create decodebin for source\n");
hr = E_OUTOFMEMORY;
goto fail;
}
/* In Media Foundation, sources may read from any media source stream
without fear of blocking due to buffering limits on another. Trailmakers,
a Unity3D engine game does this by only reading from the audio stream once,
and never deselecting this. These properties replicate that behavior.
Note that with most elements, this causes excessive memory use, however
this is also what occurs on Windows.
*/
g_object_set(object->decodebin, "max-size-buffers", 0, NULL);
g_object_set(object->decodebin, "max-size-time", G_GUINT64_CONSTANT(0), NULL);
g_object_set(object->decodebin, "max-size-bytes", 0, NULL);
gst_bin_add(GST_BIN(object->container), object->decodebin);
g_signal_connect(object->decodebin, "pad-added", G_CALLBACK(mf_src_stream_added_wrapper), object);
g_signal_connect(object->decodebin, "pad-removed", G_CALLBACK(mf_src_stream_removed_wrapper), object);
g_signal_connect(object->decodebin, "no-more-pads", G_CALLBACK(mf_src_no_more_pads_wrapper), object);
object->their_sink = gst_element_get_static_pad(object->decodebin, "sink");
if ((ret = gst_pad_link(object->my_src, object->their_sink)) < 0)
{
WARN("Failed to link our bytestream pad to the demuxer input, error %d.\n", ret);
hr = E_FAIL;
goto fail;
}
object->state = SOURCE_OPENING;
gst_element_set_state(object->container, GST_STATE_PAUSED);
ret = gst_element_get_state(object->container, NULL, NULL, -1);
if (ret == GST_STATE_CHANGE_FAILURE)
{
ERR("Failed to play source, error %d.\n", ret);
hr = E_FAIL;
goto fail;
}
WaitForSingleObject(object->no_more_pads_event, INFINITE);
for (i = 0; i < object->stream_count; i++)
{
GstSample *preroll;
g_signal_emit_by_name(object->streams[i]->appsink, "pull-preroll", &preroll);
if (FAILED(hr = media_stream_init_desc(object->streams[i])))
{
ERR("Failed to finish initialization of media stream %p, hr %x.\n", object->streams[i], hr);
IMFMediaStream_Release(&object->streams[i]->IMFMediaStream_iface);
goto fail;
}
gst_sample_unref(preroll);
}
object->state = SOURCE_STOPPED;
*out_media_source = object;
return S_OK;
fail:
WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
IMFMediaSource_Release(&object->IMFMediaSource_iface);
return hr;
}
struct winegstreamer_stream_handler_result
{
struct list entry;
IMFAsyncResult *result;
MF_OBJECT_TYPE obj_type;
IUnknown *object;
};
struct winegstreamer_stream_handler
{
IMFByteStreamHandler IMFByteStreamHandler_iface;
IMFAsyncCallback IMFAsyncCallback_iface;
LONG refcount;
struct list results;
CRITICAL_SECTION cs;
};
static struct winegstreamer_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface)
{
return CONTAINING_RECORD(iface, struct winegstreamer_stream_handler, IMFByteStreamHandler_iface);
}
static struct winegstreamer_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface)
{
return CONTAINING_RECORD(iface, struct winegstreamer_stream_handler, IMFAsyncCallback_iface);
}
static HRESULT WINAPI winegstreamer_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj)
{
TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
if (IsEqualIID(riid, &IID_IMFByteStreamHandler) ||
IsEqualIID(riid, &IID_IUnknown))
{
*obj = iface;
IMFByteStreamHandler_AddRef(iface);
return S_OK;
}
WARN("Unsupported %s.\n", debugstr_guid(riid));
*obj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI winegstreamer_stream_handler_AddRef(IMFByteStreamHandler *iface)
{
struct winegstreamer_stream_handler *handler = impl_from_IMFByteStreamHandler(iface);
ULONG refcount = InterlockedIncrement(&handler->refcount);
TRACE("%p, refcount %u.\n", handler, refcount);
return refcount;
}
static ULONG WINAPI winegstreamer_stream_handler_Release(IMFByteStreamHandler *iface)
{
struct winegstreamer_stream_handler *handler = impl_from_IMFByteStreamHandler(iface);
ULONG refcount = InterlockedDecrement(&handler->refcount);
struct winegstreamer_stream_handler_result *result, *next;
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount)
{
LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct winegstreamer_stream_handler_result, entry)
{
list_remove(&result->entry);
IMFAsyncResult_Release(result->result);
if (result->object)
IUnknown_Release(result->object);
heap_free(result);
}
DeleteCriticalSection(&handler->cs);
heap_free(handler);
}
return refcount;
}
struct create_object_context
{
IUnknown IUnknown_iface;
LONG refcount;
IPropertyStore *props;
IMFByteStream *stream;
WCHAR *url;
DWORD flags;
};
static struct create_object_context *impl_from_IUnknown(IUnknown *iface)
{
return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface);
}
static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
{
TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
if (IsEqualIID(riid, &IID_IUnknown))
{
*obj = iface;
IUnknown_AddRef(iface);
return S_OK;
}
WARN("Unsupported %s.\n", debugstr_guid(riid));
*obj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI create_object_context_AddRef(IUnknown *iface)
{
struct create_object_context *context = impl_from_IUnknown(iface);
ULONG refcount = InterlockedIncrement(&context->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
return refcount;
}
static ULONG WINAPI create_object_context_Release(IUnknown *iface)
{
struct create_object_context *context = impl_from_IUnknown(iface);
ULONG refcount = InterlockedDecrement(&context->refcount);
TRACE("%p, refcount %u.\n", iface, refcount);
if (!refcount)
{
if (context->props)
IPropertyStore_Release(context->props);
if (context->stream)
IMFByteStream_Release(context->stream);
heap_free(context->url);
heap_free(context);
}
return refcount;
}
static const IUnknownVtbl create_object_context_vtbl =
{
create_object_context_QueryInterface,
create_object_context_AddRef,
create_object_context_Release,
};
static WCHAR *heap_strdupW(const WCHAR *str)
{
WCHAR *ret = NULL;
if (str)
{
unsigned int size;
size = (lstrlenW(str) + 1) * sizeof(WCHAR);
ret = heap_alloc(size);
if (ret)
memcpy(ret, str, size);
}
return ret;
}
static HRESULT WINAPI winegstreamer_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags,
IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state)
{
struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
struct create_object_context *context;
IMFAsyncResult *caller, *item;
HRESULT hr;
TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state);
if (cancel_cookie)
*cancel_cookie = NULL;
if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller)))
return hr;
context = heap_alloc(sizeof(*context));
if (!context)
{
IMFAsyncResult_Release(caller);
return E_OUTOFMEMORY;
}
context->IUnknown_iface.lpVtbl = &create_object_context_vtbl;
context->refcount = 1;
context->props = props;
if (context->props)
IPropertyStore_AddRef(context->props);
context->flags = flags;
context->stream = stream;
if (context->stream)
IMFByteStream_AddRef(context->stream);
if (url)
context->url = heap_strdupW(url);
if (!context->stream)
{
IMFAsyncResult_Release(caller);
IUnknown_Release(&context->IUnknown_iface);
return E_OUTOFMEMORY;
}
hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item);
IUnknown_Release(&context->IUnknown_iface);
if (SUCCEEDED(hr))
{
if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item)))
{
if (cancel_cookie)
{
*cancel_cookie = (IUnknown *)caller;
IUnknown_AddRef(*cancel_cookie);
}
}
IMFAsyncResult_Release(item);
}
IMFAsyncResult_Release(caller);
return hr;
}
static HRESULT WINAPI winegstreamer_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result,
MF_OBJECT_TYPE *obj_type, IUnknown **object)
{
struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
struct winegstreamer_stream_handler_result *found = NULL, *cur;
HRESULT hr;
TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object);
EnterCriticalSection(&this->cs);
LIST_FOR_EACH_ENTRY(cur, &this->results, struct winegstreamer_stream_handler_result, entry)
{
if (result == cur->result)
{
list_remove(&cur->entry);
found = cur;
break;
}
}
LeaveCriticalSection(&this->cs);
if (found)
{
*obj_type = found->obj_type;
*object = found->object;
hr = IMFAsyncResult_GetStatus(found->result);
IMFAsyncResult_Release(found->result);
heap_free(found);
}
else
{
*obj_type = MF_OBJECT_INVALID;
*object = NULL;
hr = MF_E_UNEXPECTED;
}
return hr;
}
static HRESULT WINAPI winegstreamer_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie)
{
struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface);
struct winegstreamer_stream_handler_result *found = NULL, *cur;
TRACE("%p, %p.\n", iface, cancel_cookie);
EnterCriticalSection(&this->cs);
LIST_FOR_EACH_ENTRY(cur, &this->results, struct winegstreamer_stream_handler_result, entry)
{
if (cancel_cookie == (IUnknown *)cur->result)
{
list_remove(&cur->entry);
found = cur;
break;
}
}
LeaveCriticalSection(&this->cs);
if (found)
{
IMFAsyncResult_Release(found->result);
if (found->object)
IUnknown_Release(found->object);
heap_free(found);
}
return found ? S_OK : MF_E_UNEXPECTED;
}
static HRESULT WINAPI winegstreamer_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes)
{
FIXME("stub (%p %p)\n", iface, bytes);
return E_NOTIMPL;
}
static const IMFByteStreamHandlerVtbl winegstreamer_stream_handler_vtbl =
{
winegstreamer_stream_handler_QueryInterface,
winegstreamer_stream_handler_AddRef,
winegstreamer_stream_handler_Release,
winegstreamer_stream_handler_BeginCreateObject,
winegstreamer_stream_handler_EndCreateObject,
winegstreamer_stream_handler_CancelObjectCreation,
winegstreamer_stream_handler_GetMaxNumberOfBytesRequiredForResolution,
};
static HRESULT WINAPI winegstreamer_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
{
if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
IsEqualIID(riid, &IID_IUnknown))
{
*obj = iface;
IMFAsyncCallback_AddRef(iface);
return S_OK;
}
WARN("Unsupported %s.\n", debugstr_guid(riid));
*obj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI winegstreamer_stream_handler_callback_AddRef(IMFAsyncCallback *iface)
{
struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface);
}
static ULONG WINAPI winegstreamer_stream_handler_callback_Release(IMFAsyncCallback *iface)
{
struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface);
}
static HRESULT WINAPI winegstreamer_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
{
return E_NOTIMPL;
}
static HRESULT winegstreamer_stream_handler_create_object(struct winegstreamer_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags,
IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type)
{
TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
if (flags & MF_RESOLUTION_MEDIASOURCE)
{
HRESULT hr;
struct media_source *new_source;
if (FAILED(hr = media_source_constructor(stream, &new_source)))
return hr;
TRACE("->(%p)\n", new_source);
*out_object = (IUnknown*)&new_source->IMFMediaSource_iface;
*out_obj_type = MF_OBJECT_MEDIASOURCE;
return S_OK;
}
else
{
FIXME("flags = %08x\n", flags);
return E_NOTIMPL;
}
}
static HRESULT WINAPI winegstreamer_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
{
struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface);
struct winegstreamer_stream_handler_result *handler_result;
MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID;
IUnknown *object = NULL, *context_object;
struct create_object_context *context;
IMFAsyncResult *caller;
HRESULT hr;
caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result);
if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object)))
{
WARN("Expected context set for callee result.\n");
return hr;
}
context = impl_from_IUnknown(context_object);
hr = winegstreamer_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type);
handler_result = heap_alloc(sizeof(*handler_result));
if (handler_result)
{
handler_result->result = caller;
IMFAsyncResult_AddRef(handler_result->result);
handler_result->obj_type = obj_type;
handler_result->object = object;
EnterCriticalSection(&handler->cs);
list_add_tail(&handler->results, &handler_result->entry);
LeaveCriticalSection(&handler->cs);
}
else
{
if (object)
IUnknown_Release(object);
hr = E_OUTOFMEMORY;
}
IUnknown_Release(&context->IUnknown_iface);
IMFAsyncResult_SetStatus(caller, hr);
MFInvokeCallback(caller);
return S_OK;
}
static const IMFAsyncCallbackVtbl winegstreamer_stream_handler_callback_vtbl =
{
winegstreamer_stream_handler_callback_QueryInterface,
winegstreamer_stream_handler_callback_AddRef,
winegstreamer_stream_handler_callback_Release,
winegstreamer_stream_handler_callback_GetParameters,
winegstreamer_stream_handler_callback_Invoke,
};
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj)
{
struct winegstreamer_stream_handler *this;
HRESULT hr;
TRACE("%s, %p.\n", debugstr_guid(riid), obj);
this = heap_alloc_zero(sizeof(*this));
if (!this)
return E_OUTOFMEMORY;
list_init(&this->results);
InitializeCriticalSection(&this->cs);
this->IMFByteStreamHandler_iface.lpVtbl = &winegstreamer_stream_handler_vtbl;
this->IMFAsyncCallback_iface.lpVtbl = &winegstreamer_stream_handler_callback_vtbl;
this->refcount = 1;
hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj);
IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
return hr;
}
/* helper for callback forwarding */
void perform_cb_media_source(struct cb_data *cbdata)
{
switch(cbdata->type)
{
case BYTESTREAM_WRAPPER_PULL:
{
struct getrange_data *data = &cbdata->u.getrange_data;
cbdata->u.getrange_data.ret = bytestream_wrapper_pull(data->pad, data->parent,
data->ofs, data->len, data->buf);
break;
}
case BYTESTREAM_QUERY:
{
struct query_function_data *data = &cbdata->u.query_function_data;
cbdata->u.query_function_data.ret = bytestream_query(data->pad, data->parent, data->query);
break;
}
case BYTESTREAM_PAD_MODE_ACTIVATE:
{
struct activate_mode_data *data = &cbdata->u.activate_mode_data;
cbdata->u.activate_mode_data.ret = bytestream_pad_mode_activate(data->pad, data->parent, data->mode, data->activate);
break;
}
case BYTESTREAM_PAD_EVENT_PROCESS:
{
struct event_src_data *data = &cbdata->u.event_src_data;
cbdata->u.event_src_data.ret = bytestream_pad_event_process(data->pad, data->parent, data->event);
break;
}
case MF_SRC_BUS_WATCH:
{
struct watch_bus_data *data = &cbdata->u.watch_bus_data;
cbdata->u.watch_bus_data.ret = bus_watch(data->bus, data->msg, data->user);
break;
}
case MF_SRC_STREAM_ADDED:
{
struct pad_added_data *data = &cbdata->u.pad_added_data;
stream_added(data->element, data->pad, data->user);
break;
}
case MF_SRC_STREAM_REMOVED:
{
struct pad_removed_data *data = &cbdata->u.pad_removed_data;
stream_removed(data->element, data->pad, data->user);
break;
}
case MF_SRC_NO_MORE_PADS:
{
struct no_more_pads_data *data = &cbdata->u.no_more_pads_data;
no_more_pads(data->element, data->user);
break;
}
default:
{
assert(0);
}
}
}