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 | 13 +++++++ dlls/winegstreamer/media_source.c | 26 ++++++++++++++ dlls/winegstreamer/unixlib.h | 15 ++++++++ dlls/winegstreamer/wg_parser.c | 59 +++++++++++++++++++++++++++++-- 5 files changed, 113 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 523e2711edb..ef053e28351 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -96,6 +96,8 @@ 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); +bool wg_parser_stream_get_tag(struct wg_parser_stream *stream, enum wg_parser_tag tag, + char *buffer, uint32_t size); /* 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 ea52797712e..0f070fa02b6 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -273,6 +273,19 @@ uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream) return params.duration; }
+bool wg_parser_stream_get_tag(struct wg_parser_stream *stream, enum wg_parser_tag tag, char *buffer, uint32_t size) +{ + struct wg_parser_stream_get_tag_params params = + { + .stream = stream, + .tag = tag, + .buffer = buffer, + .size = size, + }; + + return !__wine_unix_call(unix_handle, unix_wg_parser_stream_get_tag, ¶ms); +} + 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..5f93e3e39bf 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -1481,7 +1481,33 @@ 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; + char str[128]; + WCHAR *strW; + DWORD len; + IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]); + + for (j = 0; j < ARRAY_SIZE(tags); ++j) + { + if (!wg_parser_stream_get_tag(object->streams[i]->wg_stream, tags[j].tag, str, sizeof(str))) + continue; + if (!(len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0))) + 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); + } }
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..f761c112a4a 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_MAX +}; + +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..53d0cbd62c9 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;
uint64_t duration; + gchar *tags[WG_PARSER_TAG_MAX]; };
static NTSTATUS wg_parser_get_stream_count(void *args) @@ -397,6 +400,21 @@ 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_MAX) + 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) + 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 +592,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 +615,13 @@ static GstFlowReturn sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *bu
pthread_mutex_lock(&parser->mutex);
+ if (!stream->has_tags) + { + /* If we receieved a buffer waiting for tags in wg_parser_connect() does not make sense anymore. */ + stream->has_tags = 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 +788,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 +805,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 +1271,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 +1342,7 @@ static NTSTATUS wg_parser_connect(void *args) struct wg_parser_stream *stream = parser->streams[i]; gint64 duration;
- while (!stream->has_caps && !parser->error) + while ((!stream->has_caps || !stream->has_tags) && !parser->error) pthread_cond_wait(&parser->init_cond, &parser->mutex);
/* GStreamer doesn't actually provide any guarantees about when duration @@ -1354,6 +1406,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 +1730,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),
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 5f93e3e39bf..17cb5bde325 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; char str[128]; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index f761c112a4a..60e54a25bf3 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_MAX };
diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 53d0cbd62c9..19bda4eff6f 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -1273,14 +1273,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; + GstMapInfo map_info; + 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; + if (!gst_buffer_map(buf, &map_info, GST_MAP_READ)) + continue; + size -= 8; + if ((stream->tags[WG_PARSER_TAG_NAME] = g_malloc(size + 1))) + { + memcpy(stream->tags[WG_PARSER_TAG_NAME], map_info.data + 8, size); + stream->tags[WG_PARSER_TAG_NAME][size] = 0; + } + gst_buffer_unmap(buf, &map_info); + } 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
Zebediah Figura (@zfigura) commented about dlls/winegstreamer/media_source.c:
}
tags[] =
{
{WG_PARSER_TAG_LANGUAGE, &MF_SD_LANGUAGE},
};
unsigned int j;
char str[128];
WCHAR *strW;
DWORD len;
IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, &descriptors[i]);
for (j = 0; j < ARRAY_SIZE(tags); ++j)
{
if (!wg_parser_stream_get_tag(object->streams[i]->wg_stream, tags[j].tag, str, sizeof(str)))
continue;
We should try to reallocate a dynamic buffer, instead of failing if the fixed buffer is too short. [There are, of course, many ways to do this; I don't have strong opinions on which is the best.]
Zebediah Figura (@zfigura) commented about dlls/winegstreamer/unixlib.h:
UINT64 duration;
};
+enum wg_parser_tag +{
- WG_PARSER_TAG_LANGUAGE,
- WG_PARSER_TAG_MAX
WG_PARSER_TAG_COUNT if it's not equal to an element.
Zebediah Figura (@zfigura) commented about dlls/winegstreamer/wg_parser.c:
pthread_mutex_lock(&parser->mutex);
- if (!stream->has_tags)
- {
/* If we receieved a buffer waiting for tags in wg_parser_connect() does not make sense anymore. */
stream->has_tags = TRUE;
pthread_cond_signal(&parser->init_cond);
- }
Lowercase "true".
I think it'd be mildly clearer to use a separate variable "has_buffer" and check e.g. "has_buffer || has_tags" in wg_parser_connect(), but I don't feel strongly about it.
Zebediah Figura (@zfigura) commented about dlls/winegstreamer/wg_parser.c:
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;
if (!gst_buffer_map(buf, &map_info, GST_MAP_READ))
continue;
size -= 8;
if ((stream->tags[WG_PARSER_TAG_NAME] = g_malloc(size + 1)))
{
memcpy(stream->tags[WG_PARSER_TAG_NAME], map_info.data + 8, size);
stream->tags[WG_PARSER_TAG_NAME][size] = 0;
}
gst_buffer_unmap(buf, &map_info);
FWIW, there's gst_buffer_extract_dup() for this.