[PATCH 3/3] winegstreamer: Implement IMFMediaStream::GetStreamDescriptor.
Zebediah Figura
zfigura at codeweavers.com
Wed Sep 9 19:00:57 CDT 2020
On 9/8/20 10:47 AM, Derek Lesho wrote:
> 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 | 5 +
> dlls/winegstreamer/media_source.c | 137 +++++++++++++++++++-
> dlls/winegstreamer/mfplat.c | 208 ++++++++++++++++++++++++++++++
> 5 files changed, 368 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 ef07d3591e7..86392eea4e3 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,10 @@ void start_dispatch_thread(void) DECLSPEC_HIDDEN;
>
> extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN;
>
> +HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
> +GstCaps *make_mf_compatible_caps(GstCaps *caps);
> +IMFMediaType *mf_media_type_from_caps(GstCaps *caps);
Missing DECLSPEC_HIDDEN.
> +
> HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN;
>
> #endif /* __GST_PRIVATE_INCLUDED__ */
> diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c
> index 29af2b72def..345b1fe4528 100644
> --- a/dlls/winegstreamer/media_source.c
> +++ b/dlls/winegstreamer/media_source.c
> @@ -47,14 +47,19 @@ 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;
> enum
> {
> STREAM_STUB,
> STREAM_INACTIVE,
> STREAM_SHUTDOWN,
> } state;
> + /* used when in STUB state: */
> + DWORD stream_id;
> + HANDLE caps_event;
> };
>
> struct media_source
> @@ -312,6 +317,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)
> @@ -393,7 +400,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)
> @@ -436,9 +446,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;
>
> @@ -455,10 +468,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);
> @@ -473,6 +486,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);
I think this is unnecessary; the caps should already be fixed.
> +
> + stream_type = mf_media_type_from_caps(stream->their_caps);
> + gst_caps_unref(stream->their_caps);
This seems a bit error-prone. I'd suggest getting rid of the field and
instead calling gst_caps_copy(gst_pad_get_current_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);
While this does tell you that one of the mfplat functions failed, it
doesn't actually tell you which one. There's similar patterns elsewhere
in this series.
> + 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);
> @@ -680,6 +730,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 */
> +
I think we should leave this part out for now.
> + 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;
> @@ -697,6 +764,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. */
In general, if a code path is dead, it shouldn't be introduced (yet).
> + 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;
> @@ -737,6 +834,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;
The cast is unnecessary, here and elsewhere.
> + 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, struct media_source **out_media_source)
> {
> GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
> @@ -787,6 +904,8 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
>
> gst_bin_add(GST_BIN(object->container), object->decodebin);
>
> + if(!GetEnvironmentVariableA("MF_DECODE_IN_SOURCE", NULL, 0))
Debugging code?
> + 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);
> @@ -812,6 +931,12 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_
> }
>
> WaitForSingleObject(object->all_streams_event, INFINITE);
> + for (unsigned int i = 0; i < object->stream_count; i++)
Misplaced declaration.
> + {
> + WaitForSingleObject(object->streams[i]->caps_event, INFINITE);
> + if (FAILED(hr = media_stream_init_desc(object->streams[i])))
> + goto fail;
> + }
>
> object->state = SOURCE_STOPPED;
>
> @@ -1337,6 +1462,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 c996f06211e..3667bc3cc38 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"
> @@ -436,3 +441,206 @@ HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj)
>
> return CLASS_E_CLASSNOTAVAILABLE;
> }
> +
> +const static struct
"static const"
> +{
> + const GUID *subtype;
> + GstVideoFormat format;
> +}
> +uncompressed_formats[] =
"uncompressed_video_formats", probably.
> +{
> + {&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 */
This seems wrong in general.
> +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)))
There's a lot of superfluous parentheses after local not operators in
this series; this one seems especially superfluous.
> + {
> + 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");
In which case the function should probably fail.
> + }
> + }
> + 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);
Superfluous cast.
> +
> + 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")))
You may find it easier to use gst_audio_info_from_caps() here.
> + {
> + 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;
> +}
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://www.winehq.org/pipermail/wine-devel/attachments/20200909/215a528a/attachment.sig>
More information about the wine-devel
mailing list