[PATCH 5/5] winegstreamer: Implement IMFMediaStream::GetStreamDescriptor.
Derek Lesho
dlesho at codeweavers.com
Wed Aug 26 12:35:12 CDT 2020
Signed-off-by: Derek Lesho <dlesho at codeweavers.com>
---
dlls/winegstreamer/gst_cbs.c | 13 ++
dlls/winegstreamer/gst_cbs.h | 8 ++
dlls/winegstreamer/gst_private.h | 4 +
dlls/winegstreamer/media_source.c | 137 +++++++++++++++++++-
dlls/winegstreamer/mfplat.c | 208 ++++++++++++++++++++++++++++++
5 files changed, 367 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/gst_cbs.c b/dlls/winegstreamer/gst_cbs.c
index 4755f5b42f1..e56c987eb38 100644
--- a/dlls/winegstreamer/gst_cbs.c
+++ b/dlls/winegstreamer/gst_cbs.c
@@ -404,3 +404,16 @@ void source_all_streams_wrapper(GstElement *element, gpointer user)
call_cb(&cbdata);
}
+
+GstPadProbeReturn caps_listener_wrapper(GstPad *pad, GstPadProbeInfo *info, gpointer user)
+{
+ struct cb_data cbdata = { STREAM_PAD_EVENT };
+
+ cbdata.u.pad_probe_data.pad = pad;
+ cbdata.u.pad_probe_data.info = info;
+ cbdata.u.pad_probe_data.user = user;
+
+ call_cb(&cbdata);
+
+ return cbdata.u.pad_probe_data.ret;
+}
diff --git a/dlls/winegstreamer/gst_cbs.h b/dlls/winegstreamer/gst_cbs.h
index d87cc8c21e9..7173c09746e 100644
--- a/dlls/winegstreamer/gst_cbs.h
+++ b/dlls/winegstreamer/gst_cbs.h
@@ -52,6 +52,7 @@ enum CB_TYPE {
SOURCE_STREAM_ADDED,
SOURCE_STREAM_REMOVED,
SOURCE_ALL_STREAMS,
+ STREAM_PAD_EVENT,
MEDIA_SOURCE_MAX,
};
@@ -137,6 +138,12 @@ struct cb_data {
GstQuery *query;
gboolean ret;
} query_sink_data;
+ struct pad_probe_data {
+ GstPad *pad;
+ GstPadProbeInfo *info;
+ gpointer user;
+ GstPadProbeReturn ret;
+ } pad_probe_data;
} u;
int finished;
@@ -172,5 +179,6 @@ GstBusSyncReply watch_source_bus_wrapper(GstBus *bus, GstMessage *message, gpoin
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;
+GstPadProbeReturn caps_listener_wrapper(GstPad *pad, GstPadProbeInfo *info, gpointer user);
#endif
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h
index 71ca4290885..780cf1b02fa 100644
--- a/dlls/winegstreamer/gst_private.h
+++ b/dlls/winegstreamer/gst_private.h
@@ -36,6 +36,7 @@
#include "winuser.h"
#include "dshow.h"
#include "strmif.h"
+#include "mfobjects.h"
#include "wine/heap.h"
#include "wine/strmbase.h"
@@ -54,6 +55,9 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
+GstCaps *make_mf_compatible_caps(GstCaps *caps);
+IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
+
enum source_type
{
SOURCE_TYPE_MPEG_4,
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
index fa0e1065ea7..f08d46c48c5 100644
--- a/dlls/winegstreamer/media_source.c
+++ b/dlls/winegstreamer/media_source.c
@@ -38,8 +38,10 @@ struct media_stream
LONG ref;
struct media_source *parent_source;
IMFMediaEventQueue *event_queue;
+ IMFStreamDescriptor *descriptor;
GstElement *appsink;
GstPad *their_src, *my_sink;
+ GstCaps *their_caps;
/* usually reflects state of source */
enum
{
@@ -47,6 +49,9 @@ struct media_stream
STREAM_INACTIVE,
STREAM_SHUTDOWN,
} state;
+ /* used when in STUB state: */
+ DWORD stream_id;
+ HANDLE caps_event;
};
struct media_source
@@ -305,6 +310,8 @@ static ULONG WINAPI media_stream_Release(IMFMediaStream *iface)
{
if (stream->my_sink)
gst_object_unref(GST_OBJECT(stream->my_sink));
+ if (stream->descriptor)
+ IMFStreamDescriptor_Release(stream->descriptor);
if (stream->event_queue)
IMFMediaEventQueue_Release(stream->event_queue);
if (stream->parent_source)
@@ -386,7 +393,10 @@ static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IM
if (stream->state == STREAM_SHUTDOWN)
return MF_E_SHUTDOWN;
- return E_NOTIMPL;
+ IMFStreamDescriptor_AddRef(stream->descriptor);
+ *descriptor = stream->descriptor;
+
+ return S_OK;
}
static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token)
@@ -429,9 +439,12 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD
IMFMediaSource_AddRef(&source->IMFMediaSource_iface);
object->parent_source = source;
object->their_src = pad;
+ object->stream_id = stream_id;
object->state = STREAM_STUB;
+ object->caps_event = CreateEventA(NULL, TRUE, FALSE, NULL);
+
if (FAILED(hr = MFCreateEventQueue(&object->event_queue)))
goto fail;
@@ -448,10 +461,10 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD
g_object_set(object->appsink, "wait-on-eos", FALSE, NULL);
object->my_sink = gst_element_get_static_pad(object->appsink, "sink");
- gst_pad_set_element_private(object->my_sink, object);
-
gst_pad_link(object->their_src, object->my_sink);
+ gst_pad_add_probe(object->my_sink, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, caps_listener_wrapper, object, NULL);
+
gst_element_sync_state_with_parent(object->appsink);
TRACE("->(%p)\n", object);
@@ -466,6 +479,43 @@ static HRESULT new_media_stream(struct media_source *source, GstPad *pad, DWORD
return hr;
}
+static HRESULT media_stream_init_desc(struct media_stream *stream)
+{
+ HRESULT hr;
+ IMFMediaTypeHandler *type_handler;
+ IMFMediaType *stream_type = NULL;
+
+ stream->their_caps = gst_caps_fixate(stream->their_caps);
+
+ stream_type = mf_media_type_from_caps(stream->their_caps);
+ gst_caps_unref(stream->their_caps);
+ if (!stream_type)
+ {
+ hr = E_FAIL;
+ goto fail;
+ }
+
+ if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, 1, &stream_type, &stream->descriptor)))
+ goto fail;
+
+ if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler)))
+ goto fail;
+
+ if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_type)))
+ goto fail;
+
+ IMFMediaTypeHandler_Release(type_handler);
+
+ stream->state = STREAM_INACTIVE;
+
+ return S_OK;
+ fail:
+ ERR("media stream initialization failed with %x\n", hr);
+ if (type_handler)
+ IMFMediaTypeHandler_Release(type_handler);
+ return hr;
+}
+
static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out)
{
struct media_source *source = impl_from_IMFMediaSource(iface);
@@ -684,6 +734,23 @@ static const IMFMediaSourceVtbl IMFMediaSource_vtbl =
media_source_Shutdown,
};
+/* If this callback is extended to use any significant win32 APIs, a wrapper function
+ should be added */
+gboolean stream_found(GstElement *bin, GstPad *pad, GstCaps *caps, gpointer user)
+{
+ GstCaps *target_caps;
+
+ /* if the stream can be converted into an MF compatible type, we'll go that route
+ otherwise, we'll rely on decodebin for the whole process */
+
+ if ((target_caps = make_mf_compatible_caps(caps)))
+ {
+ gst_caps_unref(target_caps);
+ return FALSE;
+ }
+ return TRUE;
+}
+
static void source_stream_added(GstElement *element, GstPad *pad, gpointer user)
{
struct media_source *source = (struct media_source *) user;
@@ -701,6 +768,36 @@ static void source_stream_added(GstElement *element, GstPad *pad, gpointer user)
TRACE("stream-id: %u\n", stream_id);
+ /* This codepath is currently never triggered, as we don't need to ever restart the gstreamer pipeline. It is retained in
+ case this becomes necessary in the future, for example in a case where different media types require different
+ post-processing elements. */
+ for (unsigned int i = 0; i < source->stream_count; i++)
+ {
+ DWORD existing_stream_id;
+ IMFStreamDescriptor *descriptor = source->streams[i]->descriptor;
+
+ if (source->streams[i]->state == STREAM_STUB)
+ continue;
+
+ if (FAILED(IMFStreamDescriptor_GetStreamIdentifier(descriptor, &existing_stream_id)))
+ goto leave;
+
+ if (existing_stream_id == stream_id)
+ {
+ struct media_stream *existing_stream = source->streams[i];
+ GstPadLinkReturn ret;
+
+ TRACE("Found existing stream %p\n", existing_stream);
+
+ existing_stream->their_src = pad;
+
+ if ((ret = gst_pad_link(existing_stream->their_src, existing_stream->my_sink)) != GST_PAD_LINK_OK)
+ ERR("Error linking decodebin pad to stream %p, err = %d\n", existing_stream, ret);
+
+ goto leave;
+ }
+ }
+
if (FAILED(new_media_stream(source, pad, stream_id, &stream)))
{
goto leave;
@@ -741,6 +838,26 @@ static void source_all_streams(GstElement *element, gpointer user)
SetEvent(source->all_streams_event);
}
+static GstPadProbeReturn caps_listener(GstPad *pad, GstPadProbeInfo *info, gpointer user)
+{
+ struct media_stream *stream = (struct media_stream *) user;
+ GstEvent *event = gst_pad_probe_info_get_event(info);
+
+ if (GST_EVENT_TYPE(event) == GST_EVENT_CAPS)
+ {
+ GstCaps *caps;
+ TRACE("got caps for stream %p\n", stream);
+
+ gst_event_parse_caps(event, &caps);
+ stream->their_caps = gst_caps_copy(caps);
+ SetEvent(stream->caps_event);
+
+ return GST_PAD_PROBE_REMOVE;
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_type type, struct media_source **out_media_source)
{
GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
@@ -793,6 +910,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
gst_bin_add(GST_BIN(object->container), object->decodebin);
+ if(!GetEnvironmentVariableA("MF_DECODE_IN_SOURCE", NULL, 0))
+ g_signal_connect(object->decodebin, "autoplug-continue", G_CALLBACK(stream_found), object);
g_signal_connect(object->decodebin, "pad-added", G_CALLBACK(source_stream_added_wrapper), object);
g_signal_connect(object->decodebin, "pad-removed", G_CALLBACK(source_stream_removed_wrapper), object);
g_signal_connect(object->decodebin, "no-more-pads", G_CALLBACK(source_all_streams_wrapper), object);
@@ -818,6 +937,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, enum source_t
}
WaitForSingleObject(object->all_streams_event, INFINITE);
+ for (unsigned int i = 0; i < object->stream_count; i++)
+ {
+ WaitForSingleObject(object->streams[i]->caps_event, INFINITE);
+ if (FAILED(hr = media_stream_init_desc(object->streams[i])))
+ goto fail;
+ }
object->state = SOURCE_STOPPED;
@@ -1349,6 +1474,12 @@ void perform_cb_media_source(struct cb_data *cbdata)
source_all_streams(data->element, data->user);
break;
}
+ case STREAM_PAD_EVENT:
+ {
+ struct pad_probe_data *data = &cbdata->u.pad_probe_data;
+ cbdata->u.pad_probe_data.ret = caps_listener(data->pad, data->info, data->user);
+ break;
+ }
default:
{
ERR("Wrong callback forwarder called\n");
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c
index 16e55247de1..06f7984bbe6 100644
--- a/dlls/winegstreamer/mfplat.c
+++ b/dlls/winegstreamer/mfplat.c
@@ -16,6 +16,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#include "config.h"
+#include <gst/gst.h>
+
+#include "gst_private.h"
+
#include <stdarg.h>
#include "gst_private.h"
@@ -442,3 +447,206 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
return CLASS_E_CLASSNOTAVAILABLE;
}
+
+const static struct
+{
+ const GUID *subtype;
+ GstVideoFormat format;
+}
+uncompressed_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},
+};
+
+/* caps will be modified to represent the exact type needed for the format */
+static IMFMediaType* transform_to_media_type(GstCaps *caps)
+{
+ 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;
+ }
+
+ info = gst_caps_get_structure(caps, 0);
+ mime_type = gst_structure_get_name(info);
+
+ if (!(strncmp(mime_type, "video", 5)))
+ {
+ GstVideoInfo video_info;
+
+ if (!(gst_video_info_from_caps(&video_info, caps)))
+ {
+ 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);
+
+ IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, ((UINT64)video_info.fps_n << 32) | video_info.fps_d);
+
+ 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_formats); i++)
+ {
+ if (uncompressed_formats[i].format == video_info.finfo->format)
+ {
+ IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, uncompressed_formats[i].subtype);
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(uncompressed_formats))
+ FIXME("Unrecognized format.\n");
+ }
+ }
+ else
+ {
+ FIXME("Unrecognized video format %s\n", mime_type);
+ IMFMediaType_Release(media_type);
+ return NULL;
+ }
+ }
+ 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, (DWORD)channel_mask);
+
+ if (gst_structure_get_int(info, "bitrate", &bitrate))
+ IMFMediaType_SetUINT32(media_type, &MF_MT_AVG_BITRATE, bitrate);
+
+ if (!(strcmp(mime_type, "audio/x-raw")))
+ {
+ const char *format;
+ if ((format = gst_structure_get_string(info, "format")))
+ {
+ char type;
+ unsigned int bits_per_sample;
+ char endian[2];
+ char new_format[6];
+ if ((strlen(format) > 5) || (sscanf(format, "%c%u%2c", &type, &bits_per_sample, endian) < 2))
+ {
+ FIXME("Unhandled audio format %s\n", format);
+ IMFMediaType_Release(media_type);
+ return NULL;
+ }
+
+ if (type == 'F')
+ {
+ IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_Float);
+ }
+ else if (type == 'U' || type == 'S')
+ {
+ IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &MFAudioFormat_PCM);
+ if (bits_per_sample == 8)
+ type = 'U';
+ else
+ type = 'S';
+ }
+ else
+ {
+ FIXME("Unrecognized audio format: %s\n", format);
+ IMFMediaType_Release(media_type);
+ return NULL;
+ }
+
+ IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bits_per_sample);
+
+ if (endian[0] == 'B')
+ endian[0] = 'L';
+
+ sprintf(new_format, "%c%u%.2s", type, bits_per_sample, bits_per_sample > 8 ? endian : 0);
+ gst_caps_set_simple(caps, "format", G_TYPE_STRING, new_format, NULL);
+ }
+ else
+ {
+ ERR("Failed to get audio format\n");
+ }
+ }
+ else
+ {
+ FIXME("Unrecognized audio format %s\n", mime_type);
+ IMFMediaType_Release(media_type);
+ return NULL;
+ }
+ }
+ else
+ {
+ IMFMediaType_Release(media_type);
+ return NULL;
+ }
+
+ return media_type;
+}
+
+/* returns NULL if doesn't match exactly */
+IMFMediaType *mf_media_type_from_caps(GstCaps *caps)
+{
+ GstCaps *writeable_caps;
+ IMFMediaType *ret;
+
+ writeable_caps = gst_caps_copy(caps);
+ ret = transform_to_media_type(writeable_caps);
+
+ if (!(gst_caps_is_equal(caps, writeable_caps)))
+ {
+ IMFMediaType_Release(ret);
+ ret = NULL;
+ }
+ gst_caps_unref(writeable_caps);
+ return ret;
+}
+
+GstCaps *make_mf_compatible_caps(GstCaps *caps)
+{
+ GstCaps *ret;
+ IMFMediaType *media_type;
+
+ ret = gst_caps_copy(caps);
+
+ if ((media_type = transform_to_media_type(ret)))
+ IMFMediaType_Release(media_type);
+
+ if (!media_type)
+ {
+ gst_caps_unref(ret);
+ return NULL;
+ }
+
+ return ret;
+}
\ No newline at end of file
--
2.28.0
More information about the wine-devel
mailing list