Commit f9c43fc9 authored by Zebediah Figura's avatar Zebediah Figura Committed by Alexandre Julliard

winegstreamer: Reimplement the media source on top of the wg_parser object.

parent 6f785fa7
......@@ -8,7 +8,6 @@ PARENTSRC = ../strmbase
C_SRCS = \
audioconvert.c \
filter.c \
gst_cbs.c \
gstdemux.c \
main.c \
media_source.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;
......
/*
* 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);
}
/*
* 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
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment