[PATCH 5/6] winegstreamer: Reimplement the media source on top of the wg_parser object.
Zebediah Figura
z.figura12 at gmail.com
Thu Feb 18 17:01:27 CST 2021
Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
dlls/winegstreamer/Makefile.in | 1 -
dlls/winegstreamer/audioconvert.c | 12 -
dlls/winegstreamer/gst_cbs.c | 235 ----------
dlls/winegstreamer/gst_cbs.h | 110 -----
dlls/winegstreamer/gst_private.h | 8 +-
dlls/winegstreamer/gstdemux.c | 19 -
dlls/winegstreamer/main.c | 2 -
dlls/winegstreamer/media_source.c | 699 +++++++-----------------------
dlls/winegstreamer/mfplat.c | 460 ++++++++------------
dlls/winegstreamer/wg_parser.c | 12 +-
10 files changed, 337 insertions(+), 1221 deletions(-)
delete mode 100644 dlls/winegstreamer/gst_cbs.c
delete mode 100644 dlls/winegstreamer/gst_cbs.h
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in
index c5e4ed1ef42..debdae2d4bb 100644
--- a/dlls/winegstreamer/Makefile.in
+++ b/dlls/winegstreamer/Makefile.in
@@ -8,7 +8,6 @@ PARENTSRC = ../strmbase
C_SRCS = \
audioconvert.c \
filter.c \
- gst_cbs.c \
gstdemux.c \
main.c \
media_source.c \
diff --git a/dlls/winegstreamer/audioconvert.c b/dlls/winegstreamer/audioconvert.c
index 85f44dd8856..f4e841fe3e4 100644
--- a/dlls/winegstreamer/audioconvert.c
+++ b/dlls/winegstreamer/audioconvert.c
@@ -277,7 +277,6 @@ fail:
static HRESULT WINAPI audio_converter_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
{
GUID major_type, subtype;
- GstCaps *input_caps;
DWORD unused;
HRESULT hr;
@@ -323,11 +322,6 @@ static HRESULT WINAPI audio_converter_SetInputType(IMFTransform *iface, DWORD id
if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float))
return MF_E_INVALIDTYPE;
- if (!(input_caps = caps_from_mf_media_type(type)))
- return MF_E_INVALIDTYPE;
-
- gst_caps_unref(input_caps);
-
if (flags & MFT_SET_TYPE_TEST_ONLY)
return S_OK;
@@ -356,7 +350,6 @@ static HRESULT WINAPI audio_converter_SetOutputType(IMFTransform *iface, DWORD i
{
struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface);
GUID major_type, subtype;
- GstCaps *output_caps;
DWORD unused;
HRESULT hr;
@@ -403,11 +396,6 @@ static HRESULT WINAPI audio_converter_SetOutputType(IMFTransform *iface, DWORD i
if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float))
return MF_E_INVALIDTYPE;
- if (!(output_caps = caps_from_mf_media_type(type)))
- return MF_E_INVALIDTYPE;
-
- gst_caps_unref(output_caps);
-
if (flags & MFT_SET_TYPE_TEST_ONLY)
return S_OK;
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
deleted file mode 100644
index d037c195971..00000000000
--- a/dlls/winegstreamer/gst_cbs.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright 2015 Andrew Eikum 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 "config.h"
-
-#include <gst/gst.h>
-
-#include "objbase.h"
-
-#include "wine/list.h"
-
-#include "gst_cbs.h"
-
-static pthread_key_t wine_gst_key;
-
-void mark_wine_thread(void)
-{
- /* set it to non-NULL to indicate that this is a Wine thread */
- pthread_setspecific(wine_gst_key, &wine_gst_key);
-}
-
-static BOOL is_wine_thread(void)
-{
- return pthread_getspecific(wine_gst_key) != NULL;
-}
-
-static pthread_mutex_t cb_list_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t cb_list_cond = PTHREAD_COND_INITIALIZER;
-static struct list cb_list = LIST_INIT(cb_list);
-
-static void CALLBACK perform_cb(TP_CALLBACK_INSTANCE *instance, void *user)
-{
- struct cb_data *cbdata = user;
-
- perform_cb_media_source(cbdata);
-
- pthread_mutex_lock(&cbdata->lock);
- cbdata->finished = 1;
- pthread_cond_broadcast(&cbdata->cond);
- pthread_mutex_unlock(&cbdata->lock);
-}
-
-static DWORD WINAPI dispatch_thread(void *user)
-{
- struct cb_data *cbdata;
-
- CoInitializeEx(NULL, COINIT_MULTITHREADED);
-
- pthread_mutex_lock(&cb_list_lock);
-
- while (1)
- {
- while (list_empty(&cb_list)) pthread_cond_wait(&cb_list_cond, &cb_list_lock);
-
- cbdata = LIST_ENTRY(list_head(&cb_list), struct cb_data, entry);
- list_remove(&cbdata->entry);
- TrySubmitThreadpoolCallback(&perform_cb, cbdata, NULL);
- }
-
- pthread_mutex_unlock(&cb_list_lock);
-
- CoUninitialize();
-
- return 0;
-}
-
-void start_dispatch_thread(void)
-{
- pthread_key_create(&wine_gst_key, NULL);
- CloseHandle(CreateThread(NULL, 0, &dispatch_thread, NULL, 0, NULL));
-}
-
-/* gstreamer calls our callbacks from threads that Wine did not create. Some
- * callbacks execute code which requires Wine to have created the thread
- * (critical sections, debug logging, dshow client code). Since gstreamer can't
- * provide an API to override its thread creation, we have to intercept all
- * callbacks in code which avoids the Wine thread requirement, and then
- * dispatch those callbacks on a thread that is known to be created by Wine.
- *
- * This thread must not run any code that depends on the Wine TEB!
- */
-
-static void call_cb(struct cb_data *cbdata)
-{
- pthread_mutex_init(&cbdata->lock, NULL);
- pthread_cond_init(&cbdata->cond, NULL);
- cbdata->finished = 0;
-
- if(is_wine_thread()){
- /* The thread which triggered gstreamer to call this callback may
- * already hold a critical section. If so, executing the callback on a
- * worker thread can cause a deadlock. If we are already on a Wine
- * thread, then there is no need to run this callback on a worker
- * thread anyway, which avoids the deadlock issue. */
- perform_cb(NULL, cbdata);
-
- pthread_cond_destroy(&cbdata->cond);
- pthread_mutex_destroy(&cbdata->lock);
-
- return;
- }
-
- pthread_mutex_lock(&cb_list_lock);
-
- list_add_tail(&cb_list, &cbdata->entry);
- pthread_cond_broadcast(&cb_list_cond);
-
- pthread_mutex_lock(&cbdata->lock);
-
- pthread_mutex_unlock(&cb_list_lock);
-
- while(!cbdata->finished)
- pthread_cond_wait(&cbdata->cond, &cbdata->lock);
-
- pthread_mutex_unlock(&cbdata->lock);
-
- pthread_cond_destroy(&cbdata->cond);
- pthread_mutex_destroy(&cbdata->lock);
-}
-
-GstFlowReturn bytestream_wrapper_pull_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
- GstBuffer **buf)
-{
- struct cb_data cbdata = { BYTESTREAM_WRAPPER_PULL };
-
- 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 bytestream_query_wrapper(GstPad *pad, GstObject *parent, GstQuery *query)
-{
- struct cb_data cbdata = { BYTESTREAM_QUERY };
-
- 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 bytestream_pad_mode_activate_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate)
-{
- struct cb_data cbdata = { BYTESTREAM_PAD_MODE_ACTIVATE };
-
- 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.activate_mode_data.ret;
-}
-
-gboolean bytestream_pad_event_process_wrapper(GstPad *pad, GstObject *parent, GstEvent *event)
-{
- struct cb_data cbdata = { BYTESTREAM_PAD_EVENT_PROCESS };
-
- 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;
-}
-
-GstBusSyncReply mf_src_bus_watch_wrapper(GstBus *bus, GstMessage *message, gpointer user)
-{
- struct cb_data cbdata = { MF_SRC_BUS_WATCH };
-
- 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;
-}
-
-void mf_src_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user)
-{
- struct cb_data cbdata = { MF_SRC_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 mf_src_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user)
-{
- struct cb_data cbdata = { MF_SRC_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 mf_src_no_more_pads_wrapper(GstElement *element, gpointer user)
-{
- struct cb_data cbdata = { MF_SRC_NO_MORE_PADS };
-
- cbdata.u.no_more_pads_data.element = element;
- cbdata.u.no_more_pads_data.user = user;
-
- call_cb(&cbdata);
-}
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
deleted file mode 100644
index 1daebee7906..00000000000
--- a/dlls/winegstreamer/gst_cbs.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2015 Andrew Eikum 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
- */
-
-#ifndef GST_CBS_H
-#define GST_CBS_H
-
-#include "wine/list.h"
-#include "windef.h"
-#include <pthread.h>
-
-enum CB_TYPE {
- BYTESTREAM_WRAPPER_PULL,
- BYTESTREAM_QUERY,
- BYTESTREAM_PAD_MODE_ACTIVATE,
- BYTESTREAM_PAD_EVENT_PROCESS,
- MF_SRC_BUS_WATCH,
- MF_SRC_STREAM_ADDED,
- MF_SRC_STREAM_REMOVED,
- MF_SRC_NO_MORE_PADS,
- MEDIA_SOURCE_MAX,
-};
-
-struct cb_data {
- enum CB_TYPE type;
- union {
- struct watch_bus_data {
- GstBus *bus;
- GstMessage *msg;
- gpointer user;
- GstBusSyncReply ret;
- } watch_bus_data;
- struct pad_added_data {
- GstElement *element;
- GstPad *pad;
- gpointer user;
- } pad_added_data;
- struct query_function_data {
- GstPad *pad;
- GstObject *parent;
- GstQuery *query;
- gboolean ret;
- } query_function_data;
- struct activate_mode_data {
- GstPad *pad;
- GstObject *parent;
- GstPadMode mode;
- gboolean activate;
- gboolean ret;
- } activate_mode_data;
- struct no_more_pads_data {
- GstElement *element;
- gpointer user;
- } no_more_pads_data;
- struct getrange_data {
- GstPad *pad;
- GstObject *parent;
- guint64 ofs;
- guint len;
- GstBuffer **buf;
- GstFlowReturn ret;
- } getrange_data;
- struct event_src_data {
- GstPad *pad;
- GstObject *parent;
- GstEvent *event;
- gboolean ret;
- } event_src_data;
- struct pad_removed_data {
- GstElement *element;
- GstPad *pad;
- gpointer user;
- } pad_removed_data;
- } u;
-
- int finished;
- pthread_mutex_t lock;
- pthread_cond_t cond;
- struct list entry;
-};
-
-void mark_wine_thread(void) DECLSPEC_HIDDEN;
-void perform_cb_media_source(struct cb_data *data) DECLSPEC_HIDDEN;
-
-GstFlowReturn got_data_wrapper(GstPad *pad, GstObject *parent, GstBuffer *buf) DECLSPEC_HIDDEN;
-void Gstreamer_transform_pad_added_wrapper(GstElement *filter, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
-GstFlowReturn bytestream_wrapper_pull_wrapper(GstPad *pad, GstObject *parent, guint64 ofs, guint len, GstBuffer **buf) DECLSPEC_HIDDEN;
-gboolean bytestream_query_wrapper(GstPad *pad, GstObject *parent, GstQuery *query) DECLSPEC_HIDDEN;
-gboolean bytestream_pad_mode_activate_wrapper(GstPad *pad, GstObject *parent, GstPadMode mode, gboolean activate) DECLSPEC_HIDDEN;
-gboolean bytestream_pad_event_process_wrapper(GstPad *pad, GstObject *parent, GstEvent *event) DECLSPEC_HIDDEN;
-GstBusSyncReply mf_src_bus_watch_wrapper(GstBus *bus, GstMessage *message, gpointer user) DECLSPEC_HIDDEN;
-void mf_src_stream_added_wrapper(GstElement *bin, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
-void mf_src_stream_removed_wrapper(GstElement *element, GstPad *pad, gpointer user) DECLSPEC_HIDDEN;
-void mf_src_no_more_pads_wrapper(GstElement *element, gpointer user) DECLSPEC_HIDDEN;
-
-#endif
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 128546a0265..03d19af1031 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -180,6 +180,8 @@ struct unix_funcs
void **data, uint64_t *offset, uint32_t *size);
void (CDECL *wg_parser_complete_read_request)(struct wg_parser *parser, bool ret);
+ void (CDECL *wg_parser_set_unlimited_buffering)(struct wg_parser *parser);
+
uint32_t (CDECL *wg_parser_get_stream_count)(struct wg_parser *parser);
struct wg_parser_stream *(CDECL *wg_parser_get_stream)(struct wg_parser *parser, uint32_t index);
@@ -217,10 +219,8 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN;
-HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
-IMFMediaType *mf_media_type_from_caps(const GstCaps *caps) DECLSPEC_HIDDEN;
-GstCaps *caps_from_mf_media_type(IMFMediaType *type) DECLSPEC_HIDDEN;
-IMFSample *mf_sample_from_gst_buffer(GstBuffer *in) DECLSPEC_HIDDEN;
+IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) DECLSPEC_HIDDEN;
+void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) DECLSPEC_HIDDEN;
HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c
index 75b1cece095..910abada48c 100644
--- a/dlls/winegstreamer/gstdemux.c
+++ b/dlls/winegstreamer/gstdemux.c
@@ -23,7 +23,6 @@
#include "config.h"
#include "gst_private.h"
#include "gst_guids.h"
-#include "gst_cbs.h"
#include "vfwmsgs.h"
#include "amvideo.h"
@@ -959,8 +958,6 @@ static HRESULT parser_sink_connect(struct strmbase_sink *iface, IPin *peer, cons
LONGLONG unused;
unsigned int i;
- mark_wine_thread();
-
filter->reader = NULL;
if (FAILED(hr = IPin_QueryInterface(peer, &IID_IAsyncReader, (void **)&filter->reader)))
return hr;
@@ -996,8 +993,6 @@ static void parser_sink_disconnect(struct strmbase_sink *iface)
{
struct parser *filter = impl_from_strmbase_sink(iface);
- mark_wine_thread();
-
GST_RemoveOutputPins(filter);
IAsyncReader_Release(filter->reader);
@@ -1104,8 +1099,6 @@ HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out)
if (!parser_init_gstreamer())
return E_FAIL;
- mark_wine_thread();
-
if (!(object = heap_alloc_zero(sizeof(*object))))
return E_OUTOFMEMORY;
@@ -1199,7 +1192,6 @@ static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface)
{
struct parser_source *pin = impl_from_IMediaSeeking(iface);
- mark_wine_thread();
unix_funcs->wg_parser_stream_seek(pin->wg_stream, pin->seek.dRate, 0, 0,
AM_SEEKING_NoPositioning, AM_SEEKING_NoPositioning);
return S_OK;
@@ -1235,8 +1227,6 @@ static HRESULT WINAPI GST_Seeking_SetPositions(IMediaSeeking *iface,
pin, current ? debugstr_time(*current) : "<null>", current_flags,
stop ? debugstr_time(*stop) : "<null>", stop_flags);
- mark_wine_thread();
-
if (pin->pin.pin.filter->state == State_Stopped)
{
SourceSeekingImpl_SetPositions(iface, current, current_flags, stop, stop_flags);
@@ -1356,8 +1346,6 @@ static HRESULT WINAPI GST_QualityControl_Notify(IQualityControl *iface, IBaseFil
pin, sender, q.Type == Famine ? "Famine" : "Flood", q.Proportion,
debugstr_time(q.Late), debugstr_time(q.TimeStamp));
- mark_wine_thread();
-
/* DirectShow filters sometimes pass negative timestamps (Audiosurf uses the
* current time instead of the time of the last buffer). GstClockTime is
* unsigned, so clamp it to 0. */
@@ -1545,7 +1533,6 @@ static HRESULT GST_RemoveOutputPins(struct parser *This)
unsigned int i;
TRACE("(%p)\n", This);
- mark_wine_thread();
if (!This->sink_connected)
return S_OK;
@@ -1645,8 +1632,6 @@ HRESULT wave_parser_create(IUnknown *outer, IUnknown **out)
if (!parser_init_gstreamer())
return E_FAIL;
- mark_wine_thread();
-
if (!(object = heap_alloc_zero(sizeof(*object))))
return E_OUTOFMEMORY;
@@ -1735,8 +1720,6 @@ HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out)
if (!parser_init_gstreamer())
return E_FAIL;
- mark_wine_thread();
-
if (!(object = heap_alloc_zero(sizeof(*object))))
return E_OUTOFMEMORY;
@@ -1846,8 +1829,6 @@ HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out)
if (!parser_init_gstreamer())
return E_FAIL;
- mark_wine_thread();
-
if (!(object = heap_alloc_zero(sizeof(*object))))
return E_OUTOFMEMORY;
diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c
index 4e46a99f513..ee9f57b6c57 100644
--- a/dlls/winegstreamer/main.c
+++ b/dlls/winegstreamer/main.c
@@ -183,8 +183,6 @@ static BOOL CALLBACK init_gstreamer_proc(INIT_ONCE *once, void *param, void **ct
if (!handle)
ERR("Failed to pin module %p.\n", winegstreamer_instance);
- start_dispatch_thread();
-
return TRUE;
}
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index 604e0ef056c..32b4477ad88 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -1,6 +1,7 @@
/* GStreamer Media Source
*
* Copyright 2020 Derek Lesho
+ * Copyright 2020 Zebediah Figura for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -22,7 +23,6 @@
#include <gst/gst.h>
#include "gst_private.h"
-#include "gst_cbs.h"
#include <assert.h>
#include <stdarg.h>
@@ -49,8 +49,9 @@ struct media_stream
struct media_source *parent_source;
IMFMediaEventQueue *event_queue;
IMFStreamDescriptor *descriptor;
- GstElement *appsink;
- GstPad *their_src, *my_sink;
+
+ struct wg_parser_stream *wg_stream;
+
enum
{
STREAM_INACTIVE,
@@ -97,13 +98,12 @@ struct media_source
DWORD async_commands_queue;
IMFMediaEventQueue *event_queue;
IMFByteStream *byte_stream;
+
+ struct wg_parser *wg_parser;
+
struct media_stream **streams;
ULONG stream_count;
IMFPresentationDescriptor *pres_desc;
- GstBus *bus;
- GstElement *container;
- GstElement *decodebin;
- GstPad *my_src, *their_sink;
enum
{
SOURCE_OPENING,
@@ -111,24 +111,11 @@ struct media_source
SOURCE_RUNNING,
SOURCE_SHUTDOWN,
} state;
- HANDLE no_more_pads_event;
- uint64_t file_size, next_pull_offset;
+ LONGLONG start_time;
HANDLE read_thread;
bool read_thread_shutdown;
-
- pthread_mutex_t mutex;
- pthread_cond_t read_cond, read_done_cond;
- struct
- {
- void *data;
- uint64_t offset;
- uint32_t size;
- bool done;
- bool ret;
- } read_request;
- bool shutdown;
};
static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface)
@@ -271,19 +258,15 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
{
PROPVARIANT *position = &command->u.start.position;
BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY;
- GstStateChangeReturn ret;
unsigned int i;
- gst_element_set_state(source->container, GST_STATE_PAUSED);
- ret = gst_element_get_state(source->container, NULL, NULL, -1);
- assert(ret == GST_STATE_CHANGE_SUCCESS);
-
/* seek to beginning on stop->play */
if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY)
{
position->vt = VT_I8;
position->u.hVal.QuadPart = 0;
}
+ source->start_time = position->u.hVal.QuadPart;
for (i = 0; i < source->stream_count; i++)
{
@@ -291,8 +274,6 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
IMFStreamDescriptor *sd;
IMFMediaTypeHandler *mth;
IMFMediaType *current_mt;
- GstCaps *current_caps;
- GstCaps *prev_caps;
DWORD stream_id;
BOOL was_active;
BOOL selected;
@@ -310,35 +291,20 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
if (selected)
{
+ struct wg_format format;
+
IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth);
IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt);
- current_caps = caps_from_mf_media_type(current_mt);
- g_object_get(stream->appsink, "caps", &prev_caps, NULL);
- if (!prev_caps || !gst_caps_is_equal(prev_caps, current_caps))
- {
- GstEvent *reconfigure_event = gst_event_new_reconfigure();
- g_object_set(stream->appsink, "caps", current_caps, NULL);
- gst_pad_push_event(gst_element_get_static_pad(stream->appsink, "sink"), reconfigure_event);
- }
- gst_caps_unref(current_caps);
- if (prev_caps)
- gst_caps_unref(prev_caps);
+ mf_media_type_to_wg_format(current_mt, &format);
+ unix_funcs->wg_parser_stream_enable(stream->wg_stream, &format);
+
IMFMediaType_Release(current_mt);
IMFMediaTypeHandler_Release(mth);
}
- g_object_set(stream->appsink, "drop", !selected, NULL);
-
if (position->vt != VT_EMPTY)
- {
- GstEvent *seek_event = gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
- GST_SEEK_TYPE_SET, position->u.hVal.QuadPart / 100, GST_SEEK_TYPE_NONE, 0);
-
- gst_pad_push_event(stream->my_sink, seek_event);
-
stream->eos = FALSE;
- }
if (selected)
{
@@ -358,20 +324,25 @@ static void start_pipeline(struct media_source *source, struct source_async_comm
source->state = SOURCE_RUNNING;
- gst_element_set_state(source->container, GST_STATE_PLAYING);
+ unix_funcs->wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0,
+ position->u.hVal.QuadPart, 0, AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning);
+ unix_funcs->wg_parser_end_flush(source->wg_parser);
}
static void stop_pipeline(struct media_source *source)
{
unsigned int i;
- gst_element_set_state(source->container, GST_STATE_PAUSED);
+ unix_funcs->wg_parser_begin_flush(source->wg_parser);
for (i = 0; i < source->stream_count; i++)
{
struct media_stream *stream = source->streams[i];
if (stream->state != STREAM_INACTIVE)
+ {
IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamStopped, &GUID_NULL, S_OK, NULL);
+ unix_funcs->wg_parser_stream_disable(stream->wg_stream);
+ }
}
IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL);
@@ -396,40 +367,114 @@ static void dispatch_end_of_presentation(struct media_source *source)
IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty);
}
-static void wait_on_sample(struct media_stream *stream, IUnknown *token)
+static void send_buffer(struct media_stream *stream, const struct wg_parser_event *event, IUnknown *token)
{
- PROPVARIANT empty_var = {.vt = VT_EMPTY};
- GstSample *gst_sample;
- GstBuffer *buffer;
+ IMFMediaBuffer *buffer;
IMFSample *sample;
+ HRESULT hr;
+ BYTE *data;
- TRACE("%p, %p\n", stream, token);
+ if (FAILED(hr = MFCreateSample(&sample)))
+ {
+ ERR("Failed to create sample, hr %#x.\n", hr);
+ return;
+ }
+
+ if (FAILED(hr = MFCreateMemoryBuffer(event->u.buffer.size, &buffer)))
+ {
+ ERR("Failed to create buffer, hr %#x.\n", hr);
+ IMFSample_Release(sample);
+ return;
+ }
- g_signal_emit_by_name(stream->appsink, "pull-sample", &gst_sample);
- if (gst_sample)
+ if (FAILED(hr = IMFSample_AddBuffer(sample, buffer)))
{
- buffer = gst_sample_get_buffer(gst_sample);
+ ERR("Failed to add buffer, hr %#x.\n", hr);
+ goto out;
+ }
- TRACE("PTS = %llu\n", (unsigned long long int) GST_BUFFER_PTS(buffer));
+ if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, event->u.buffer.size)))
+ {
+ ERR("Failed to set size, hr %#x.\n", hr);
+ goto out;
+ }
- sample = mf_sample_from_gst_buffer(buffer);
- gst_sample_unref(gst_sample);
+ if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL)))
+ {
+ ERR("Failed to lock buffer, hr %#x.\n", hr);
+ goto out;
+ }
- if (token)
- IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
+ if (!unix_funcs->wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, event->u.buffer.size))
+ {
+ unix_funcs->wg_parser_stream_release_buffer(stream->wg_stream);
+ IMFMediaBuffer_Unlock(buffer);
+ goto out;
+ }
+ unix_funcs->wg_parser_stream_release_buffer(stream->wg_stream);
- IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, &GUID_NULL, S_OK, (IUnknown *)sample);
- IMFSample_Release(sample);
+ if (FAILED(hr = IMFMediaBuffer_Unlock(buffer)))
+ {
+ ERR("Failed to unlock buffer, hr %#x.\n", hr);
+ goto out;
}
- else
+
+ if (FAILED(hr = IMFSample_SetSampleTime(sample, event->u.buffer.pts - stream->parent_source->start_time)))
+ {
+ ERR("Failed to set sample time, hr %#x.\n", hr);
+ goto out;
+ }
+
+ if (FAILED(hr = IMFSample_SetSampleDuration(sample, event->u.buffer.duration)))
{
- g_object_get(stream->appsink, "eos", &stream->eos, NULL);
- if (stream->eos)
+ ERR("Failed to set sample duration, hr %#x.\n", hr);
+ goto out;
+ }
+
+ if (token)
+ IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token);
+
+ IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample,
+ &GUID_NULL, S_OK, (IUnknown *)sample);
+
+out:
+ IMFMediaBuffer_Release(buffer);
+ IMFSample_Release(sample);
+}
+
+static void wait_on_sample(struct media_stream *stream, IUnknown *token)
+{
+ PROPVARIANT empty_var = {.vt = VT_EMPTY};
+ struct wg_parser_event event;
+
+ TRACE("%p, %p\n", stream, token);
+
+ for (;;)
+ {
+ if (!unix_funcs->wg_parser_stream_get_event(stream->wg_stream, &event))
+ return;
+
+ TRACE("Got event of type %#x.\n", event.type);
+
+ switch (event.type)
{
- if (token)
- IUnknown_Release(token);
- IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
- dispatch_end_of_presentation(stream->parent_source);
+ case WG_PARSER_EVENT_BUFFER:
+ send_buffer(stream, &event, token);
+ return;
+
+ case WG_PARSER_EVENT_EOS:
+ stream->eos = TRUE;
+ if (token)
+ IUnknown_Release(token);
+ IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var);
+ dispatch_end_of_presentation(stream->parent_source);
+ return;
+
+ case WG_PARSER_EVENT_SEGMENT:
+ break;
+
+ case WG_PARSER_EVENT_NONE:
+ assert(0);
}
}
}
@@ -475,86 +520,6 @@ static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl =
source_async_commands_Invoke,
};
-GstFlowReturn bytestream_wrapper_pull(GstPad *pad, GstObject *parent, guint64 ofs, guint len,
- GstBuffer **buf)
-{
- struct media_source *source = gst_pad_get_element_private(pad);
- GstBuffer *new_buffer = NULL;
- GstMapInfo info;
- bool ret;
-
- 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)
- ofs = source->next_pull_offset;
- source->next_pull_offset = ofs + len;
- if (ofs >= source->file_size)
- return GST_FLOW_EOS;
- if (ofs + len >= source->file_size)
- len = source->file_size - ofs;
-
- if (!(*buf))
- *buf = new_buffer = gst_buffer_new_and_alloc(len);
- gst_buffer_map(*buf, &info, GST_MAP_WRITE);
-
- pthread_mutex_lock(&source->mutex);
-
- assert(!source->read_request.data);
- source->read_request.data = info.data;
- source->read_request.offset = ofs;
- source->read_request.size = len;
- source->read_request.done = false;
- pthread_cond_signal(&source->read_cond);
-
- /* Note that we don't unblock this wait on GST_EVENT_FLUSH_START. We expect
- * the upstream pin to flush if necessary. We should never be blocked on
- * read_thread() not running. */
-
- while (!source->read_request.done)
- pthread_cond_wait(&source->read_done_cond, &source->mutex);
-
- ret = source->read_request.ret;
-
- pthread_mutex_unlock(&source->mutex);
-
- gst_buffer_unmap(*buf, &info);
-
- if (!ret && new_buffer)
- gst_buffer_unref(new_buffer);
- return ret ? GST_FLOW_OK : GST_FLOW_ERROR;
-}
-
-static bool get_read_request(struct media_source *source, void **data, uint64_t *offset, uint32_t *size)
-{
- pthread_mutex_lock(&source->mutex);
-
- while (!source->shutdown && !source->read_request.data)
- pthread_cond_wait(&source->read_cond, &source->mutex);
-
- if (source->shutdown)
- {
- pthread_mutex_unlock(&source->mutex);
- return false;
- }
-
- *data = source->read_request.data;
- *offset = source->read_request.offset;
- *size = source->read_request.size;
-
- pthread_mutex_unlock(&source->mutex);
- return true;
-}
-
-static void complete_read_request(struct media_source *source, bool ret)
-{
- pthread_mutex_lock(&source->mutex);
- source->read_request.done = true;
- source->read_request.ret = ret;
- source->read_request.data = NULL;
- pthread_mutex_unlock(&source->mutex);
- pthread_cond_signal(&source->read_done_cond);
-}
-
static DWORD CALLBACK read_thread(void *arg)
{
struct media_source *source = arg;
@@ -570,137 +535,20 @@ static DWORD CALLBACK read_thread(void *arg)
HRESULT hr;
void *data;
- if (!get_read_request(source, &data, &offset, &size))
+ if (!unix_funcs->wg_parser_get_read_request(source->wg_parser, &data, &offset, &size))
continue;
if (SUCCEEDED(hr = IMFByteStream_SetCurrentPosition(byte_stream, offset)))
hr = IMFByteStream_Read(byte_stream, data, size, &ret_size);
if (SUCCEEDED(hr) && ret_size != size)
ERR("Unexpected short read: requested %u bytes, got %u.\n", size, ret_size);
- complete_read_request(source, SUCCEEDED(hr));
+ unix_funcs->wg_parser_complete_read_request(source->wg_parser, SUCCEEDED(hr));
}
TRACE("Media source is shutting down; exiting.\n");
return 0;
}
-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);
@@ -869,92 +717,28 @@ static const IMFMediaStreamVtbl media_stream_vtbl =
media_stream_RequestSample
};
-/* Setup a chain of elements which should hopefully allow transformations to any IMFMediaType
- the user throws at us through gstreamer's caps negotiation. */
-static HRESULT media_stream_connect_to_sink(struct media_stream *stream)
-{
- GstCaps *source_caps = gst_pad_query_caps(stream->their_src, NULL);
- const gchar *stream_type;
-
- if (!source_caps)
- return E_FAIL;
-
- stream_type = gst_structure_get_name(gst_caps_get_structure(source_caps, 0));
- gst_caps_unref(source_caps);
-
- if (!strcmp(stream_type, "video/x-raw"))
- {
- GstElement *videoconvert = gst_element_factory_make("videoconvert", NULL);
-
- gst_bin_add(GST_BIN(stream->parent_source->container), videoconvert);
-
- stream->my_sink = gst_element_get_static_pad(videoconvert, "sink");
-
- if (!gst_element_link(videoconvert, stream->appsink))
- return E_FAIL;
-
- gst_element_sync_state_with_parent(videoconvert);
- }
- else if (!strcmp(stream_type, "audio/x-raw"))
- {
- GstElement *audioconvert = gst_element_factory_make("audioconvert", NULL);
-
- gst_bin_add(GST_BIN(stream->parent_source->container), audioconvert);
-
- stream->my_sink = gst_element_get_static_pad(audioconvert, "sink");
-
- if (!gst_element_link(audioconvert, stream->appsink))
- return E_FAIL;
-
- gst_element_sync_state_with_parent(audioconvert);
- }
- else
- {
- stream->my_sink = gst_element_get_static_pad(stream->appsink, "sink");
- }
-
- if (gst_pad_link(stream->their_src, stream->my_sink) != GST_PAD_LINK_OK)
- return E_FAIL;
-
- return S_OK;
-}
-
-static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD stream_id, struct media_stream **out_stream)
+static HRESULT new_media_stream(struct media_source *source,
+ struct wg_parser_stream *wg_stream, 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);
+ TRACE("source %p, wg_stream %p, stream_id %u.\n", source, wg_stream, stream_id);
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;
object->eos = FALSE;
+ object->wg_stream = wg_stream;
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);
-
- if (FAILED(hr = media_stream_connect_to_sink(object)))
- goto fail;
-
- gst_element_sync_state_with_parent(object->appsink);
-
TRACE("->(%p)\n", object);
*out_stream = object;
@@ -969,18 +753,17 @@ fail:
static HRESULT media_stream_init_desc(struct media_stream *stream)
{
- GstCaps *current_caps = gst_pad_get_current_caps(stream->their_src);
IMFMediaTypeHandler *type_handler = NULL;
IMFMediaType **stream_types = NULL;
IMFMediaType *stream_type = NULL;
+ struct wg_format format;
DWORD type_count = 0;
- const gchar *major_type;
unsigned int i;
HRESULT hr;
- major_type = gst_structure_get_name(gst_caps_get_structure(current_caps, 0));
+ unix_funcs->wg_parser_stream_get_preferred_format(stream->wg_stream, &format);
- if (!strcmp(major_type, "video/x-raw"))
+ if (format.major_type == WG_MAJOR_TYPE_VIDEO)
{
/* These are the most common native output types of decoders:
https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */
@@ -993,7 +776,7 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
&MFVideoFormat_I420,
};
- IMFMediaType *base_type = mf_media_type_from_caps(current_caps);
+ IMFMediaType *base_type = mf_media_type_from_wg_format(&format);
GUID base_subtype;
IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype);
@@ -1022,7 +805,7 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
}
else
{
- stream_type = mf_media_type_from_caps(current_caps);
+ stream_type = mf_media_type_from_wg_format(&format);
if (stream_type)
{
stream_types = &stream_type;
@@ -1046,7 +829,6 @@ static HRESULT media_stream_init_desc(struct media_stream *stream)
goto done;
done:
- gst_caps_unref(current_caps);
if (type_handler)
IMFMediaTypeHandler_Release(type_handler);
for (i = 0; i < type_count; i++)
@@ -1237,16 +1019,7 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
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));
+ unix_funcs->wg_parser_disconnect(source->wg_parser);
if (source->pres_desc)
IMFPresentationDescriptor_Release(source->pres_desc);
@@ -1261,8 +1034,6 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
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)
@@ -1276,24 +1047,15 @@ static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface)
if (source->read_thread)
{
source->read_thread_shutdown = true;
- pthread_mutex_lock(&source->mutex);
- source->shutdown = true;
- pthread_mutex_unlock(&source->mutex);
- pthread_cond_signal(&source->read_cond);
WaitForSingleObject(source->read_thread, INFINITE);
CloseHandle(source->read_thread);
}
- pthread_mutex_destroy(&source->mutex);
- pthread_cond_destroy(&source->read_cond);
- pthread_cond_destroy(&source->read_done_cond);
+ unix_funcs->wg_parser_destroy(source->wg_parser);
if (source->stream_count)
heap_free(source->streams);
- if (source->no_more_pads_event)
- CloseHandle(source->no_more_pads_event);
-
if (source->async_commands_queue)
MFUnlockWorkQueue(source->async_commands_queue);
@@ -1317,64 +1079,16 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
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);
-
IMFStreamDescriptor **descriptors = NULL;
struct media_source *object;
gint64 total_pres_time = 0;
+ struct wg_parser *parser;
DWORD bytestream_caps;
uint64_t file_size;
unsigned int i;
HRESULT hr;
- int ret;
if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &bytestream_caps)))
return hr;
@@ -1399,8 +1113,6 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
object->ref = 1;
object->byte_stream = bytestream;
IMFByteStream_AddRef(bytestream);
- object->no_more_pads_event = CreateEventA(NULL, FALSE, FALSE, NULL);
- object->file_size = file_size;
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
goto fail;
@@ -1408,81 +1120,47 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue)))
goto fail;
- pthread_mutex_init(&object->mutex, NULL);
- pthread_cond_init(&object->read_cond, NULL);
- pthread_cond_init(&object->read_done_cond, NULL);
-
object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL);
- 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)))
+ if (!(parser = unix_funcs->wg_decodebin_parser_create()))
{
- WARN("Failed to create decodebin for source\n");
hr = E_OUTOFMEMORY;
goto fail;
}
+ object->wg_parser = parser;
- /* 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);
+ object->state = SOURCE_OPENING;
- gst_bin_add(GST_BIN(object->container), object->decodebin);
+ if (FAILED(hr = unix_funcs->wg_parser_connect(parser, file_size)))
+ goto fail;
- 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);
+ /* 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, only reads one sample from the audio stream (and
+ * never deselects it). Remove buffering limits from decodebin in order to
+ * account for this. Note that this does leak memory, but the same memory
+ * leak occurs with native. */
+ unix_funcs->wg_parser_set_unlimited_buffering(parser);
- object->their_sink = gst_element_get_static_pad(object->decodebin, "sink");
+ object->stream_count = unix_funcs->wg_parser_get_stream_count(parser);
- if ((ret = gst_pad_link(object->my_src, object->their_sink)) < 0)
+ if (!(object->streams = heap_alloc_zero(object->stream_count * sizeof(*object->streams))))
{
- WARN("Failed to link our bytestream pad to the demuxer input, error %d.\n", ret);
- hr = E_FAIL;
+ hr = E_OUTOFMEMORY;
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)
+ for (i = 0; i < object->stream_count; ++i)
{
- ERR("Failed to play source, error %d.\n", ret);
- hr = E_FAIL;
- goto fail;
- }
+ if (FAILED(hr = new_media_stream(object, unix_funcs->wg_parser_get_stream(parser, i), i, &object->streams[i])))
+ 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);
}
/* init presentation descriptor */
@@ -1505,23 +1183,11 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
descriptors = NULL;
for (i = 0; i < object->stream_count; i++)
- {
- gint64 stream_pres_time;
- if (gst_pad_query_duration(object->streams[i]->their_src, GST_FORMAT_TIME, &stream_pres_time))
- {
- TRACE("Stream %u has duration %llu\n", i, (unsigned long long int) 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);
- }
- }
+ total_pres_time = max(total_pres_time,
+ unix_funcs->wg_parser_stream_get_duration(object->streams[i]->wg_stream));
if (object->stream_count)
- IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time / 100);
+ IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time);
object->state = SOURCE_STOPPED;
@@ -1992,64 +1658,3 @@ HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj)
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);
- }
- }
-}
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index f300988fc5c..8891facdffe 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -1,5 +1,6 @@
/*
* Copyright 2019 Nikolay Sivov for CodeWeavers
+ * Copyright 2020 Zebediah Figura for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -21,11 +22,14 @@
#include "gst_private.h"
+#include <assert.h>
#include <stdarg.h>
#include "gst_private.h"
#include "mfapi.h"
#include "mfidl.h"
+#include "ks.h"
+#include "ksmedia.h"
#include "wine/debug.h"
#include "wine/heap.h"
@@ -517,351 +521,227 @@ HRESULT mfplat_DllRegisterServer(void)
static const struct
{
const GUID *subtype;
- GstVideoFormat format;
+ enum wg_video_format format;
+}
+video_formats[] =
+{
+ {&MFVideoFormat_ARGB32, WG_VIDEO_FORMAT_BGRA},
+ {&MFVideoFormat_RGB32, WG_VIDEO_FORMAT_BGRx},
+ {&MFVideoFormat_RGB24, WG_VIDEO_FORMAT_BGR},
+ {&MFVideoFormat_RGB555, WG_VIDEO_FORMAT_RGB15},
+ {&MFVideoFormat_RGB565, WG_VIDEO_FORMAT_RGB16},
+ {&MFVideoFormat_AYUV, WG_VIDEO_FORMAT_AYUV},
+ {&MFVideoFormat_I420, WG_VIDEO_FORMAT_I420},
+ {&MFVideoFormat_NV12, WG_VIDEO_FORMAT_NV12},
+ {&MFVideoFormat_UYVY, WG_VIDEO_FORMAT_UYVY},
+ {&MFVideoFormat_YUY2, WG_VIDEO_FORMAT_YUY2},
+ {&MFVideoFormat_YV12, WG_VIDEO_FORMAT_YV12},
+ {&MFVideoFormat_YVYU, WG_VIDEO_FORMAT_YVYU},
+};
+
+static const struct
+{
+ const GUID *subtype;
+ UINT32 depth;
+ enum wg_audio_format format;
}
-uncompressed_video_formats[] =
+audio_formats[] =
{
- {&MFVideoFormat_ARGB32, GST_VIDEO_FORMAT_BGRA},
- {&MFVideoFormat_RGB32, GST_VIDEO_FORMAT_BGRx},
- {&MFVideoFormat_RGB24, GST_VIDEO_FORMAT_BGR},
- {&MFVideoFormat_RGB565, GST_VIDEO_FORMAT_BGR16},
- {&MFVideoFormat_RGB555, GST_VIDEO_FORMAT_BGR15},
+ {&MFAudioFormat_PCM, 8, WG_AUDIO_FORMAT_U8},
+ {&MFAudioFormat_PCM, 16, WG_AUDIO_FORMAT_S16LE},
+ {&MFAudioFormat_PCM, 24, WG_AUDIO_FORMAT_S24LE},
+ {&MFAudioFormat_PCM, 32, WG_AUDIO_FORMAT_S32LE},
+ {&MFAudioFormat_Float, 32, WG_AUDIO_FORMAT_F32LE},
+ {&MFAudioFormat_Float, 64, WG_AUDIO_FORMAT_F64LE},
};
-/* returns NULL if doesn't match exactly */
-IMFMediaType *mf_media_type_from_caps(const GstCaps *caps)
+static inline UINT64 make_uint64(UINT32 high, UINT32 low)
{
- IMFMediaType *media_type;
- GstStructure *info;
- const char *mime_type;
-
- if (TRACE_ON(mfplat))
- {
- gchar *human_readable = gst_caps_to_string(caps);
- TRACE("caps = %s\n", debugstr_a(human_readable));
- g_free(human_readable);
- }
-
- if (FAILED(MFCreateMediaType(&media_type)))
- return NULL;
+ return ((UINT64)high << 32) | low;
+}
- info = gst_caps_get_structure(caps, 0);
- mime_type = gst_structure_get_name(info);
+static IMFMediaType *mf_media_type_from_wg_format_audio(const struct wg_format *format)
+{
+ IMFMediaType *type;
+ unsigned int i;
- if (!strncmp(mime_type, "video", 5))
+ for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
{
- GstVideoInfo video_info;
-
- if (!gst_video_info_from_caps(&video_info, caps))
+ if (format->u.audio.format == audio_formats[i].format)
{
- return NULL;
- }
-
- IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
-
- IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, ((UINT64)video_info.width << 32) | video_info.height);
+ if (FAILED(MFCreateMediaType(&type)))
+ return NULL;
- IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d);
+ IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+ IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, audio_formats[i].subtype);
+ IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, audio_formats[i].depth);
+ IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, format->u.audio.rate);
+ IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, format->u.audio.channels);
+ IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, format->u.audio.channel_mask);
- if (!strcmp(mime_type, "video/x-raw"))
- {
- GUID fourcc_subtype = MFVideoFormat_Base;
- unsigned int i;
-
- IMFMediaType_SetUINT32(media_type, &MF_MT_COMPRESSED, FALSE);
-
- /* First try FOURCC */
- if ((fourcc_subtype.Data1 = gst_video_format_to_fourcc(video_info.finfo->format)))
- {
- IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &fourcc_subtype);
- }
- else
- {
- for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++)
- {
- if (uncompressed_video_formats[i].format == video_info.finfo->format)
- {
- IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_video_formats[i].subtype);
- break;
- }
- }
- if (i == ARRAY_SIZE(uncompressed_video_formats))
- {
- FIXME("Unrecognized uncompressed video format %s\n", gst_video_format_to_string(video_info.finfo->format));
- IMFMediaType_Release(media_type);
- return NULL;
- }
- }
- }
- else
- {
- FIXME("Unrecognized video format %s\n", mime_type);
- return NULL;
+ return type;
}
}
- else if (!strncmp(mime_type, "audio", 5))
- {
- gint rate, channels, bitrate;
- guint64 channel_mask;
- IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
-
- if (gst_structure_get_int(info, "rate", &rate))
- IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate);
-
- if (gst_structure_get_int(info, "channels", &channels))
- IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channels);
- if (gst_structure_get(info, "channel-mask", GST_TYPE_BITMASK, &channel_mask, NULL))
- IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_CHANNEL_MASK, channel_mask);
+ return NULL;
+}
- if (gst_structure_get_int(info, "bitrate", &bitrate))
- IMFMediaType_SetUINT32(media_type, &MF_MT_AVG_BITRATE, bitrate);
+static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format *format)
+{
+ IMFMediaType *type;
+ unsigned int i;
- if (!strcmp(mime_type, "audio/x-raw"))
+ for (i = 0; i < ARRAY_SIZE(video_formats); ++i)
+ {
+ if (format->u.video.format == video_formats[i].format)
{
- GstAudioInfo audio_info;
- DWORD depth;
-
- if (!gst_audio_info_from_caps(&audio_info, caps))
- {
- ERR("Failed to get caps audio info\n");
- IMFMediaType_Release(media_type);
+ if (FAILED(MFCreateMediaType(&type)))
return NULL;
- }
- depth = GST_AUDIO_INFO_DEPTH(&audio_info);
+ IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
+ IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, video_formats[i].subtype);
+ IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE,
+ make_uint64(format->u.video.width, format->u.video.height));
+ IMFMediaType_SetUINT64(type, &MF_MT_FRAME_RATE,
+ make_uint64(format->u.video.fps_n, format->u.video.fps_d));
+ IMFMediaType_SetUINT32(type, &MF_MT_COMPRESSED, FALSE);
- /* validation */
- if ((audio_info.finfo->flags & GST_AUDIO_FORMAT_FLAG_INTEGER && depth > 8) ||
- (audio_info.finfo->flags & GST_AUDIO_FORMAT_FLAG_SIGNED && depth <= 8) ||
- (audio_info.finfo->endianness != G_LITTLE_ENDIAN && depth > 8))
- {
- IMFMediaType_Release(media_type);
- return NULL;
- }
-
- /* conversion */
- switch (audio_info.finfo->flags)
- {
- case GST_AUDIO_FORMAT_FLAG_FLOAT:
- IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float);
- break;
- case GST_AUDIO_FORMAT_FLAG_INTEGER:
- case GST_AUDIO_FORMAT_FLAG_SIGNED:
- IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
- break;
- default:
- FIXME("Unrecognized audio format %x\n", audio_info.finfo->format);
- IMFMediaType_Release(media_type);
- return NULL;
- }
-
- IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, depth);
- }
- else
- {
- FIXME("Unrecognized audio format %s\n", mime_type);
- IMFMediaType_Release(media_type);
- return NULL;
+ return type;
}
}
- else
- {
- IMFMediaType_Release(media_type);
- return NULL;
- }
- return media_type;
+ return NULL;
}
-GstCaps *caps_from_mf_media_type(IMFMediaType *type)
+IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format)
{
- GUID major_type;
- GUID subtype;
- GstCaps *output = NULL;
-
- if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
- return NULL;
- if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
- return NULL;
-
- if (IsEqualGUID(&major_type, &MFMediaType_Video))
+ switch (format->major_type)
{
- UINT64 frame_rate = 0, frame_size = 0;
- DWORD width, height;
- GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
- GUID subtype_base;
- GstVideoInfo info;
- unsigned int i;
-
- if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
+ case WG_MAJOR_TYPE_UNKNOWN:
return NULL;
- width = frame_size >> 32;
- height = frame_size;
- output = gst_caps_new_empty_simple("video/x-raw");
-
- for (i = 0; i < ARRAY_SIZE(uncompressed_video_formats); i++)
- {
- if (IsEqualGUID(uncompressed_video_formats[i].subtype, &subtype))
- {
- format = uncompressed_video_formats[i].format;
- break;
- }
- }
+ case WG_MAJOR_TYPE_AUDIO:
+ return mf_media_type_from_wg_format_audio(format);
- subtype_base = subtype;
- subtype_base.Data1 = 0;
- if (format == GST_VIDEO_FORMAT_UNKNOWN && IsEqualGUID(&MFVideoFormat_Base, &subtype_base))
- format = gst_video_format_from_fourcc(subtype.Data1);
+ case WG_MAJOR_TYPE_VIDEO:
+ return mf_media_type_from_wg_format_video(format);
+ }
- if (format == GST_VIDEO_FORMAT_UNKNOWN)
- {
- FIXME("Unrecognized format %s\n", debugstr_guid(&subtype));
- return NULL;
- }
+ assert(0);
+ return NULL;
+}
- gst_video_info_set_format(&info, format, width, height);
- output = gst_video_info_to_caps(&info);
+static void mf_media_type_to_wg_format_audio(IMFMediaType *type, struct wg_format *format)
+{
+ UINT32 rate, channels, channel_mask, depth;
+ unsigned int i;
+ GUID subtype;
- if (frame_size)
- {
- gst_caps_set_simple(output, "width", G_TYPE_INT, width, NULL);
- gst_caps_set_simple(output, "height", G_TYPE_INT, height, NULL);
- }
- if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)))
- {
- /* Darksiders: Warmastered Edition uses a MF_MT_FRAME_RATE of 0,
- and gstreamer won't accept an undefined number as the framerate. */
- if (!(DWORD32)frame_rate)
- frame_rate = 1;
- gst_caps_set_simple(output, "framerate", GST_TYPE_FRACTION, (DWORD32)(frame_rate >> 32), (DWORD32) frame_rate, NULL);
- }
- return output;
+ if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
+ {
+ FIXME("Subtype is not set.\n");
+ return;
}
- else if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+ if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
{
- DWORD rate = -1, channels = -1, channel_mask = -1;
-
- if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate)))
- {
- ERR("Sample rate not set.\n");
- return NULL;
- }
- if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
+ FIXME("Sample rate is not set.\n");
+ return;
+ }
+ if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels)))
+ {
+ FIXME("Channel count is not set.\n");
+ return;
+ }
+ if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth)))
+ {
+ FIXME("Depth is not set.\n");
+ return;
+ }
+ if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask)))
+ {
+ if (channels == 1)
+ channel_mask = KSAUDIO_SPEAKER_MONO;
+ else if (channels == 2)
+ channel_mask = KSAUDIO_SPEAKER_STEREO;
+ else
{
- ERR("Channel count not set.\n");
- return NULL;
+ FIXME("Channel mask is not set.\n");
+ return;
}
- IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask);
+ }
- if (IsEqualGUID(&subtype, &MFAudioFormat_Float))
- {
- GstAudioInfo float_info;
+ format->major_type = WG_MAJOR_TYPE_AUDIO;
+ format->u.audio.channels = channels;
+ format->u.audio.channel_mask = channel_mask;
+ format->u.audio.rate = rate;
- gst_audio_info_set_format(&float_info, GST_AUDIO_FORMAT_F32LE, rate, channels, NULL);
- output = gst_audio_info_to_caps(&float_info);
- }
- else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM))
- {
- GstAudioFormat pcm_format;
- GstAudioInfo pcm_info;
- DWORD bits_per_sample;
-
- if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits_per_sample)))
- {
- pcm_format = gst_audio_format_build_integer(bits_per_sample > 8, G_LITTLE_ENDIAN, bits_per_sample, bits_per_sample);
-
- gst_audio_info_set_format(&pcm_info, pcm_format, rate, channels, NULL);
- output = gst_audio_info_to_caps(&pcm_info);
- }
- else
- {
- ERR("Bits per sample not set.\n");
- return NULL;
- }
- }
- else
+ for (i = 0; i < ARRAY_SIZE(audio_formats); ++i)
+ {
+ if (IsEqualGUID(&subtype, audio_formats[i].subtype) && depth == audio_formats[i].depth)
{
- FIXME("Unrecognized subtype %s\n", debugstr_guid(&subtype));
- return NULL;
+ format->u.audio.format = audio_formats[i].format;
+ return;
}
-
- if (channel_mask != -1)
- gst_caps_set_simple(output, "channel-mask", GST_TYPE_BITMASK, (guint64) channel_mask, NULL);
-
- return output;
}
-
- FIXME("Unrecognized major type %s\n", debugstr_guid(&major_type));
- return NULL;
+ FIXME("Unrecognized audio subtype %s, depth %u.\n", debugstr_guid(&subtype), depth);
}
-/* IMFSample = GstBuffer
- IMFBuffer = GstMemory */
-
-/* TODO: Future optimization could be to create a custom
- IMFMediaBuffer wrapper around GstMemory, and to utilize
- gst_memory_new_wrapped on IMFMediaBuffer data. However,
- this wouldn't work if we allow the callers to allocate
- the buffers. */
-
-IMFSample* mf_sample_from_gst_buffer(GstBuffer *gst_buffer)
+static void mf_media_type_to_wg_format_video(IMFMediaType *type, struct wg_format *format)
{
- IMFMediaBuffer *mf_buffer = NULL;
- GstMapInfo map_info = {0};
- LONGLONG duration, time;
- BYTE *mapped_buf = NULL;
- IMFSample *out = NULL;
- HRESULT hr;
-
- if (FAILED(hr = MFCreateSample(&out)))
- goto done;
-
- duration = GST_BUFFER_DURATION(gst_buffer);
- time = GST_BUFFER_PTS(gst_buffer);
-
- if (FAILED(hr = IMFSample_SetSampleDuration(out, duration / 100)))
- goto done;
-
- if (FAILED(hr = IMFSample_SetSampleTime(out, time / 100)))
- goto done;
+ UINT64 frame_rate, frame_size;
+ unsigned int i;
+ GUID subtype;
- if (!gst_buffer_map(gst_buffer, &map_info, GST_MAP_READ))
+ if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
{
- hr = E_FAIL;
- goto done;
+ FIXME("Subtype is not set.\n");
+ return;
+ }
+ if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
+ {
+ FIXME("Frame size is not set.\n");
+ return;
}
- if (FAILED(hr = MFCreateMemoryBuffer(map_info.maxsize, &mf_buffer)))
- goto done;
-
- if (FAILED(hr = IMFMediaBuffer_Lock(mf_buffer, &mapped_buf, NULL, NULL)))
- goto done;
+ format->major_type = WG_MAJOR_TYPE_VIDEO;
+ format->u.video.width = (UINT32)(frame_size >> 32);
+ format->u.video.height = (UINT32)frame_size;
+ format->u.video.fps_n = 1;
+ format->u.video.fps_d = 1;
- memcpy(mapped_buf, map_info.data, map_info.size);
+ if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate)
+ {
+ format->u.video.fps_n = (UINT32)(frame_rate >> 32);
+ format->u.video.fps_d = (UINT32)frame_rate;
+ }
- if (FAILED(hr = IMFMediaBuffer_Unlock(mf_buffer)))
- goto done;
+ for (i = 0; i < ARRAY_SIZE(video_formats); ++i)
+ {
+ if (IsEqualGUID(&subtype, video_formats[i].subtype))
+ {
+ format->u.video.format = video_formats[i].format;
+ return;
+ }
+ }
+ FIXME("Unrecognized video subtype %s.\n", debugstr_guid(&subtype));
+}
- if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(mf_buffer, map_info.size)))
- goto done;
+void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format)
+{
+ GUID major_type;
- if (FAILED(hr = IMFSample_AddBuffer(out, mf_buffer)))
- goto done;
+ memset(format, 0, sizeof(*format));
-done:
- if (mf_buffer)
- IMFMediaBuffer_Release(mf_buffer);
- if (map_info.data)
- gst_buffer_unmap(gst_buffer, &map_info);
- if (FAILED(hr))
+ if (FAILED(IMFMediaType_GetMajorType(type, &major_type)))
{
- ERR("Failed to copy IMFSample to GstBuffer, hr = %#x\n", hr);
- if (out)
- IMFSample_Release(out);
- out = NULL;
+ FIXME("Major type is not set.\n");
+ return;
}
- return out;
+ if (IsEqualGUID(&major_type, &MFMediaType_Audio))
+ mf_media_type_to_wg_format_audio(type, format);
+ else if (IsEqualGUID(&major_type, &MFMediaType_Video))
+ mf_media_type_to_wg_format_video(type, format);
+ else
+ FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type));
}
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c
index 2540ecd80df..257da2538af 100644
--- a/dlls/winegstreamer/wg_parser.c
+++ b/dlls/winegstreamer/wg_parser.c
@@ -43,7 +43,7 @@ struct wg_parser
struct wg_parser_stream **streams;
unsigned int stream_count;
- GstElement *container;
+ GstElement *container, *decodebin;
GstBus *bus;
GstPad *my_src, *their_sink;
@@ -540,6 +540,13 @@ static void CDECL wg_parser_complete_read_request(struct wg_parser *parser, bool
pthread_cond_signal(&parser->read_done_cond);
}
+static void CDECL wg_parser_set_unlimited_buffering(struct wg_parser *parser)
+{
+ g_object_set(parser->decodebin, "max-size-buffers", 0, NULL);
+ g_object_set(parser->decodebin, "max-size-time", G_GUINT64_CONSTANT(0), NULL);
+ g_object_set(parser->decodebin, "max-size-bytes", 0, NULL);
+}
+
static void CDECL wg_parser_stream_get_preferred_format(struct wg_parser_stream *stream, struct wg_format *format)
{
*format = stream->preferred_format;
@@ -1602,6 +1609,7 @@ static BOOL decodebin_parser_init_gst(struct wg_parser *parser)
}
gst_bin_add(GST_BIN(parser->container), element);
+ parser->decodebin = element;
g_signal_connect(element, "pad-added", G_CALLBACK(existing_new_pad), parser);
g_signal_connect(element, "pad-removed", G_CALLBACK(removed_decoded_pad), parser);
@@ -1876,6 +1884,8 @@ static const struct unix_funcs funcs =
wg_parser_get_read_request,
wg_parser_complete_read_request,
+ wg_parser_set_unlimited_buffering,
+
wg_parser_get_stream_count,
wg_parser_get_stream,
--
2.30.1
More information about the wine-devel
mailing list