-- v5: mfplay/tests: Add MF_SD_LANGUAGE and MF_SD_STREAM_NAME value tests. winegstreamer: Extract stream name from QT demuxer private data. winegstreamer: Query stream tags and set MF_SD_LANGUAGE attribute.
From: Paul Gofman pgofman@codeweavers.com
Loosely based on a patch by Derek Lesho and Rémi Bernon. --- dlls/winegstreamer/gst_private.h | 2 +- dlls/winegstreamer/main.c | 31 ++++++++++++++++ dlls/winegstreamer/media_source.c | 30 +++++++++++++++ dlls/winegstreamer/unixlib.h | 15 ++++++++ dlls/winegstreamer/wg_parser.c | 62 ++++++++++++++++++++++++++++++- dlls/winegstreamer/wm_reader.c | 2 + 6 files changed, 139 insertions(+), 3 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index ee5e6dae3cf..3890db543ff 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -33,7 +33,6 @@ #define NONAMELESSUNION #include "dshow.h" #include "mfidl.h" -#include "wmsdk.h" #include "wine/debug.h" #include "wine/strmbase.h"
@@ -96,6 +95,7 @@ void wg_parser_stream_notify_qos(struct wg_parser_stream *stream,
/* Returns the duration in 100-nanosecond units. */ uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream); +char *wg_parser_stream_get_tag(struct wg_parser_stream *stream, enum wg_parser_tag tag); /* start_pos and stop_pos are in 100-nanosecond units. */ void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 9e54780ac4e..2675430019f 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -21,6 +21,9 @@ #define WINE_NO_NAMELESS_EXTENSION
#define EXTERN_GUID DEFINE_GUID + +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "initguid.h" #include "gst_private.h" #include "winternl.h" @@ -273,6 +276,34 @@ uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream) return params.duration; }
+char *wg_parser_stream_get_tag(struct wg_parser_stream *stream, enum wg_parser_tag tag) +{ + uint32_t size = 0; + struct wg_parser_stream_get_tag_params params = + { + .stream = stream, + .tag = tag, + .size = &size, + }; + char *buffer; + + if (WINE_UNIX_CALL(unix_wg_parser_stream_get_tag, ¶ms) != STATUS_BUFFER_TOO_SMALL) + return NULL; + if (!(buffer = malloc(size))) + { + ERR("No memory.\n"); + return NULL; + } + params.buffer = buffer; + if (WINE_UNIX_CALL(unix_wg_parser_stream_get_tag, ¶ms)) + { + ERR("wg_parser_stream_get_tag failed unexpectedly.\n"); + free(buffer); + return NULL; + } + return buffer; +} + void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags) { diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 9bb7a441a8f..59047a5ef82 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1481,7 +1481,37 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ descriptors = malloc(object->stream_count * sizeof(IMFStreamDescriptor *)); for (i = 0; i < object->stream_count; i++) { + static const struct + { + enum wg_parser_tag tag; + const GUID *mf_attr; + } + tags[] = + { + {WG_PARSER_TAG_LANGUAGE, &MF_SD_LANGUAGE}, + }; + unsigned int j; + WCHAR *strW; + DWORD len; + char *str; + IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]); + + for (j = 0; j < ARRAY_SIZE(tags); ++j) + { + if (!(str = wg_parser_stream_get_tag(object->streams[i]->wg_stream, tags[j].tag))) + continue; + if (!(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) + { + free(str); + continue; + } + strW = malloc(len * sizeof(*strW)); + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, strW, len)) + IMFStreamDescriptor_SetString(descriptors[i], tags[j].mf_attr, strW); + free(strW); + free(str); + } }
if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc))) diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 09aa68eb4cc..f048426b27a 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -252,6 +252,20 @@ struct wg_parser_stream_get_duration_params UINT64 duration; };
+enum wg_parser_tag +{ + WG_PARSER_TAG_LANGUAGE, + WG_PARSER_TAG_COUNT +}; + +struct wg_parser_stream_get_tag_params +{ + struct wg_parser_stream *stream; + enum wg_parser_tag tag; + char *buffer; + UINT32 *size; +}; + struct wg_parser_stream_seek_params { struct wg_parser_stream *stream; @@ -312,6 +326,7 @@ enum unix_funcs unix_wg_parser_stream_notify_qos,
unix_wg_parser_stream_get_duration, + unix_wg_parser_stream_get_tag, unix_wg_parser_stream_seek,
unix_wg_transform_create, diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 14970d327c8..dc98901c226 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -34,6 +34,8 @@ #include <gst/video/video.h> #include <gst/audio/audio.h>
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "winternl.h" #include "dshow.h"
@@ -107,9 +109,10 @@ struct wg_parser_stream GstBuffer *buffer; GstMapInfo map_info;
- bool flushing, eos, enabled, has_caps; + bool flushing, eos, enabled, has_caps, has_tags, has_buffer;
uint64_t duration; + gchar *tags[WG_PARSER_TAG_COUNT]; };
static NTSTATUS wg_parser_get_stream_count(void *args) @@ -397,6 +400,24 @@ static NTSTATUS wg_parser_stream_get_duration(void *args) return S_OK; }
+static NTSTATUS wg_parser_stream_get_tag(void *args) +{ + struct wg_parser_stream_get_tag_params *params = args; + uint32_t len; + + if (params->tag >= WG_PARSER_TAG_COUNT) + return STATUS_INVALID_PARAMETER; + if (!params->stream->tags[params->tag]) + return STATUS_NOT_FOUND; + if ((len = strlen(params->stream->tags[params->tag]) + 1) > *params->size) + { + *params->size = len; + return STATUS_BUFFER_TOO_SMALL; + } + memcpy(params->buffer, params->stream->tags[params->tag], len); + return STATUS_SUCCESS; +} + static NTSTATUS wg_parser_stream_seek(void *args) { GstSeekType start_type = GST_SEEK_TYPE_SET, stop_type = GST_SEEK_TYPE_SET; @@ -574,6 +595,13 @@ static gboolean sink_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) break; }
+ case GST_EVENT_TAG: + pthread_mutex_lock(&parser->mutex); + stream->has_tags = true; + pthread_cond_signal(&parser->init_cond); + pthread_mutex_unlock(&parser->mutex); + break; + default: GST_WARNING("Ignoring "%s" event.", GST_EVENT_TYPE_NAME(event)); } @@ -590,6 +618,12 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu
pthread_mutex_lock(&parser->mutex);
+ if (!stream->has_buffer) + { + stream->has_buffer = true; + pthread_cond_signal(&parser->init_cond); + } + /* Allow this buffer to be flushed by GStreamer. We are effectively * implementing a queue object here. */
@@ -756,6 +790,8 @@ static struct wg_parser_stream *create_stream(struct wg_parser *parser)
static void free_stream(struct wg_parser_stream *stream) { + unsigned int i; + if (stream->their_src) { if (stream->post_sink) @@ -771,6 +807,11 @@ static void free_stream(struct wg_parser_stream *stream) pthread_cond_destroy(&stream->event_cond); pthread_cond_destroy(&stream->event_empty_cond);
+ for (i = 0; i < ARRAY_SIZE(stream->tags); ++i) + { + if (stream->tags[i]) + g_free(stream->tags[i]); + } free(stream); }
@@ -1232,6 +1273,19 @@ static gboolean src_event_cb(GstPad *pad, GstObject *parent, GstEvent *event) return ret; }
+static void query_tags(struct wg_parser_stream *stream) +{ + GstTagList *tag_list; + GstEvent *tag_event; + + if (!(tag_event = gst_pad_get_sticky_event(stream->their_src, GST_EVENT_TAG, 0))) + return; + + gst_event_parse_tag(tag_event, &tag_list); + gst_tag_list_get_string(tag_list, "language-code", &stream->tags[WG_PARSER_TAG_LANGUAGE]); + gst_event_unref(tag_event); +} + static NTSTATUS wg_parser_connect(void *args) { GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE("quartz_src", @@ -1290,7 +1344,8 @@ static NTSTATUS wg_parser_connect(void *args) struct wg_parser_stream *stream = parser->streams[i]; gint64 duration;
- while (!stream->has_caps && !parser->error) + /* If we receieved a buffer waiting for tags or caps does not make sense anymore. */ + while ((!stream->has_caps || !stream->has_tags) && !parser->error && !stream->has_buffer) pthread_cond_wait(&parser->init_cond, &parser->mutex);
/* GStreamer doesn't actually provide any guarantees about when duration @@ -1354,6 +1409,8 @@ static NTSTATUS wg_parser_connect(void *args) } }
+ query_tags(stream); + /* Now that we're fully initialized, enable the stream so that further * samples get queued instead of being discarded. We don't actually need * the samples (in particular, the frontend should seek before @@ -1676,6 +1733,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = X(wg_parser_stream_notify_qos),
X(wg_parser_stream_get_duration), + X(wg_parser_stream_get_tag), X(wg_parser_stream_seek),
X(wg_transform_create), diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 804af4a9aed..0e9e7927549 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -17,6 +17,8 @@ */
#include "gst_private.h" +#include "initguid.h" +#include "wmsdk.h"
WINE_DEFAULT_DEBUG_CHANNEL(wmvcore);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/winegstreamer/media_source.c | 1 + dlls/winegstreamer/unixlib.h | 1 + dlls/winegstreamer/wg_parser.c | 33 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+)
diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 59047a5ef82..542189b28f5 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1489,6 +1489,7 @@ static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_ tags[] = { {WG_PARSER_TAG_LANGUAGE, &MF_SD_LANGUAGE}, + {WG_PARSER_TAG_NAME, &MF_SD_STREAM_NAME}, }; unsigned int j; WCHAR *strW; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index f048426b27a..2b5b8af37d5 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -255,6 +255,7 @@ struct wg_parser_stream_get_duration_params enum wg_parser_tag { WG_PARSER_TAG_LANGUAGE, + WG_PARSER_TAG_NAME, WG_PARSER_TAG_COUNT };
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index dc98901c226..d2db1f039e4 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1275,14 +1275,47 @@ static gboolean src_event_cb(GstPad *pad, GstObject *parent, GstEvent *event)
static void query_tags(struct wg_parser_stream *stream) { + const gchar *struct_name; GstTagList *tag_list; GstEvent *tag_event; + guint i, tag_count; + const GValue *val; + GstSample *sample; + GstBuffer *buf; + gsize size;
if (!(tag_event = gst_pad_get_sticky_event(stream->their_src, GST_EVENT_TAG, 0))) return;
gst_event_parse_tag(tag_event, &tag_list); gst_tag_list_get_string(tag_list, "language-code", &stream->tags[WG_PARSER_TAG_LANGUAGE]); + + /* Extract stream name from Quick Time demuxer private tag where it puts unrecognized chunks. */ + tag_count = gst_tag_list_get_tag_size(tag_list, "private-qt-tag"); + for (i = 0; i < tag_count; ++i) + { + if (!(val = gst_tag_list_get_value_index(tag_list, "private-qt-tag", i))) + continue; + if (!GST_VALUE_HOLDS_SAMPLE(val) || !(sample = gst_value_get_sample(val))) + continue; + struct_name = gst_structure_get_name(gst_sample_get_info(sample)); + if (!struct_name || strcmp(struct_name, "application/x-gst-qt-name-tag")) + continue; + if (!(buf = gst_sample_get_buffer(sample))) + continue; + if ((size = gst_buffer_get_size(buf)) < 8) + continue; + size -= 8; + if (!(stream->tags[WG_PARSER_TAG_NAME] = g_malloc(size + 1))) + continue; + if (gst_buffer_extract(buf, 8, stream->tags[WG_PARSER_TAG_NAME], size) != size) + { + g_free(stream->tags[WG_PARSER_TAG_NAME]); + stream->tags[WG_PARSER_TAG_NAME] = NULL; + continue; + } + stream->tags[WG_PARSER_TAG_NAME][size] = 0; + } gst_event_unref(tag_event); }
From: Paul Gofman pgofman@codeweavers.com
--- dlls/mfplay/tests/Makefile.in | 2 +- dlls/mfplay/tests/mfplay.c | 14 +++++++++++++- dlls/mfplay/tests/test.mp4 | Bin 1554 -> 1610 bytes 3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/dlls/mfplay/tests/Makefile.in b/dlls/mfplay/tests/Makefile.in index e790fa3e2ad..aabc08258f9 100644 --- a/dlls/mfplay/tests/Makefile.in +++ b/dlls/mfplay/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = mfplay.dll -IMPORTS = mfplay user32 uuid mfuuid mfplat +IMPORTS = mfplay user32 uuid mfuuid mfplat ole32
C_SRCS = \ mfplay.c diff --git a/dlls/mfplay/tests/mfplay.c b/dlls/mfplay/tests/mfplay.c index 50ecb1b09b9..6cecd3bbdd9 100644 --- a/dlls/mfplay/tests/mfplay.c +++ b/dlls/mfplay/tests/mfplay.c @@ -356,7 +356,19 @@ static void test_media_item(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
hr = IMFPMediaItem_GetStreamAttribute(item, 0, &MF_SD_LANGUAGE, &propvar); - ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(propvar.vt == VT_LPWSTR, "Unexpected vt %u.\n", propvar.vt); + ok(!wcscmp(propvar.pwszVal, L"en"), "Unexpected value %s.\n", debugstr_w(propvar.pwszVal)); + PropVariantClear(&propvar); + + hr = IMFPMediaItem_GetStreamAttribute(item, 0, &MF_SD_STREAM_NAME, &propvar); + ok(hr == S_OK || broken(hr == MF_E_ATTRIBUTENOTFOUND) /* Before Win10 1607. */, "Unexpected hr %#lx.\n", hr); + if (hr == S_OK) + { + ok(propvar.vt == VT_LPWSTR, "Unexpected vt %u.\n", propvar.vt); + ok(!wcscmp(propvar.pwszVal, L"test"), "Unexpected value %s.\n", debugstr_w(propvar.pwszVal)); + PropVariantClear(&propvar); + }
hr = IMFPMediaItem_GetPresentationAttribute(item, &MF_PD_DURATION, &propvar); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/mfplay/tests/test.mp4 b/dlls/mfplay/tests/test.mp4 index a5bbca6bbf7692eab047050b79b8ccc83516e6b4..6770d064716330cf4a7652f826c09a3eabce119e 100644 GIT binary patch delta 137 zcmbQlbBbrf3MR&-lUFhoGoGI;%iPXrJb5Xz0+Z<R$;X+E81GH~#cVG*uec;Bhk=3N zRdGpi3Xlc?Mj*a5If!MeqCi1naRF2WBr1|rQd9z@fxr+*8<tFFVO=jMQkqhd2o&PU ZOUz9zNi8m!yn!`}!&1-4T+h(J006FdBrE^`
delta 85 zcmX@bGl^%z3MR(f$t#(P8K+N{Wo~EWpS+Y=fhqLJ<m1dnjJqcPVz!s8DlSRNVPIf5 lR$Nk?0;EBJ5s0@<4r19lS(|mu<kzg>92R<p=6Z$(h5(wY8D9VZ