Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/mfplat/tests/mfplat.c | 11 +- dlls/winegstreamer/gst_cbs.c | 125 +++ dlls/winegstreamer/gst_cbs.h | 33 +- dlls/winegstreamer/media_source.c | 1201 ++++++++++++++++++++++++++++- 4 files changed, 1349 insertions(+), 21 deletions(-)
diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 851b049a4b..aca0fac130 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -493,10 +493,7 @@ static void test_source_resolver(void) ok(obj_type == MF_OBJECT_MEDIASOURCE, "got %d\n", obj_type);
hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, &descriptor); -todo_wine ok(hr == S_OK, "Failed to get presentation descriptor, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests; ok(descriptor != NULL, "got %p\n", descriptor);
hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, 0, &selected, &sd); @@ -507,10 +504,7 @@ todo_wine IMFStreamDescriptor_Release(sd);
hr = IMFMediaTypeHandler_GetMajorType(handler, &guid); -todo_wine ok(hr == S_OK, "Failed to get stream major type, hr %#x.\n", hr); - if (FAILED(hr)) - goto skip_source_tests;
/* Check major/minor type for the test media. */ ok(IsEqualGUID(&guid, &MFMediaType_Video), "Unexpected major type %s.\n", debugstr_guid(&guid)); @@ -598,10 +592,7 @@ todo_wine hr = IMFMediaSource_CreatePresentationDescriptor(mediasource, NULL); ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr);
-skip_source_tests: - - if (descriptor) - IMFPresentationDescriptor_Release(descriptor); + IMFPresentationDescriptor_Release(descriptor); IMFMediaSource_Release(mediasource); IMFByteStream_Release(stream);
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c index a0618798d8..9c27c3490d 100644 --- a/dlls/winegstreamer/gst_cbs.c +++ b/dlls/winegstreamer/gst_cbs.c @@ -49,6 +49,8 @@ static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
if (cbdata->type < GSTDEMUX_MAX) perform_cb_gstdemux(cbdata); + else if (cbdata->type < MEDIA_SOURCE_MAX) + perform_cb_media_source(cbdata);
pthread_mutex_lock(&cbdata->lock); cbdata->finished = 1; @@ -305,3 +307,126 @@ gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
return cbdata.u.query_sink_data.ret; } + +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct cb_data cbdata = { PULL_FROM_BYTESTREAM }; + + cbdata.u.getrange_data.pad = pad; + cbdata.u.getrange_data.parent = parent; + cbdata.u.getrange_data.ofs = ofs; + cbdata.u.getrange_data.len = len; + cbdata.u.getrange_data.buf = buf; + + call_cb(&cbdata); + + return cbdata.u.getrange_data.ret; +} + +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct cb_data cbdata = { QUERY_BYTESTREAM }; + + cbdata.u.query_function_data.pad = pad; + cbdata.u.query_function_data.parent = parent; + cbdata.u.query_function_data.query = query; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) +{ + struct cb_data cbdata = { ACTIVATE_BYTESTREAM_PAD_MODE }; + + cbdata.u.activate_mode_data.pad = pad; + cbdata.u.activate_mode_data.parent = parent; + cbdata.u.activate_mode_data.mode = mode; + cbdata.u.activate_mode_data.activate = activate; + + call_cb(&cbdata); + + return cbdata.u.query_function_data.ret; +} + +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct cb_data cbdata = { PROCESS_BYTESTREAM_PAD_EVENT }; + + cbdata.u.event_src_data.pad = pad; + cbdata.u.event_src_data.parent = parent; + cbdata.u.event_src_data.event = event; + + call_cb(&cbdata); + + return cbdata.u.event_src_data.ret; +} + +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_ADDED }; + + cbdata.u.pad_added_data.element = bin; + cbdata.u.pad_added_data.pad = pad; + cbdata.u.pad_added_data.user = user; + + call_cb(&cbdata); +} + +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) +{ + struct cb_data cbdata = { SOURCE_STREAM_REMOVED }; + + cbdata.u.pad_removed_data.element = element; + cbdata.u.pad_removed_data.pad = pad; + cbdata.u.pad_removed_data.user = user; + + call_cb(&cbdata); +} + +void source_all_streams_wrapper(GstElement *element, gpointer user) +{ + struct cb_data cbdata = { SOURCE_ALL_STREAMS }; + + cbdata.u.no_more_pads_data.element = element; + cbdata.u.no_more_pads_data.user = user; + + call_cb(&cbdata); +} + +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_NEW_SAMPLE }; + + cbdata.u.new_sample_data.appsink = appsink; + cbdata.u.new_sample_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.new_sample_data.ret; +} + +void stream_eos_wrapper(GstElement *appsink, gpointer user) +{ + struct cb_data cbdata = { STREAM_EOS }; + + cbdata.u.eos_data.appsink = appsink; + cbdata.u.eos_data.user = user; + + call_cb(&cbdata); +} + +GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) +{ + struct cb_data cbdata = { WATCH_SOURCE_BUS }; + + cbdata.u.watch_bus_data.bus = bus; + cbdata.u.watch_bus_data.msg = message; + cbdata.u.watch_bus_data.user = user; + + call_cb(&cbdata); + + return cbdata.u.watch_bus_data.ret; +} \ No newline at end of file diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h index 4725f23ad1..2c83f294f4 100644 --- a/dlls/winegstreamer/gst_cbs.h +++ b/dlls/winegstreamer/gst_cbs.h @@ -43,7 +43,18 @@ enum CB_TYPE { AUTOPLUG_BLACKLIST, UNKNOWN_TYPE, QUERY_SINK, - GSTDEMUX_MAX + GSTDEMUX_MAX, + PULL_FROM_BYTESTREAM, + QUERY_BYTESTREAM, + ACTIVATE_BYTESTREAM_PAD_MODE, + PROCESS_BYTESTREAM_PAD_EVENT, + SOURCE_STREAM_ADDED, + SOURCE_STREAM_REMOVED, + SOURCE_ALL_STREAMS, + STREAM_NEW_SAMPLE, + STREAM_EOS, + WATCH_SOURCE_BUS, + MEDIA_SOURCE_MAX, };
struct cb_data { @@ -128,6 +139,15 @@ struct cb_data { GstQuery *query; gboolean ret; } query_sink_data; + struct new_sample_data { + GstElement *appsink; + gpointer user; + GstFlowReturn ret; + } new_sample_data; + struct eos_data { + GstElement *appsink; + gpointer user; + } eos_data; } u;
int finished; @@ -138,6 +158,7 @@ struct cb_data {
void mark_wine_thread(void) DECLSPEC_HIDDEN; void perform_cb_gstdemux(struct cb_data *data) DECLSPEC_HIDDEN; +void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
GstBusSyncReply watch_bus_wrapper(GstBus *bus, GstMessage *msg, gpointer user) DECLSPEC_HIDDEN; void existing_new_pad_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; @@ -154,5 +175,15 @@ GstAutoplugSelectResult autoplug_blacklist_wrapper(GstElement *bin, GstPad *pad, void unknown_type_wrapper(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user) DECLSPEC_HIDDEN; void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; gboolean query_sink_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +GstFlowReturn pull_from_bytestream_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN; +gboolean query_bytestream_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN; +gboolean activate_bytestream_pad_mode_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN; +gboolean process_bytestream_pad_event_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN; +void source_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN; +void source_all_streams_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN; +GstFlowReturn stream_new_sample_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +void stream_eos_wrapper(GstElement *appsink, gpointer user) DECLSPEC_HIDDEN; +GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
#endif diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 13a8db9d16..e7710dbd08 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1,4 +1,9 @@ +#include "config.h" + +#include <gst/gst.h> + #include "gst_private.h" +#include "gst_cbs.h"
#include <stdarg.h>
@@ -8,6 +13,7 @@ #include "mfapi.h" #include "mferror.h" #include "mfidl.h" +#include "mfobjects.h"
#include "wine/debug.h" #include "wine/heap.h" @@ -15,13 +21,559 @@
WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
+static struct source_desc +{ + GstStaticCaps bytestream_caps; +} source_descs[] = +{ + {/*SOURCE_TYPE_MPEG_4*/ + GST_STATIC_CAPS("video/quicktime"), + } +}; + +struct sample_request +{ + struct list entry; + IUnknown *token; +}; + +struct media_source; + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + struct media_source *parent_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + GstElement *parser, *appsink; + GstPad *their_src, *my_sink; + /* usually reflects state of source */ + enum + { + STREAM_INACTIVE, + STREAM_ENABLED, + STREAM_PAUSED, + STREAM_RUNNING, + STREAM_SHUTDOWN, + } state; + BOOL eos; + CRITICAL_SECTION dispatch_samples_cs; + struct list sample_requests; + unsigned int pending_samples; +}; + struct media_source { IMFMediaSource IMFMediaSource_iface; LONG ref; + enum source_type type; IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + struct media_stream **streams; + ULONG stream_count; + IMFPresentationDescriptor *pres_desc; + GstBus *bus; + GstElement *container; + GstElement *demuxer; + GstPad *my_src, *their_sink; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, /* (READY) */ + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; + CRITICAL_SECTION streams_cs; + HANDLE init_complete_event; +}; + +/* stream */ + +static void media_source_notify_stream_ended(struct media_source *source); +static void stream_dispatch_samples(struct media_stream *This) +{ + struct sample_request *req, *cursor2; + unsigned int fufilled_counter = 0; + + if (This->state != STREAM_RUNNING && This->state != STREAM_SHUTDOWN) + return; + + EnterCriticalSection(&This->dispatch_samples_cs); + + LIST_FOR_EACH_ENTRY_SAFE(req, cursor2, &This->sample_requests, struct sample_request, entry) + { + IMFSample *sample; + + if (This->state == STREAM_SHUTDOWN + /* Not sure if this is correct: */ + || (!(This->pending_samples) && This->eos)) + { + if (req->token) + { + IUnknown_Release(req->token); + } + list_remove(&req->entry); + continue; + } + + if (!(This->pending_samples)) + { + break; + } + + /* Get the sample from the appsink, then construct an IMFSample */ + /* We do this in the dispatch function so we can have appsink buffer for us */ + { + GstSample *gst_sample; + + g_signal_emit_by_name(This->appsink, "pull-sample", &gst_sample); + if (!gst_sample) + { + ERR("Appsink has no samples and pending_samples != 0\n"); + break; + } + + sample = mf_sample_from_gst_buffer(gst_sample_get_buffer(gst_sample)); + + gst_sample_unref(gst_sample); + } + + if (req->token) + { + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, req->token); + } + + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample); + + if (req->token) + { + IUnknown_Release(req->token); + } + + list_remove(&req->entry); + + This->pending_samples--; + fufilled_counter++; + } + + TRACE("Fufilled %u sample requests. %u pending samples. %s more requests.\n", + fufilled_counter, This->pending_samples, list_empty(&This->sample_requests) ? "no" : ""); + + if (This->eos && !This->pending_samples && This->state == STREAM_RUNNING) + { + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty); + media_source_notify_stream_ended(This->parent_source); + } + LeaveCriticalSection(&This->dispatch_samples_cs); +} + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &This->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 *This = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%u\n", This, ref); + + if (!ref) + { + if (This->state != STREAM_SHUTDOWN) + ERR("incomplete cleanup\n"); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%#x, %p)\n", This, flags, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, callback, state); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p, %p)\n", This, result, event); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_EndGetEvent(This->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 *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + FIXME("stub (%p)->(%p)\n", This, source); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + return E_NOTIMPL; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + + TRACE("(%p)->(%p)\n", This, descriptor); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + IMFStreamDescriptor_AddRef(This->descriptor); + *descriptor = This->descriptor; + + return S_OK; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *This = impl_from_IMFMediaStream(iface); + struct sample_request *req; + + TRACE("(%p)->(%p)\n", iface, token); + + if (This->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (This->state == STREAM_INACTIVE || This->state == STREAM_ENABLED) + { + WARN("Stream isn't active\n"); + return MF_E_MEDIA_SOURCE_WRONGSTATE; + } + + if (This->eos && !This->pending_samples) + return MF_E_END_OF_STREAM; + + req = heap_alloc(sizeof(*req)); + if (token) + IUnknown_AddRef(token); + req->token = token; + list_add_tail(&This->sample_requests, &req->entry); + + stream_dispatch_samples(This); + + return S_OK; +} + +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 GstFlowReturn stream_new_sample(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) got sample\n", This); + + if (This->state == STREAM_INACTIVE) + { + GstSample *discard_sample; + g_signal_emit_by_name(This->appsink, "pull-sample", &discard_sample); + gst_sample_unref(discard_sample); + return GST_FLOW_OK; + } + + This->pending_samples++; + stream_dispatch_samples(This); + return GST_FLOW_OK; +} + +void stream_eos(GstElement *appsink, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + + TRACE("(%p) EOS\n", This); + + This->eos = TRUE; + + stream_dispatch_samples(This); +} + +GstBusSyncReply watch_source_bus(GstBus *bus, GstMessage *message, gpointer user) +{ + struct media_stream *This = (struct media_stream *) user; + GError *err = NULL; + gchar *dbg_info = NULL; + + TRACE("source %p message type %s\n", This, 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; + } + + return GST_BUS_DROP; +} + +static void media_stream_teardown(struct media_stream *This) +{ + TRACE("(%p)\n", This); + + This->state = STREAM_SHUTDOWN; + + if (This->their_src) + gst_object_unref(GST_OBJECT(This->their_src)); + if (This->my_sink) + gst_object_unref(GST_OBJECT(This->my_sink)); + + /* Frees pending requests and samples when state == STREAM_SHUTDOWN */ + stream_dispatch_samples(This); + + if (This->descriptor) + IMFStreamDescriptor_Release(This->descriptor); + if (This->event_queue) + IMFMediaEventQueue_Release(This->event_queue); + if (This->parent_source) + IMFMediaSource_Release(&This->parent_source->IMFMediaSource_iface); + + DeleteCriticalSection(&This->dispatch_samples_cs); +} + +static HRESULT media_stream_constructor(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream) +{ + HRESULT hr; + GstCaps *caps = NULL, *desired_caps = NULL; + IMFMediaType *media_type; + IMFMediaTypeHandler *type_handler; + struct media_stream *This = heap_alloc_zero(sizeof(*This)); + + TRACE("(%p %p)->(%p)\n", source, pad, out_stream); + + This->state = STREAM_INACTIVE; + This->pending_samples = 0; + list_init(&This->sample_requests); + This->eos = FALSE; + InitializeCriticalSection(&This->dispatch_samples_cs); + + if (FAILED(hr = IMFMediaSource_AddRef(&source->IMFMediaSource_iface))) + { + goto fail; + } + This->parent_source = source; + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + { + goto fail; + } + + caps = gst_pad_query_caps(pad, NULL); + + if (!(caps)) + { + goto fail; + } + + if (FAILED(hr = MFCreateMediaType(&media_type))) + { + goto fail; + } + + if (!(This->appsink = gst_element_factory_make("appsink", NULL))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(This->parent_source->container), This->appsink); + + gst_caps_ref(caps); + desired_caps = gst_caps_make_writable(caps); + media_type = media_type_from_caps(desired_caps); + TRACE("caps %s desired_caps %s\n", gst_caps_to_string(caps), gst_caps_to_string(desired_caps)); + if (!(gst_caps_is_equal(caps, desired_caps))) + { + GList *parser_list_one, *parser_list_two; + GstElementFactory *parser_factory; + + parser_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_PARSER, 1); + + parser_list_two = gst_element_factory_list_filter(parser_list_one, caps, GST_PAD_SINK, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + + parser_list_two = gst_element_factory_list_filter(parser_list_one, desired_caps, GST_PAD_SRC, 0); + gst_plugin_feature_list_free(parser_list_one); + parser_list_one = parser_list_two; + + if (!(g_list_length(parser_list_one))) + { + gst_plugin_feature_list_free(parser_list_one); + ERR("Failed to find parser for stream\n"); + hr = E_FAIL; + goto fail; + } + + parser_factory = g_list_first(parser_list_one)->data; + TRACE("Found parser %s.\n", GST_ELEMENT_NAME(parser_factory)); + + if (!(This->parser = gst_element_factory_create(parser_factory, NULL))) + { + hr = E_FAIL; + goto fail; + } + gst_bin_add(GST_BIN(This->parent_source->container), This->parser); + if (!(gst_element_link(This->parser, This->appsink))) + { + hr = E_FAIL; + goto fail; + } + + g_object_set(This->appsink, "caps", desired_caps, NULL); + + gst_plugin_feature_list_free(parser_list_one); + } + gst_caps_unref(caps); + gst_caps_unref(desired_caps); + caps = NULL; + + MFCreateStreamDescriptor(stream_id, 1, &media_type, &This->descriptor); + + IMFStreamDescriptor_GetMediaTypeHandler(This->descriptor, &type_handler); + IMFMediaTypeHandler_SetCurrentMediaType(type_handler, media_type); + IMFMediaTypeHandler_Release(type_handler); + IMFMediaType_Release(media_type); + media_type = NULL; + + g_object_set(This->appsink, "emit-signals", TRUE, NULL); + g_object_set(This->appsink, "sync", FALSE, NULL); + g_signal_connect(This->appsink, "new-sample", G_CALLBACK(stream_new_sample_wrapper), This); + g_signal_connect(This->appsink, "eos", G_CALLBACK(stream_eos_wrapper), This); + + This->their_src = pad; + This->my_sink = gst_element_get_static_pad(This->parser ? This->parser : This->appsink, "sink"); + gst_pad_set_element_private(pad, This); + + gst_pad_link(This->their_src, This->my_sink); + + gst_element_sync_state_with_parent(This->appsink); + if (This->parser) + gst_element_sync_state_with_parent(This->parser); + + This->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + This->ref = 1; + + TRACE("->(%p)\n", This); + + *out_stream = This; + return S_OK; + + fail: + WARN("Failed to construct media stream, hr %#x.\n", hr); + + /* Destroy temporary objects */ + if (caps) + gst_caps_unref(caps); + if (media_type) + IMFMediaType_Release(media_type); + + media_stream_teardown(This); + heap_free(This); + return hr; +} + /* source */
static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) @@ -71,6 +623,8 @@ static ULONG WINAPI media_source_Release(IMFMediaSource *iface)
if (!ref) { + if (This->state != SOURCE_SHUTDOWN) + ERR("Application has freed media source without calling ::Shutdown\n"); heap_free(This); }
@@ -83,6 +637,9 @@ static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags,
TRACE("(%p)->(%#x, %p)\n", This, flags, event);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_GetEvent(This->event_queue, flags, event); }
@@ -92,6 +649,9 @@ static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsync
TRACE("(%p)->(%p, %p)\n", This, callback, state);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_BeginGetEvent(This->event_queue, callback, state); }
@@ -101,6 +661,9 @@ static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncRe
TRACE("(%p)->(%p, %p)\n", This, result, event);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_EndGetEvent(This->event_queue, result, event); }
@@ -111,6 +674,9 @@ static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventT
TRACE("(%p)->(%d, %s, %#x, %p)\n", This, event_type, debugstr_guid(ext_type), hr, value);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return IMFMediaEventQueue_QueueEventParamVar(This->event_queue, event_type, ext_type, hr, value); }
@@ -118,28 +684,102 @@ static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWO { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, characteristics); + TRACE("(%p)->(%p)\n", This, characteristics);
- return E_NOTIMPL; + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + *characteristics = 0; + + return S_OK; }
static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p)->(%p): stub\n", This, descriptor); + TRACE("(%p)->(%p)\n", This, descriptor);
- return E_NOTIMPL; + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (!(This->pres_desc)) + { + return MF_E_NOT_INITIALIZED; + } + + IMFPresentationDescriptor_Clone(This->pres_desc, descriptor); + + return S_OK; }
static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, const GUID *time_format, const PROPVARIANT *start_position) { struct media_source *This = impl_from_IMFMediaSource(iface); + int ret; + PROPVARIANT empty_var; + empty_var.vt = VT_EMPTY;
- FIXME("(%p)->(%p, %p, %p): stub\n", This, descriptor, time_format, start_position); + TRACE("(%p)->(%p, %p, %p)\n", This, descriptor, time_format, start_position);
- return E_NOTIMPL; + if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + /* Find out which streams are active */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + IMFStreamDescriptor *stream_desc; + DWORD in_stream_id; + BOOL selected; + + IMFPresentationDescriptor_GetStreamDescriptorByIndex(descriptor, i, &selected, &stream_desc); + IMFStreamDescriptor_GetStreamIdentifier(stream_desc, &in_stream_id); + + for (unsigned int k = 0; k < This->stream_count; k++) + { + DWORD cur_stream_id; + + IMFStreamDescriptor_GetStreamIdentifier(This->streams[k]->descriptor, &cur_stream_id); + + if (in_stream_id == cur_stream_id) + { + BOOL was_active = This->streams[k]->state != STREAM_INACTIVE; + This->streams[k]->state = selected ? STREAM_RUNNING : STREAM_INACTIVE; + if (selected) + { + IMFMediaEventQueue_QueueEventParamUnk(This->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &This->streams[k]->IMFMediaStream_iface); + IMFMediaEventQueue_QueueEventParamVar(This->streams[k]->event_queue, + MEStreamStarted, &GUID_NULL, S_OK, &empty_var); + stream_dispatch_samples(This->streams[k]); + } + } + } + + IMFStreamDescriptor_Release(stream_desc); + } + + if (!(IsEqualIID(time_format, &GUID_NULL) && + (start_position->vt == VT_EMPTY || (start_position->vt == VT_I8 && start_position->u.hVal.QuadPart == 0)))) + { + ERR("unhandled start time\n"); + return MF_E_UNSUPPORTED_TIME_FORMAT; + } + + This->state = SOURCE_RUNNING; + gst_element_set_state(This->container, GST_STATE_PLAYING); + ret = gst_element_get_state(This->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + return E_FAIL; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MESourceStarted, &GUID_NULL, S_OK, &empty_var); + + return S_OK; }
static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) @@ -148,6 +788,9 @@ static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface)
FIXME("(%p): stub\n", This);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
@@ -157,13 +800,42 @@ static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface)
FIXME("(%p): stub\n", This);
+ if (This->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + return E_NOTIMPL; }
static HRESULT media_source_teardown(struct media_source *This) { + if (This->my_src) + gst_object_unref(GST_OBJECT(This->my_src)); + if (This->their_sink) + gst_object_unref(GST_OBJECT(This->their_sink)); + if (This->container) + { + gst_element_set_state(This->container, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(This->container)); + } + if (This->pres_desc) + IMFPresentationDescriptor_Release(This->pres_desc); if (This->event_queue) IMFMediaEventQueue_Release(This->event_queue); + if (This->byte_stream) + IMFByteStream_Release(This->byte_stream); + + for (unsigned int i = 0; i < This->stream_count; i++) + { + media_stream_teardown(This->streams[i]); + IMFMediaStream_Release(&This->streams[i]->IMFMediaStream_iface); + } + + if (This->stream_count) + heap_free(This->streams); + + if (This->init_complete_event) + CloseHandle(This->init_complete_event); + DeleteCriticalSection(&This->streams_cs);
return S_OK; } @@ -172,9 +844,10 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) { struct media_source *This = impl_from_IMFMediaSource(iface);
- FIXME("(%p): stub\n", This); + TRACE("(%p)\n", This);
- return E_NOTIMPL; + This->state = SOURCE_SHUTDOWN; + return media_source_teardown(This); }
static const IMFMediaSourceVtbl IMFMediaSource_vtbl = @@ -194,28 +867,459 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl = media_source_Shutdown, };
+GstFlowReturn pull_from_bytestream(GstPad *pad, GstObject *parent, guint64 ofs, guint len, + GstBuffer **buf) +{ + struct media_source *This = gst_pad_get_element_private(pad); + IMFByteStream *byte_stream = This->byte_stream; + BOOL is_eof; + GstMapInfo info; + ULONG bytes_read; + HRESULT hr; + + TRACE("gstreamer requesting %u bytes at %s from source %p into buffer %p\n", len, wine_dbgstr_longlong(ofs), This, 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; + + *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; + } + GST_BUFFER_OFFSET(*buf) = ofs; + return GST_FLOW_OK; +} + +static gboolean query_bytestream(GstPad *pad, GstObject *parent, GstQuery *query) +{ + struct media_source *This = gst_pad_get_element_private(pad); + GstFormat format; + QWORD bytestream_len; + + TRACE("GStreamer queries source %p for %s\n", This, GST_QUERY_TYPE_NAME(query)); + + if (FAILED(IMFByteStream_GetLength(This->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; + } + 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; + } + case GST_QUERY_CAPS: + { + GstCaps *caps, *filter; + + gst_query_parse_caps(query, &filter); + + caps = gst_static_caps_get(&source_descs[This->type].bytestream_caps); + + if (filter) { + GstCaps* filtered; + filtered = gst_caps_intersect_full( + filter, caps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref(caps); + caps = filtered; + } + gst_query_set_caps_result(query, caps); + gst_caps_unref(caps); + return TRUE; + } + default: + { + WARN("Unhandled query type %s\n", GST_QUERY_TYPE_NAME(query)); + return FALSE; + } + } +} + +static gboolean activate_bytestream_pad_mode(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)); + + switch (mode) { + case GST_PAD_MODE_PULL: + return TRUE; + default: + return FALSE; + } + return FALSE; +} + +static gboolean process_bytestream_pad_event(GstPad *pad, GstObject *parent, GstEvent *event) +{ + struct media_source *This = gst_pad_get_element_private(pad); + + TRACE("filter %p, type "%s".\n", This, GST_EVENT_TYPE_NAME(event)); + + switch (event->type) { + 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; +} + +static void source_stream_added(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + struct media_source *source = (struct media_source *) user; + struct media_stream **new_stream_array; + gchar *g_stream_id; + const char *stream_id_string; + DWORD stream_id; + + EnterCriticalSection(&source->streams_cs); + + g_stream_id = gst_pad_get_stream_id(pad); + stream_id_string = strstr(g_stream_id, "/"); + sscanf(stream_id_string, "/%03u", &stream_id); + TRACE("stream-id: %u\n", stream_id); + g_free(g_stream_id); + + /* find existing stream */ + for (unsigned int i = 0; i < source->stream_count; i++) + { + DWORD existing_stream_id; + IMFStreamDescriptor *descriptor = source->streams[i]->descriptor; + + if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id))) + goto leave; + + if (existing_stream_id == stream_id) + { + struct media_stream *existing_stream = source->streams[i]; + + TRACE("Found existing stream %p\n", existing_stream); + + if (!existing_stream->my_sink) + { + ERR("Couldn't find our sink\n"); + goto leave; + } + + existing_stream->their_src = pad; + gst_pad_set_element_private(pad, existing_stream); + + if (existing_stream->state != STREAM_INACTIVE) + { + GstPadLinkReturn err = gst_pad_link(existing_stream->their_src, existing_stream->my_sink); + if (err != GST_PAD_LINK_OK) + { + ERR("Error linking demuxer to stream %p, err = %d\n", existing_stream, err); + } + gst_element_sync_state_with_parent(existing_stream->appsink); + if (existing_stream->parser) + gst_element_sync_state_with_parent(existing_stream->parser); + } + goto leave; + } + } + + if (FAILED(media_stream_constructor(source, pad, stream_id, &stream))) + { + goto leave; + } + + if (!(new_stream_array = heap_realloc(source->streams, (source->stream_count + 1) * (sizeof(*new_stream_array))))) + { + ERR("Failed to add stream to source\n"); + goto leave; + } + + source->streams = new_stream_array; + source->streams[source->stream_count++] = stream; + + leave: + LeaveCriticalSection(&source->streams_cs); + return; +} + +static void source_stream_removed(GstElement *element, GstPad *pad, gpointer user) +{ + struct media_stream *stream; + + if (gst_pad_get_direction(pad) != GST_PAD_SRC) + { + return; + } + + stream = (struct media_stream *) gst_pad_get_element_private(pad); + + if (stream) + { + TRACE("Stream %p of Source %p removed\n", stream, stream->parent_source); + + if (stream->their_src != pad) + { + ERR("assert: unexpected pad/user combination!!!"); + return; + } + + gst_pad_unlink(stream->their_src, stream->my_sink); + + stream->their_src = NULL; + gst_pad_set_element_private(pad, NULL); + } +} + +static void source_all_streams(GstElement *element, gpointer user) +{ + IMFStreamDescriptor **descriptors; + struct media_source *source = (struct media_source *) user; + + EnterCriticalSection(&source->streams_cs); + if (source->state != SOURCE_OPENING) + goto leave; + + /* Init presentation descriptor */ + + descriptors = heap_alloc(source->stream_count * sizeof(IMFStreamDescriptor*)); + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFMediaStream_GetStreamDescriptor(&source->streams[i]->IMFMediaStream_iface, &descriptors[i]); + } + + if (FAILED(MFCreatePresentationDescriptor(source->stream_count, descriptors, &source->pres_desc))) + goto leave; + + for (unsigned int i = 0; i < source->stream_count; i++) + { + IMFStreamDescriptor_Release(descriptors[i]); + } + heap_free(descriptors); + + SetEvent(source->init_complete_event); + + leave: + LeaveCriticalSection(&source->streams_cs); +} + +static void media_source_notify_stream_ended(struct media_source *This) +{ + PROPVARIANT empty; + empty.vt = VT_EMPTY; + + /* A stream has ended, check whether all have */ + for (unsigned int i = 0; i < This->stream_count; i++) + { + struct media_stream *stream = This->streams[i]; + + if (!stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(This->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source) { + GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE( + "mf_src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + source_descs[type].bytestream_caps); + struct media_source *This = heap_alloc_zero(sizeof(*This)); + GList *demuxer_list_one, *demuxer_list_two; + GstElementFactory *demuxer_factory = NULL; + int ret; HRESULT hr;
if (!This) return E_OUTOFMEMORY;
- if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + This->container = gst_bin_new(NULL); + This->bus = gst_bus_new(); + gst_bus_set_sync_handler(This->bus, watch_source_bus_wrapper, This, NULL); + gst_element_set_bus(This->container, This->bus); + + /* Find demuxer */ + demuxer_list_one = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_DEMUXER, 1); + + demuxer_list_two = gst_element_factory_list_filter(demuxer_list_one, gst_static_caps_get(&source_descs[type].bytestream_caps), GST_PAD_SINK, 0); + gst_plugin_feature_list_free(demuxer_list_one); + + if (!(g_list_length(demuxer_list_two))) + { + ERR("Failed to find demuxer for source.\n"); + gst_plugin_feature_list_free(demuxer_list_two); + hr = E_FAIL; goto fail; + }
+ demuxer_factory = g_list_first(demuxer_list_two)->data; + gst_object_ref(demuxer_factory); + TRACE("Found demuxer %s.\n", GST_ELEMENT_NAME(demuxer_factory)); + + gst_plugin_feature_list_free(demuxer_list_two); + + This->type = type; + This->state = SOURCE_OPENING; + InitializeCriticalSection(&This->streams_cs); + This->init_complete_event = CreateEventA(NULL, TRUE, FALSE, NULL); + + /* Setup interface early as the streams interact with us during initialization */ This->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; This->ref = 1;
+ if (FAILED(hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStream, (void **)&This->byte_stream))) + { + goto fail; + } + + if (FAILED(hr = MFCreateEventQueue(&This->event_queue))) + goto fail; + + /* create demuxer */ + + This->my_src = gst_pad_new_from_static_template(&src_template, "mf-src"); + gst_pad_set_element_private(This->my_src, This); + gst_pad_set_getrange_function(This->my_src, pull_from_bytestream_wrapper); + gst_pad_set_query_function(This->my_src, query_bytestream_wrapper); + gst_pad_set_activatemode_function(This->my_src, activate_bytestream_pad_mode_wrapper); + gst_pad_set_event_function(This->my_src, process_bytestream_pad_event_wrapper); + + This->demuxer = gst_element_factory_create(demuxer_factory, NULL); + if (!(This->demuxer)) + { + WARN("Failed to create demuxer for source\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + gst_bin_add(GST_BIN(This->container), This->demuxer); + + This->their_sink = gst_element_get_static_pad(This->demuxer, "sink"); + + if ((ret = gst_pad_link(This->my_src, This->their_sink)) < 0) + { + WARN("Failed to link our bytestream pad to the demuxer input\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + g_signal_connect(This->demuxer, "pad-added", G_CALLBACK(source_stream_added_wrapper), This); + g_signal_connect(This->demuxer, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), This); + g_signal_connect(This->demuxer, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), This); + + gst_element_set_state(This->container, GST_STATE_PAUSED); + ret = gst_element_get_state(This->container, NULL, NULL, -1); + if (ret == GST_STATE_CHANGE_FAILURE) + { + ERR("Failed to play source.\n"); + hr = E_OUTOFMEMORY; + goto fail; + } + + WaitForSingleObject(This->init_complete_event, INFINITE); + CloseHandle(This->init_complete_event); + This->init_complete_event = NULL; + + /* miscelaneous presentation descriptor setup */ + { + IMFAttributes *byte_stream_attributes; + gint64 total_pres_time = 0; + + if (SUCCEEDED(IMFByteStream_QueryInterface(This->byte_stream, &IID_IMFAttributes, (void **)&byte_stream_attributes))) + { + WCHAR *mimeW = NULL; + DWORD length; + if (SUCCEEDED(IMFAttributes_GetAllocatedString(byte_stream_attributes, &MF_BYTESTREAM_CONTENT_TYPE, &mimeW, &length))) + { + IMFPresentationDescriptor_SetString(This->pres_desc, &MF_PD_MIME_TYPE, mimeW); + CoTaskMemFree(mimeW); + } + IMFAttributes_Release(byte_stream_attributes); + } + + for (unsigned int i = 0; i < This->stream_count; i++) + { + GstQuery *query = gst_query_new_duration(GST_FORMAT_TIME); + if (gst_pad_query(This->streams[i]->their_src, query)) + { + gint64 stream_pres_time; + gst_query_parse_duration(query, NULL, &stream_pres_time); + + TRACE("Stream %u has duration %lu\n", i, stream_pres_time); + + if (stream_pres_time > total_pres_time) + total_pres_time = stream_pres_time; + } + else + { + WARN("Unable to get presentation time of stream %u\n", i); + } + } + + if (This->stream_count) + IMFPresentationDescriptor_SetUINT64(This->pres_desc, &MF_PD_DURATION, total_pres_time / 100); + } + + gst_pad_set_active(This->my_src, 1); + gst_element_set_state(This->container, GST_STATE_READY); + if (!(This->pres_desc)) + { + hr = E_FAIL; + goto fail; + } + + This->state = SOURCE_STOPPED; + *out_media_source = This; return S_OK;
fail: WARN("Failed to construct MFMediaSource, hr %#x.\n", hr);
+ if (demuxer_factory) + gst_object_unref(demuxer_factory); media_source_teardown(This); heap_free(This); + *out_media_source = NULL; return hr; }
@@ -573,6 +1677,9 @@ static HRESULT container_stream_handler_create_object(struct container_stream_ha { TRACE("(%p %s %p %u %p %p %p)\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type);
+ if (!(init_gstreamer())) + return E_FAIL; + if (flags & MF_RESOLUTION_MEDIASOURCE) { HRESULT hr; @@ -676,4 +1783,78 @@ HRESULT container_stream_handler_construct(REFIID riid, void **obj, enum source_ IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface);
return hr; -} \ No newline at end of file +} + +/* helper for callback forwarding */ +void perform_cb_media_source(struct cb_data *cbdata) +{ + switch(cbdata->type) + { + case PULL_FROM_BYTESTREAM: + { + struct getrange_data *data = &cbdata->u.getrange_data; + cbdata->u.getrange_data.ret = pull_from_bytestream(data->pad, data->parent, + data->ofs, data->len, data->buf); + break; + } + case QUERY_BYTESTREAM: + { + struct query_function_data *data = &cbdata->u.query_function_data; + cbdata->u.query_function_data.ret = query_bytestream(data->pad, data->parent, data->query); + break; + } + case ACTIVATE_BYTESTREAM_PAD_MODE: + { + struct activate_mode_data *data = &cbdata->u.activate_mode_data; + cbdata->u.activate_mode_data.ret = activate_bytestream_pad_mode(data->pad, data->parent, data->mode, data->activate); + break; + } + case PROCESS_BYTESTREAM_PAD_EVENT: + { + struct event_src_data *data = &cbdata->u.event_src_data; + cbdata->u.event_src_data.ret = process_bytestream_pad_event(data->pad, data->parent, data->event); + break; + } + case SOURCE_STREAM_ADDED: + { + struct pad_added_data *data = &cbdata->u.pad_added_data; + source_stream_added(data->element, data->pad, data->user); + break; + } + case SOURCE_STREAM_REMOVED: + { + struct pad_removed_data *data = &cbdata->u.pad_removed_data; + source_stream_removed(data->element, data->pad, data->user); + break; + } + case SOURCE_ALL_STREAMS: + { + struct no_more_pads_data *data = &cbdata->u.no_more_pads_data; + source_all_streams(data->element, data->user); + break; + } + case STREAM_NEW_SAMPLE: + { + struct new_sample_data *data = &cbdata->u.new_sample_data; + cbdata->u.new_sample_data.ret = stream_new_sample(data->appsink, data->user); + break; + } + case STREAM_EOS: + { + struct eos_data *data = &cbdata->u.eos_data; + stream_eos(data->appsink, data->user); + break; + } + case WATCH_SOURCE_BUS: + { + struct watch_bus_data *data = &cbdata->u.watch_bus_data; + cbdata->u.watch_bus_data.ret = watch_source_bus(data->bus, data->msg, data->user); + break; + } + default: + { + ERR("Wrong callback forwarder called\n"); + return; + } + } +}