Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51931 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52391 Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v2: Move the format helpers to a dedicated source, use a separate major format in the wg_format struct for WMA format.
dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/gst_private.h | 3 ++ dlls/winegstreamer/main.c | 14 +++++++ dlls/winegstreamer/unix_private.h | 31 ++++++++++++++ dlls/winegstreamer/unixlib.h | 8 ++++ dlls/winegstreamer/wg_parser.c | 20 +++++++-- dlls/winegstreamer/wg_transform.c | 69 +++++++++++++++++++++++++++++++ dlls/winegstreamer/wma_decoder.c | 20 +++++++++ 8 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 dlls/winegstreamer/unix_private.h create mode 100644 dlls/winegstreamer/wg_transform.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index c53e914e246..52295418f0f 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -13,6 +13,7 @@ C_SRCS = \ mfplat.c \ quartz_parser.c \ wg_parser.c \ + wg_transform.c \ wm_asyncreader.c \ wm_reader.c \ wm_syncreader.c \ diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 222bce3b2c7..df82b229143 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -96,6 +96,9 @@ uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream) DECLSPEC 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) DECLSPEC_HIDDEN;
+struct wg_transform *wg_transform_create(void) DECLSPEC_HIDDEN; +void wg_transform_destroy(struct wg_transform *transform) DECLSPEC_HIDDEN; + unsigned int wg_format_get_max_size(const struct wg_format *format);
HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 260dd208e2f..f23fa3abcdf 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -254,6 +254,20 @@ void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, __wine_unix_call(unix_handle, unix_wg_parser_stream_seek, ¶ms); }
+struct wg_transform *wg_transform_create(void) +{ + struct wg_transform_create_params params = {0}; + + if (__wine_unix_call(unix_handle, unix_wg_transform_create, ¶ms)) + return NULL; + return params.transform; +} + +void wg_transform_destroy(struct wg_transform *transform) +{ + __wine_unix_call(unix_handle, unix_wg_transform_destroy, transform); +} + BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { if (reason == DLL_PROCESS_ATTACH) diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h new file mode 100644 index 00000000000..375d33e7728 --- /dev/null +++ b/dlls/winegstreamer/unix_private.h @@ -0,0 +1,31 @@ +/* + * winegstreamer Unix library interface + * + * Copyright 2020-2021 Zebediah Figura 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 __WINE_WINEGSTREAMER_UNIX_PRIVATE_H +#define __WINE_WINEGSTREAMER_UNIX_PRIVATE_H + +#include "unixlib.h" + +extern bool init_gstreamer(void) DECLSPEC_HIDDEN; + +extern NTSTATUS wg_transform_create(void *args) DECLSPEC_HIDDEN; +extern NTSTATUS wg_transform_destroy(void *args) DECLSPEC_HIDDEN; + +#endif /* __WINE_WINEGSTREAMER_UNIX_PRIVATE_H */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 82bb534b938..c8b98da3a64 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -217,6 +217,11 @@ struct wg_parser_stream_seek_params DWORD start_flags, stop_flags; };
+struct wg_transform_create_params +{ + struct wg_transform *transform; +}; + enum unix_funcs { unix_wg_parser_create, @@ -245,6 +250,9 @@ enum unix_funcs
unix_wg_parser_stream_get_duration, unix_wg_parser_stream_seek, + + unix_wg_transform_create, + unix_wg_transform_destroy, };
#endif /* __WINE_WINEGSTREAMER_UNIXLIB_H */ diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 013566b25e9..ac8c5a2b95c 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -37,7 +37,7 @@ #include "winternl.h" #include "dshow.h"
-#include "unixlib.h" +#include "unix_private.h"
typedef enum { @@ -51,7 +51,7 @@ typedef enum * debug logging instead of Wine debug logging. In order to be safe we forbid * any use of Wine debug logging in this entire file. */
-GST_DEBUG_CATEGORY_STATIC(wine); +GST_DEBUG_CATEGORY(wine); #define GST_CAT_DEFAULT wine
typedef BOOL (*init_gst_cb)(struct wg_parser *parser); @@ -1963,6 +1963,16 @@ static void init_gstreamer_once(void) gst_version_string(), GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO); }
+bool init_gstreamer(void) +{ + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + + if (pthread_once(&init_once, init_gstreamer_once)) + return false; + + return true; +} + static NTSTATUS wg_parser_create(void *args) { static const init_gst_cb init_funcs[] = @@ -1973,11 +1983,10 @@ static NTSTATUS wg_parser_create(void *args) [WG_PARSER_WAVPARSE] = wave_parser_init_gst, };
- static pthread_once_t once = PTHREAD_ONCE_INIT; struct wg_parser_create_params *params = args; struct wg_parser *parser;
- if (pthread_once(&once, init_gstreamer_once)) + if (!init_gstreamer()) return E_FAIL;
if (!(parser = calloc(1, sizeof(*parser)))) @@ -2044,4 +2053,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
X(wg_parser_stream_get_duration), X(wg_parser_stream_seek), + + X(wg_transform_create), + X(wg_transform_destroy), }; diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c new file mode 100644 index 00000000000..822740da0d7 --- /dev/null +++ b/dlls/winegstreamer/wg_transform.c @@ -0,0 +1,69 @@ +/* + * GStreamer transform backend + * + * Copyright 2022 Rémi Bernon 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/audio/audio.h> + +#include "winternl.h" +#include "dshow.h" + +#include "unix_private.h" + +GST_DEBUG_CATEGORY_EXTERN(wine); +#define GST_CAT_DEFAULT wine + +struct wg_transform +{ +}; + +NTSTATUS wg_transform_destroy(void *args) +{ + struct wg_transform *transform = args; + + free(transform); + return S_OK; +} + +NTSTATUS wg_transform_create(void *args) +{ + struct wg_transform_create_params *params = args; + struct wg_transform *transform; + + if (!init_gstreamer()) + return E_FAIL; + + if (!(transform = calloc(1, sizeof(*transform)))) + return E_OUTOFMEMORY; + + GST_INFO("Created winegstreamer transform %p.", transform); + params->transform = transform; + return S_OK; +} diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 78316059052..3b051230a9e 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -53,6 +53,8 @@ struct wma_decoder LONG refcount; IMFMediaType *input_type; IMFMediaType *output_type; + + struct wg_transform *wg_transform; };
static inline struct wma_decoder *impl_from_IUnknown(IUnknown *iface) @@ -60,6 +62,19 @@ static inline struct wma_decoder *impl_from_IUnknown(IUnknown *iface) return CONTAINING_RECORD(iface, struct wma_decoder, IUnknown_inner); }
+static HRESULT try_create_wg_transform(struct wma_decoder *decoder) +{ + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); + + decoder->wg_transform = wg_transform_create(); + if (decoder->wg_transform) + return S_OK; + + WARN("Failed to create wg_transform.\n"); + return E_FAIL; +} + static HRESULT WINAPI unknown_QueryInterface(IUnknown *iface, REFIID iid, void **out) { struct wma_decoder *decoder = impl_from_IUnknown(iface); @@ -104,6 +119,8 @@ static ULONG WINAPI unknown_Release(IUnknown *iface)
if (!refcount) { + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); if (decoder->input_type) IMFMediaType_Release(decoder->input_type); if (decoder->output_type) @@ -438,6 +455,9 @@ static HRESULT WINAPI transform_SetOutputType(IMFTransform *iface, DWORD id, IMF if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)decoder->output_type))) goto failed;
+ if (FAILED(hr = try_create_wg_transform(decoder))) + goto failed; + return S_OK;
failed:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/unix_private.h | 6 + dlls/winegstreamer/wg_format.c | 436 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 393 --------------------------- 4 files changed, 443 insertions(+), 393 deletions(-) create mode 100644 dlls/winegstreamer/wg_format.c
diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index 52295418f0f..0bcdb3eec65 100644 --- a/dlls/winegstreamer/Makefile.in +++ b/dlls/winegstreamer/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ media_source.c \ mfplat.c \ quartz_parser.c \ + wg_format.c \ wg_parser.c \ wg_transform.c \ wm_asyncreader.c \ diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h index 375d33e7728..f9c4da2f6ea 100644 --- a/dlls/winegstreamer/unix_private.h +++ b/dlls/winegstreamer/unix_private.h @@ -23,8 +23,14 @@
#include "unixlib.h"
+#include <gst/gst.h> + extern bool init_gstreamer(void) DECLSPEC_HIDDEN;
+extern void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) DECLSPEC_HIDDEN; +extern bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) DECLSPEC_HIDDEN; +extern GstCaps *wg_format_to_caps(const struct wg_format *format) DECLSPEC_HIDDEN; + extern NTSTATUS wg_transform_create(void *args) DECLSPEC_HIDDEN; extern NTSTATUS wg_transform_destroy(void *args) DECLSPEC_HIDDEN;
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c new file mode 100644 index 00000000000..8952acc1c2e --- /dev/null +++ b/dlls/winegstreamer/wg_format.c @@ -0,0 +1,436 @@ +/* + * GStreamer format helpers + * + * Copyright 2010 Maarten Lankhorst for CodeWeavers + * Copyright 2010 Aric Stewart for CodeWeavers + * Copyright 2019-2020 Zebediah Figura + * + * 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 + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/audio/audio.h> + +#include "winternl.h" +#include "dshow.h" + +#include "unix_private.h" + +GST_DEBUG_CATEGORY_EXTERN(wine); +#define GST_CAT_DEFAULT wine + +static enum wg_audio_format wg_audio_format_from_gst(GstAudioFormat format) +{ + switch (format) + { + case GST_AUDIO_FORMAT_U8: + return WG_AUDIO_FORMAT_U8; + case GST_AUDIO_FORMAT_S16LE: + return WG_AUDIO_FORMAT_S16LE; + case GST_AUDIO_FORMAT_S24LE: + return WG_AUDIO_FORMAT_S24LE; + case GST_AUDIO_FORMAT_S32LE: + return WG_AUDIO_FORMAT_S32LE; + case GST_AUDIO_FORMAT_F32LE: + return WG_AUDIO_FORMAT_F32LE; + case GST_AUDIO_FORMAT_F64LE: + return WG_AUDIO_FORMAT_F64LE; + default: + return WG_AUDIO_FORMAT_UNKNOWN; + } +} + +static uint32_t wg_channel_position_from_gst(GstAudioChannelPosition position) +{ + static const uint32_t position_map[] = + { + SPEAKER_FRONT_LEFT, + SPEAKER_FRONT_RIGHT, + SPEAKER_FRONT_CENTER, + SPEAKER_LOW_FREQUENCY, + SPEAKER_BACK_LEFT, + SPEAKER_BACK_RIGHT, + SPEAKER_FRONT_LEFT_OF_CENTER, + SPEAKER_FRONT_RIGHT_OF_CENTER, + SPEAKER_BACK_CENTER, + 0, + SPEAKER_SIDE_LEFT, + SPEAKER_SIDE_RIGHT, + SPEAKER_TOP_FRONT_LEFT, + SPEAKER_TOP_FRONT_RIGHT, + SPEAKER_TOP_FRONT_CENTER, + SPEAKER_TOP_CENTER, + SPEAKER_TOP_BACK_LEFT, + SPEAKER_TOP_BACK_RIGHT, + 0, + 0, + SPEAKER_TOP_BACK_CENTER, + }; + + if (position == GST_AUDIO_CHANNEL_POSITION_MONO) + return SPEAKER_FRONT_CENTER; + + if (position >= 0 && position < ARRAY_SIZE(position_map)) + return position_map[position]; + return 0; +} + +static uint32_t wg_channel_mask_from_gst(const GstAudioInfo *info) +{ + uint32_t mask = 0, position; + unsigned int i; + + for (i = 0; i < GST_AUDIO_INFO_CHANNELS(info); ++i) + { + if (!(position = wg_channel_position_from_gst(GST_AUDIO_INFO_POSITION(info, i)))) + { + GST_WARNING("Unsupported channel %#x.", GST_AUDIO_INFO_POSITION(info, i)); + return 0; + } + /* Make sure it's also in WinMM order. WinMM mandates that channels be + * ordered, as it were, from least to most significant SPEAKER_* bit. + * Hence we fail if the current channel was already specified, or if any + * higher bit was already specified. */ + if (mask & ~(position - 1)) + { + GST_WARNING("Unsupported channel order."); + return 0; + } + mask |= position; + } + return mask; +} + +static void wg_format_from_audio_info(struct wg_format *format, const GstAudioInfo *info) +{ + format->major_type = WG_MAJOR_TYPE_AUDIO; + format->u.audio.format = wg_audio_format_from_gst(GST_AUDIO_INFO_FORMAT(info)); + format->u.audio.channels = GST_AUDIO_INFO_CHANNELS(info); + format->u.audio.channel_mask = wg_channel_mask_from_gst(info); + format->u.audio.rate = GST_AUDIO_INFO_RATE(info); +} + +static enum wg_video_format wg_video_format_from_gst(GstVideoFormat format) +{ + switch (format) + { + case GST_VIDEO_FORMAT_BGRA: + return WG_VIDEO_FORMAT_BGRA; + case GST_VIDEO_FORMAT_BGRx: + return WG_VIDEO_FORMAT_BGRx; + case GST_VIDEO_FORMAT_BGR: + return WG_VIDEO_FORMAT_BGR; + case GST_VIDEO_FORMAT_RGB15: + return WG_VIDEO_FORMAT_RGB15; + case GST_VIDEO_FORMAT_RGB16: + return WG_VIDEO_FORMAT_RGB16; + case GST_VIDEO_FORMAT_AYUV: + return WG_VIDEO_FORMAT_AYUV; + case GST_VIDEO_FORMAT_I420: + return WG_VIDEO_FORMAT_I420; + case GST_VIDEO_FORMAT_NV12: + return WG_VIDEO_FORMAT_NV12; + case GST_VIDEO_FORMAT_UYVY: + return WG_VIDEO_FORMAT_UYVY; + case GST_VIDEO_FORMAT_YUY2: + return WG_VIDEO_FORMAT_YUY2; + case GST_VIDEO_FORMAT_YV12: + return WG_VIDEO_FORMAT_YV12; + case GST_VIDEO_FORMAT_YVYU: + return WG_VIDEO_FORMAT_YVYU; + default: + return WG_VIDEO_FORMAT_UNKNOWN; + } +} + +static void wg_format_from_video_info(struct wg_format *format, const GstVideoInfo *info) +{ + format->major_type = WG_MAJOR_TYPE_VIDEO; + format->u.video.format = wg_video_format_from_gst(GST_VIDEO_INFO_FORMAT(info)); + format->u.video.width = GST_VIDEO_INFO_WIDTH(info); + format->u.video.height = GST_VIDEO_INFO_HEIGHT(info); + format->u.video.fps_n = GST_VIDEO_INFO_FPS_N(info); + format->u.video.fps_d = GST_VIDEO_INFO_FPS_D(info); +} + +static void wg_format_from_caps_audio_mpeg(struct wg_format *format, const GstCaps *caps) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gint layer, channels, rate; + + if (!gst_structure_get_int(structure, "layer", &layer)) + { + GST_WARNING("Missing "layer" value."); + return; + } + if (!gst_structure_get_int(structure, "channels", &channels)) + { + GST_WARNING("Missing "channels" value."); + return; + } + if (!gst_structure_get_int(structure, "rate", &rate)) + { + GST_WARNING("Missing "rate" value."); + return; + } + + format->major_type = WG_MAJOR_TYPE_AUDIO; + + if (layer == 1) + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER1; + else if (layer == 2) + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER2; + else if (layer == 3) + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3; + + format->u.audio.channels = channels; + format->u.audio.rate = rate; +} + +static void wg_format_from_caps_video_cinepak(struct wg_format *format, const GstCaps *caps) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gint width, height, fps_n, fps_d; + + if (!gst_structure_get_int(structure, "width", &width)) + { + GST_WARNING("Missing "width" value."); + return; + } + if (!gst_structure_get_int(structure, "height", &height)) + { + GST_WARNING("Missing "height" value."); + return; + } + if (!gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d)) + { + fps_n = 0; + fps_d = 1; + } + + format->major_type = WG_MAJOR_TYPE_VIDEO; + format->u.video.format = WG_VIDEO_FORMAT_CINEPAK; + format->u.video.width = width; + format->u.video.height = height; + format->u.video.fps_n = fps_n; + format->u.video.fps_d = fps_d; +} + +void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + const char *name = gst_structure_get_name(structure); + + memset(format, 0, sizeof(*format)); + + if (!strcmp(name, "audio/x-raw")) + { + GstAudioInfo info; + + if (gst_audio_info_from_caps(&info, caps)) + wg_format_from_audio_info(format, &info); + } + else if (!strcmp(name, "video/x-raw")) + { + GstVideoInfo info; + + if (gst_video_info_from_caps(&info, caps)) + wg_format_from_video_info(format, &info); + } + else if (!strcmp(name, "audio/mpeg")) + { + wg_format_from_caps_audio_mpeg(format, caps); + } + else if (!strcmp(name, "video/x-cinepak")) + { + wg_format_from_caps_video_cinepak(format, caps); + } + else + { + gchar *str = gst_caps_to_string(caps); + + GST_FIXME("Unhandled caps %s.", str); + g_free(str); + } +} + +static GstAudioFormat wg_audio_format_to_gst(enum wg_audio_format format) +{ + switch (format) + { + case WG_AUDIO_FORMAT_U8: return GST_AUDIO_FORMAT_U8; + case WG_AUDIO_FORMAT_S16LE: return GST_AUDIO_FORMAT_S16LE; + case WG_AUDIO_FORMAT_S24LE: return GST_AUDIO_FORMAT_S24LE; + case WG_AUDIO_FORMAT_S32LE: return GST_AUDIO_FORMAT_S32LE; + case WG_AUDIO_FORMAT_F32LE: return GST_AUDIO_FORMAT_F32LE; + case WG_AUDIO_FORMAT_F64LE: return GST_AUDIO_FORMAT_F64LE; + default: return GST_AUDIO_FORMAT_UNKNOWN; + } +} + +static void wg_channel_mask_to_gst(GstAudioChannelPosition *positions, uint32_t mask, uint32_t channel_count) +{ + const uint32_t orig_mask = mask; + unsigned int i; + DWORD bit; + + static const GstAudioChannelPosition position_map[] = + { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, + GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, + }; + + for (i = 0; i < channel_count; ++i) + { + positions[i] = GST_AUDIO_CHANNEL_POSITION_NONE; + if (BitScanForward(&bit, mask)) + { + if (bit < ARRAY_SIZE(position_map)) + positions[i] = position_map[bit]; + else + GST_WARNING("Invalid channel mask %#x.\n", orig_mask); + mask &= ~(1 << bit); + } + else + { + GST_WARNING("Incomplete channel mask %#x.\n", orig_mask); + } + } +} + +static GstCaps *wg_format_to_caps_audio(const struct wg_format *format) +{ + GstAudioChannelPosition positions[32]; + GstAudioFormat audio_format; + GstAudioInfo info; + + if ((audio_format = wg_audio_format_to_gst(format->u.audio.format)) == GST_AUDIO_FORMAT_UNKNOWN) + return NULL; + + wg_channel_mask_to_gst(positions, format->u.audio.channel_mask, format->u.audio.channels); + gst_audio_info_set_format(&info, audio_format, format->u.audio.rate, format->u.audio.channels, positions); + return gst_audio_info_to_caps(&info); +} + +static GstVideoFormat wg_video_format_to_gst(enum wg_video_format format) +{ + switch (format) + { + case WG_VIDEO_FORMAT_BGRA: return GST_VIDEO_FORMAT_BGRA; + case WG_VIDEO_FORMAT_BGRx: return GST_VIDEO_FORMAT_BGRx; + case WG_VIDEO_FORMAT_BGR: return GST_VIDEO_FORMAT_BGR; + case WG_VIDEO_FORMAT_RGB15: return GST_VIDEO_FORMAT_RGB15; + case WG_VIDEO_FORMAT_RGB16: return GST_VIDEO_FORMAT_RGB16; + case WG_VIDEO_FORMAT_AYUV: return GST_VIDEO_FORMAT_AYUV; + case WG_VIDEO_FORMAT_I420: return GST_VIDEO_FORMAT_I420; + case WG_VIDEO_FORMAT_NV12: return GST_VIDEO_FORMAT_NV12; + case WG_VIDEO_FORMAT_UYVY: return GST_VIDEO_FORMAT_UYVY; + case WG_VIDEO_FORMAT_YUY2: return GST_VIDEO_FORMAT_YUY2; + case WG_VIDEO_FORMAT_YV12: return GST_VIDEO_FORMAT_YV12; + case WG_VIDEO_FORMAT_YVYU: return GST_VIDEO_FORMAT_YVYU; + default: return GST_VIDEO_FORMAT_UNKNOWN; + } +} + +static GstCaps *wg_format_to_caps_video(const struct wg_format *format) +{ + GstVideoFormat video_format; + GstVideoInfo info; + unsigned int i; + GstCaps *caps; + + if ((video_format = wg_video_format_to_gst(format->u.video.format)) == GST_VIDEO_FORMAT_UNKNOWN) + return NULL; + + gst_video_info_set_format(&info, video_format, format->u.video.width, abs(format->u.video.height)); + if ((caps = gst_video_info_to_caps(&info))) + { + /* Clear some fields that shouldn't prevent us from connecting. */ + for (i = 0; i < gst_caps_get_size(caps); ++i) + { + gst_structure_remove_fields(gst_caps_get_structure(caps, i), + "framerate", "pixel-aspect-ratio", "colorimetry", "chroma-site", NULL); + } + } + return caps; +} + +GstCaps *wg_format_to_caps(const struct wg_format *format) +{ + switch (format->major_type) + { + case WG_MAJOR_TYPE_UNKNOWN: + return NULL; + case WG_MAJOR_TYPE_AUDIO: + return wg_format_to_caps_audio(format); + case WG_MAJOR_TYPE_VIDEO: + return wg_format_to_caps_video(format); + } + assert(0); + return NULL; +} + +bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) +{ + if (a->major_type != b->major_type) + return false; + + switch (a->major_type) + { + case WG_MAJOR_TYPE_UNKNOWN: + return false; + + case WG_MAJOR_TYPE_AUDIO: + return a->u.audio.format == b->u.audio.format + && a->u.audio.channels == b->u.audio.channels + && a->u.audio.rate == b->u.audio.rate; + + case WG_MAJOR_TYPE_VIDEO: + /* Do not compare FPS. */ + return a->u.video.format == b->u.video.format + && a->u.video.width == b->u.video.width + && abs(a->u.video.height) == abs(b->u.video.height); + } + + assert(0); + return false; +} diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index ac8c5a2b95c..6828865eef6 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -111,399 +111,6 @@ struct wg_parser_stream uint64_t duration; };
-static enum wg_audio_format wg_audio_format_from_gst(GstAudioFormat format) -{ - switch (format) - { - case GST_AUDIO_FORMAT_U8: - return WG_AUDIO_FORMAT_U8; - case GST_AUDIO_FORMAT_S16LE: - return WG_AUDIO_FORMAT_S16LE; - case GST_AUDIO_FORMAT_S24LE: - return WG_AUDIO_FORMAT_S24LE; - case GST_AUDIO_FORMAT_S32LE: - return WG_AUDIO_FORMAT_S32LE; - case GST_AUDIO_FORMAT_F32LE: - return WG_AUDIO_FORMAT_F32LE; - case GST_AUDIO_FORMAT_F64LE: - return WG_AUDIO_FORMAT_F64LE; - default: - return WG_AUDIO_FORMAT_UNKNOWN; - } -} - -static uint32_t wg_channel_position_from_gst(GstAudioChannelPosition position) -{ - static const uint32_t position_map[] = - { - SPEAKER_FRONT_LEFT, - SPEAKER_FRONT_RIGHT, - SPEAKER_FRONT_CENTER, - SPEAKER_LOW_FREQUENCY, - SPEAKER_BACK_LEFT, - SPEAKER_BACK_RIGHT, - SPEAKER_FRONT_LEFT_OF_CENTER, - SPEAKER_FRONT_RIGHT_OF_CENTER, - SPEAKER_BACK_CENTER, - 0, - SPEAKER_SIDE_LEFT, - SPEAKER_SIDE_RIGHT, - SPEAKER_TOP_FRONT_LEFT, - SPEAKER_TOP_FRONT_RIGHT, - SPEAKER_TOP_FRONT_CENTER, - SPEAKER_TOP_CENTER, - SPEAKER_TOP_BACK_LEFT, - SPEAKER_TOP_BACK_RIGHT, - 0, - 0, - SPEAKER_TOP_BACK_CENTER, - }; - - if (position == GST_AUDIO_CHANNEL_POSITION_MONO) - return SPEAKER_FRONT_CENTER; - - if (position >= 0 && position < ARRAY_SIZE(position_map)) - return position_map[position]; - return 0; -} - -static uint32_t wg_channel_mask_from_gst(const GstAudioInfo *info) -{ - uint32_t mask = 0, position; - unsigned int i; - - for (i = 0; i < GST_AUDIO_INFO_CHANNELS(info); ++i) - { - if (!(position = wg_channel_position_from_gst(GST_AUDIO_INFO_POSITION(info, i)))) - { - GST_WARNING("Unsupported channel %#x.", GST_AUDIO_INFO_POSITION(info, i)); - return 0; - } - /* Make sure it's also in WinMM order. WinMM mandates that channels be - * ordered, as it were, from least to most significant SPEAKER_* bit. - * Hence we fail if the current channel was already specified, or if any - * higher bit was already specified. */ - if (mask & ~(position - 1)) - { - GST_WARNING("Unsupported channel order."); - return 0; - } - mask |= position; - } - return mask; -} - -static void wg_format_from_audio_info(struct wg_format *format, const GstAudioInfo *info) -{ - format->major_type = WG_MAJOR_TYPE_AUDIO; - format->u.audio.format = wg_audio_format_from_gst(GST_AUDIO_INFO_FORMAT(info)); - format->u.audio.channels = GST_AUDIO_INFO_CHANNELS(info); - format->u.audio.channel_mask = wg_channel_mask_from_gst(info); - format->u.audio.rate = GST_AUDIO_INFO_RATE(info); -} - -static enum wg_video_format wg_video_format_from_gst(GstVideoFormat format) -{ - switch (format) - { - case GST_VIDEO_FORMAT_BGRA: - return WG_VIDEO_FORMAT_BGRA; - case GST_VIDEO_FORMAT_BGRx: - return WG_VIDEO_FORMAT_BGRx; - case GST_VIDEO_FORMAT_BGR: - return WG_VIDEO_FORMAT_BGR; - case GST_VIDEO_FORMAT_RGB15: - return WG_VIDEO_FORMAT_RGB15; - case GST_VIDEO_FORMAT_RGB16: - return WG_VIDEO_FORMAT_RGB16; - case GST_VIDEO_FORMAT_AYUV: - return WG_VIDEO_FORMAT_AYUV; - case GST_VIDEO_FORMAT_I420: - return WG_VIDEO_FORMAT_I420; - case GST_VIDEO_FORMAT_NV12: - return WG_VIDEO_FORMAT_NV12; - case GST_VIDEO_FORMAT_UYVY: - return WG_VIDEO_FORMAT_UYVY; - case GST_VIDEO_FORMAT_YUY2: - return WG_VIDEO_FORMAT_YUY2; - case GST_VIDEO_FORMAT_YV12: - return WG_VIDEO_FORMAT_YV12; - case GST_VIDEO_FORMAT_YVYU: - return WG_VIDEO_FORMAT_YVYU; - default: - return WG_VIDEO_FORMAT_UNKNOWN; - } -} - -static void wg_format_from_video_info(struct wg_format *format, const GstVideoInfo *info) -{ - format->major_type = WG_MAJOR_TYPE_VIDEO; - format->u.video.format = wg_video_format_from_gst(GST_VIDEO_INFO_FORMAT(info)); - format->u.video.width = GST_VIDEO_INFO_WIDTH(info); - format->u.video.height = GST_VIDEO_INFO_HEIGHT(info); - format->u.video.fps_n = GST_VIDEO_INFO_FPS_N(info); - format->u.video.fps_d = GST_VIDEO_INFO_FPS_D(info); -} - -static void wg_format_from_caps_audio_mpeg(struct wg_format *format, const GstCaps *caps) -{ - const GstStructure *structure = gst_caps_get_structure(caps, 0); - gint layer, channels, rate; - - if (!gst_structure_get_int(structure, "layer", &layer)) - { - GST_WARNING("Missing "layer" value."); - return; - } - if (!gst_structure_get_int(structure, "channels", &channels)) - { - GST_WARNING("Missing "channels" value."); - return; - } - if (!gst_structure_get_int(structure, "rate", &rate)) - { - GST_WARNING("Missing "rate" value."); - return; - } - - format->major_type = WG_MAJOR_TYPE_AUDIO; - - if (layer == 1) - format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER1; - else if (layer == 2) - format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER2; - else if (layer == 3) - format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3; - - format->u.audio.channels = channels; - format->u.audio.rate = rate; -} - -static void wg_format_from_caps_video_cinepak(struct wg_format *format, const GstCaps *caps) -{ - const GstStructure *structure = gst_caps_get_structure(caps, 0); - gint width, height, fps_n, fps_d; - - if (!gst_structure_get_int(structure, "width", &width)) - { - GST_WARNING("Missing "width" value."); - return; - } - if (!gst_structure_get_int(structure, "height", &height)) - { - GST_WARNING("Missing "height" value."); - return; - } - if (!gst_structure_get_fraction(structure, "framerate", &fps_n, &fps_d)) - { - fps_n = 0; - fps_d = 1; - } - - format->major_type = WG_MAJOR_TYPE_VIDEO; - format->u.video.format = WG_VIDEO_FORMAT_CINEPAK; - format->u.video.width = width; - format->u.video.height = height; - format->u.video.fps_n = fps_n; - format->u.video.fps_d = fps_d; -} - -static void wg_format_from_caps(struct wg_format *format, const GstCaps *caps) -{ - const GstStructure *structure = gst_caps_get_structure(caps, 0); - const char *name = gst_structure_get_name(structure); - - memset(format, 0, sizeof(*format)); - - if (!strcmp(name, "audio/x-raw")) - { - GstAudioInfo info; - - if (gst_audio_info_from_caps(&info, caps)) - wg_format_from_audio_info(format, &info); - } - else if (!strcmp(name, "video/x-raw")) - { - GstVideoInfo info; - - if (gst_video_info_from_caps(&info, caps)) - wg_format_from_video_info(format, &info); - } - else if (!strcmp(name, "audio/mpeg")) - { - wg_format_from_caps_audio_mpeg(format, caps); - } - else if (!strcmp(name, "video/x-cinepak")) - { - wg_format_from_caps_video_cinepak(format, caps); - } - else - { - gchar *str = gst_caps_to_string(caps); - - GST_FIXME("Unhandled caps %s.", str); - g_free(str); - } -} - -static GstAudioFormat wg_audio_format_to_gst(enum wg_audio_format format) -{ - switch (format) - { - case WG_AUDIO_FORMAT_U8: return GST_AUDIO_FORMAT_U8; - case WG_AUDIO_FORMAT_S16LE: return GST_AUDIO_FORMAT_S16LE; - case WG_AUDIO_FORMAT_S24LE: return GST_AUDIO_FORMAT_S24LE; - case WG_AUDIO_FORMAT_S32LE: return GST_AUDIO_FORMAT_S32LE; - case WG_AUDIO_FORMAT_F32LE: return GST_AUDIO_FORMAT_F32LE; - case WG_AUDIO_FORMAT_F64LE: return GST_AUDIO_FORMAT_F64LE; - default: return GST_AUDIO_FORMAT_UNKNOWN; - } -} - -static void wg_channel_mask_to_gst(GstAudioChannelPosition *positions, uint32_t mask, uint32_t channel_count) -{ - const uint32_t orig_mask = mask; - unsigned int i; - DWORD bit; - - static const GstAudioChannelPosition position_map[] = - { - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_LFE1, - GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, - GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, - GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, - GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, - GST_AUDIO_CHANNEL_POSITION_TOP_CENTER, - GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT, - GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER, - GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT, - GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT, - GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER, - GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT, - }; - - for (i = 0; i < channel_count; ++i) - { - positions[i] = GST_AUDIO_CHANNEL_POSITION_NONE; - if (BitScanForward(&bit, mask)) - { - if (bit < ARRAY_SIZE(position_map)) - positions[i] = position_map[bit]; - else - GST_WARNING("Invalid channel mask %#x.\n", orig_mask); - mask &= ~(1 << bit); - } - else - { - GST_WARNING("Incomplete channel mask %#x.\n", orig_mask); - } - } -} - -static GstCaps *wg_format_to_caps_audio(const struct wg_format *format) -{ - GstAudioChannelPosition positions[32]; - GstAudioFormat audio_format; - GstAudioInfo info; - - if ((audio_format = wg_audio_format_to_gst(format->u.audio.format)) == GST_AUDIO_FORMAT_UNKNOWN) - return NULL; - - wg_channel_mask_to_gst(positions, format->u.audio.channel_mask, format->u.audio.channels); - gst_audio_info_set_format(&info, audio_format, format->u.audio.rate, format->u.audio.channels, positions); - return gst_audio_info_to_caps(&info); -} - -static GstVideoFormat wg_video_format_to_gst(enum wg_video_format format) -{ - switch (format) - { - case WG_VIDEO_FORMAT_BGRA: return GST_VIDEO_FORMAT_BGRA; - case WG_VIDEO_FORMAT_BGRx: return GST_VIDEO_FORMAT_BGRx; - case WG_VIDEO_FORMAT_BGR: return GST_VIDEO_FORMAT_BGR; - case WG_VIDEO_FORMAT_RGB15: return GST_VIDEO_FORMAT_RGB15; - case WG_VIDEO_FORMAT_RGB16: return GST_VIDEO_FORMAT_RGB16; - case WG_VIDEO_FORMAT_AYUV: return GST_VIDEO_FORMAT_AYUV; - case WG_VIDEO_FORMAT_I420: return GST_VIDEO_FORMAT_I420; - case WG_VIDEO_FORMAT_NV12: return GST_VIDEO_FORMAT_NV12; - case WG_VIDEO_FORMAT_UYVY: return GST_VIDEO_FORMAT_UYVY; - case WG_VIDEO_FORMAT_YUY2: return GST_VIDEO_FORMAT_YUY2; - case WG_VIDEO_FORMAT_YV12: return GST_VIDEO_FORMAT_YV12; - case WG_VIDEO_FORMAT_YVYU: return GST_VIDEO_FORMAT_YVYU; - default: return GST_VIDEO_FORMAT_UNKNOWN; - } -} - -static GstCaps *wg_format_to_caps_video(const struct wg_format *format) -{ - GstVideoFormat video_format; - GstVideoInfo info; - unsigned int i; - GstCaps *caps; - - if ((video_format = wg_video_format_to_gst(format->u.video.format)) == GST_VIDEO_FORMAT_UNKNOWN) - return NULL; - - gst_video_info_set_format(&info, video_format, format->u.video.width, abs(format->u.video.height)); - if ((caps = gst_video_info_to_caps(&info))) - { - /* Clear some fields that shouldn't prevent us from connecting. */ - for (i = 0; i < gst_caps_get_size(caps); ++i) - { - gst_structure_remove_fields(gst_caps_get_structure(caps, i), - "framerate", "pixel-aspect-ratio", "colorimetry", "chroma-site", NULL); - } - } - return caps; -} - -static GstCaps *wg_format_to_caps(const struct wg_format *format) -{ - switch (format->major_type) - { - case WG_MAJOR_TYPE_UNKNOWN: - return NULL; - case WG_MAJOR_TYPE_AUDIO: - return wg_format_to_caps_audio(format); - case WG_MAJOR_TYPE_VIDEO: - return wg_format_to_caps_video(format); - } - assert(0); - return NULL; -} - -static bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) -{ - if (a->major_type != b->major_type) - return false; - - switch (a->major_type) - { - case WG_MAJOR_TYPE_UNKNOWN: - return false; - - case WG_MAJOR_TYPE_AUDIO: - return a->u.audio.format == b->u.audio.format - && a->u.audio.channels == b->u.audio.channels - && a->u.audio.rate == b->u.audio.rate; - - case WG_MAJOR_TYPE_VIDEO: - /* Do not compare FPS. */ - return a->u.video.format == b->u.video.format - && a->u.video.width == b->u.video.width - && abs(a->u.video.height) == abs(b->u.video.height); - } - - assert(0); - return false; -} - static NTSTATUS wg_parser_get_stream_count(void *args) { struct wg_parser_get_stream_count_params *params = args;
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51931 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52391 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winegstreamer/mfplat.c | 72 +++++++++++++++++++++++++++++- dlls/winegstreamer/quartz_parser.c | 8 ++++ dlls/winegstreamer/unixlib.h | 12 +++++ dlls/winegstreamer/wg_format.c | 7 +++ dlls/winegstreamer/wm_reader.c | 8 ++++ dlls/winegstreamer/wma_decoder.c | 11 +++++ 6 files changed, 116 insertions(+), 2 deletions(-)
diff --git a/dlls/winegstreamer/mfplat.c b/dlls/winegstreamer/mfplat.c index a111bbe196d..ccd1d400d2c 100644 --- a/dlls/winegstreamer/mfplat.c +++ b/dlls/winegstreamer/mfplat.c @@ -635,6 +635,10 @@ IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) case WG_MAJOR_TYPE_UNKNOWN: return NULL;
+ case WG_MAJOR_TYPE_WMA: + FIXME("WMA format not implemented!\n"); + return NULL; + case WG_MAJOR_TYPE_AUDIO: return mf_media_type_from_wg_format_audio(format);
@@ -741,9 +745,57 @@ static void mf_media_type_to_wg_format_video(IMFMediaType *type, struct wg_forma FIXME("Unrecognized video subtype %s.\n", debugstr_guid(&subtype)); }
+static void mf_media_type_to_wg_format_wma(IMFMediaType *type, struct wg_format *format, + UINT32 version) +{ + UINT32 rate, depth, channels, block_align, bytes_per_second, codec_data_len; + BYTE codec_data[64]; + + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate))) + { + FIXME("Sample rate is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels))) + { + FIXME("Channel count is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_align))) + { + FIXME("Block alignment is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth))) + { + FIXME("Depth is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, codec_data, sizeof(codec_data), &codec_data_len))) + { + FIXME("Codec data is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second))) + { + FIXME("Bitrate is not set.\n"); + bytes_per_second = 0; + } + + format->major_type = WG_MAJOR_TYPE_WMA; + format->u.wma.version = version; + format->u.wma.bitrate = bytes_per_second * 8; + format->u.wma.rate = rate; + format->u.wma.depth = depth; + format->u.wma.channels = channels; + format->u.wma.block_align = block_align; + format->u.wma.codec_data_len = codec_data_len; + memcpy(format->u.wma.codec_data, codec_data, codec_data_len); +} + void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) { - GUID major_type; + GUID major_type, subtype;
memset(format, 0, sizeof(*format));
@@ -752,9 +804,25 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) FIXME("Major type is not set.\n"); return; } + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + { + FIXME("Subtype is not set.\n"); + return; + }
if (IsEqualGUID(&major_type, &MFMediaType_Audio)) - mf_media_type_to_wg_format_audio(type, format); + { + if (IsEqualGUID(&subtype, &MEDIASUBTYPE_MSAUDIO1)) + mf_media_type_to_wg_format_wma(type, format, 1); + else if (IsEqualGUID(&subtype, &MFAudioFormat_WMAudioV8)) + mf_media_type_to_wg_format_wma(type, format, 2); + else if (IsEqualGUID(&subtype, &MFAudioFormat_WMAudioV9)) + mf_media_type_to_wg_format_wma(type, format, 3); + else if (IsEqualGUID(&subtype, &MFAudioFormat_WMAudio_Lossless)) + mf_media_type_to_wg_format_wma(type, format, 4); + else + mf_media_type_to_wg_format_audio(type, format); + } else if (IsEqualGUID(&major_type, &MFMediaType_Video)) mf_media_type_to_wg_format_video(type, format); else diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index 45313ebda27..e06c55ccfe0 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -319,6 +319,10 @@ unsigned int wg_format_get_max_size(const struct wg_format *format) break; }
+ case WG_MAJOR_TYPE_WMA: + FIXME("WMA format not implemented!\n"); + return 0; + case WG_MAJOR_TYPE_UNKNOWN: FIXME("Cannot guess maximum sample size for unknown format.\n"); return 0; @@ -413,6 +417,10 @@ bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool case WG_MAJOR_TYPE_UNKNOWN: return false;
+ case WG_MAJOR_TYPE_WMA: + FIXME("WMA format not implemented!\n"); + return false; + case WG_MAJOR_TYPE_AUDIO: return amt_from_wg_format_audio(mt, format);
diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index c8b98da3a64..8e3f5e84bfb 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -37,6 +37,7 @@ struct wg_format WG_MAJOR_TYPE_UNKNOWN, WG_MAJOR_TYPE_VIDEO, WG_MAJOR_TYPE_AUDIO, + WG_MAJOR_TYPE_WMA, } major_type;
union @@ -88,6 +89,17 @@ struct wg_format uint32_t channel_mask; /* In WinMM format. */ uint32_t rate; } audio; + struct + { + uint32_t version; + uint32_t bitrate; + uint32_t rate; + uint32_t depth; + uint32_t channels; + uint32_t block_align; + uint32_t codec_data_len; + unsigned char codec_data[64]; + } wma; } u; };
diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 8952acc1c2e..8f771bb8abd 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -400,6 +400,9 @@ GstCaps *wg_format_to_caps(const struct wg_format *format) { case WG_MAJOR_TYPE_UNKNOWN: return NULL; + case WG_MAJOR_TYPE_WMA: + GST_FIXME("WMA format not implemented!\n"); + return NULL; case WG_MAJOR_TYPE_AUDIO: return wg_format_to_caps_audio(format); case WG_MAJOR_TYPE_VIDEO: @@ -419,6 +422,10 @@ bool wg_format_compare(const struct wg_format *a, const struct wg_format *b) case WG_MAJOR_TYPE_UNKNOWN: return false;
+ case WG_MAJOR_TYPE_WMA: + GST_FIXME("WMA format not implemented!\n"); + return false; + case WG_MAJOR_TYPE_AUDIO: return a->u.audio.format == b->u.audio.format && a->u.audio.channels == b->u.audio.channels diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index d40afb66afd..01518c6b9a8 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1687,6 +1687,9 @@ HRESULT wm_reader_get_output_format_count(struct wm_reader *reader, DWORD output *count = ARRAY_SIZE(video_formats); break;
+ case WG_MAJOR_TYPE_WMA: + FIXME("WMA format not implemented!\n"); + /* fallthrough */ case WG_MAJOR_TYPE_AUDIO: case WG_MAJOR_TYPE_UNKNOWN: *count = 1; @@ -1733,6 +1736,9 @@ HRESULT wm_reader_get_output_format(struct wm_reader *reader, DWORD output, format.u.audio.format = WG_AUDIO_FORMAT_S16LE; break;
+ case WG_MAJOR_TYPE_WMA: + FIXME("WMA format not implemented!\n"); + break; case WG_MAJOR_TYPE_UNKNOWN: break; } @@ -1808,6 +1814,8 @@ static const char *get_major_type_string(enum wg_major_type type) return "video"; case WG_MAJOR_TYPE_UNKNOWN: return "unknown"; + case WG_MAJOR_TYPE_WMA: + return "wma"; } assert(0); return NULL; diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index 3b051230a9e..b209ffc2e77 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -64,8 +64,19 @@ static inline struct wma_decoder *impl_from_IUnknown(IUnknown *iface)
static HRESULT try_create_wg_transform(struct wma_decoder *decoder) { + struct wg_format input_format, output_format; + if (decoder->wg_transform) wg_transform_destroy(decoder->wg_transform); + decoder->wg_transform = NULL; + + mf_media_type_to_wg_format(decoder->input_type, &input_format); + if (input_format.major_type == WG_MAJOR_TYPE_UNKNOWN) + return MF_E_INVALIDMEDIATYPE; + + mf_media_type_to_wg_format(decoder->output_type, &output_format); + if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) + return MF_E_INVALIDMEDIATYPE;
decoder->wg_transform = wg_transform_create(); if (decoder->wg_transform)
On 2/14/22 04:19, Rémi Bernon wrote:
@@ -752,9 +804,25 @@ void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) FIXME("Major type is not set.\n"); return; }
- if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
- {
FIXME("Subtype is not set.\n");
return;
- }
As long as we've already grabbed the subtype here, we could pass it to the other mf_media_type_to_wg_format_*() functions.
With caps created from the input / output formats.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51931 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52391 Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winegstreamer/gst_private.h | 3 ++- dlls/winegstreamer/main.c | 9 +++++-- dlls/winegstreamer/unixlib.h | 2 ++ dlls/winegstreamer/wg_format.c | 34 +++++++++++++++++++++-- dlls/winegstreamer/wg_transform.c | 45 +++++++++++++++++++++++++++++++ dlls/winegstreamer/wma_decoder.c | 2 +- 6 files changed, 89 insertions(+), 6 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index df82b229143..c1fe34373ce 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -96,7 +96,8 @@ uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream) DECLSPEC 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) DECLSPEC_HIDDEN;
-struct wg_transform *wg_transform_create(void) DECLSPEC_HIDDEN; +struct wg_transform *wg_transform_create(const struct wg_format *input_format, + const struct wg_format *output_format) DECLSPEC_HIDDEN; void wg_transform_destroy(struct wg_transform *transform) DECLSPEC_HIDDEN;
unsigned int wg_format_get_max_size(const struct wg_format *format); diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index f23fa3abcdf..f85e9995525 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -254,9 +254,14 @@ void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, __wine_unix_call(unix_handle, unix_wg_parser_stream_seek, ¶ms); }
-struct wg_transform *wg_transform_create(void) +struct wg_transform *wg_transform_create(const struct wg_format *input_format, + const struct wg_format *output_format) { - struct wg_transform_create_params params = {0}; + struct wg_transform_create_params params = + { + .input_format = input_format, + .output_format = output_format, + };
if (__wine_unix_call(unix_handle, unix_wg_transform_create, ¶ms)) return NULL; diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 8e3f5e84bfb..4adbb694766 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -232,6 +232,8 @@ struct wg_parser_stream_seek_params struct wg_transform_create_params { struct wg_transform *transform; + const struct wg_format *input_format; + const struct wg_format *output_format; };
enum unix_funcs diff --git a/dlls/winegstreamer/wg_format.c b/dlls/winegstreamer/wg_format.c index 8f771bb8abd..9d3d7808180 100644 --- a/dlls/winegstreamer/wg_format.c +++ b/dlls/winegstreamer/wg_format.c @@ -394,6 +394,37 @@ static GstCaps *wg_format_to_caps_video(const struct wg_format *format) return caps; }
+static GstCaps *wg_format_to_caps_wma(const struct wg_format *format) +{ + GstBuffer *buffer; + GstCaps *caps; + + caps = gst_caps_new_empty_simple("audio/x-wma"); + if (format->u.wma.version) + gst_caps_set_simple(caps, "wmaversion", G_TYPE_INT, format->u.wma.version, NULL); + + if (format->u.wma.bitrate) + gst_caps_set_simple(caps, "bitrate", G_TYPE_INT, format->u.wma.bitrate, NULL); + if (format->u.wma.rate) + gst_caps_set_simple(caps, "rate", G_TYPE_INT, format->u.wma.rate, NULL); + if (format->u.wma.depth) + gst_caps_set_simple(caps, "depth", G_TYPE_INT, format->u.wma.depth, NULL); + if (format->u.wma.channels) + gst_caps_set_simple(caps, "channels", G_TYPE_INT, format->u.wma.channels, NULL); + if (format->u.wma.block_align) + gst_caps_set_simple(caps, "block_align", G_TYPE_INT, format->u.wma.block_align, NULL); + + if (format->u.wma.codec_data_len) + { + buffer = gst_buffer_new_and_alloc(format->u.wma.codec_data_len); + gst_buffer_fill(buffer, 0, format->u.wma.codec_data, format->u.wma.codec_data_len); + gst_caps_set_simple(caps, "codec_data", GST_TYPE_BUFFER, buffer, NULL); + gst_buffer_unref(buffer); + } + + return caps; +} + GstCaps *wg_format_to_caps(const struct wg_format *format) { switch (format->major_type) @@ -401,8 +432,7 @@ GstCaps *wg_format_to_caps(const struct wg_format *format) case WG_MAJOR_TYPE_UNKNOWN: return NULL; case WG_MAJOR_TYPE_WMA: - GST_FIXME("WMA format not implemented!\n"); - return NULL; + return wg_format_to_caps_wma(format); case WG_MAJOR_TYPE_AUDIO: return wg_format_to_caps_audio(format); case WG_MAJOR_TYPE_VIDEO: diff --git a/dlls/winegstreamer/wg_transform.c b/dlls/winegstreamer/wg_transform.c index 822740da0d7..5debac87e4c 100644 --- a/dlls/winegstreamer/wg_transform.c +++ b/dlls/winegstreamer/wg_transform.c @@ -42,12 +42,29 @@ GST_DEBUG_CATEGORY_EXTERN(wine);
struct wg_transform { + GstPad *my_src, *my_sink; };
+static GstFlowReturn transform_sink_chain_cb(GstPad *pad, GstObject *parent, GstBuffer *buffer) +{ + struct wg_transform *transform = gst_pad_get_element_private(pad); + + GST_INFO("transform %p, buffer %p.", transform, buffer); + + gst_buffer_unref(buffer); + + return GST_FLOW_OK; +} + NTSTATUS wg_transform_destroy(void *args) { struct wg_transform *transform = args;
+ if (transform->my_sink) + g_object_unref(transform->my_sink); + if (transform->my_src) + g_object_unref(transform->my_src); + free(transform); return S_OK; } @@ -55,7 +72,11 @@ NTSTATUS wg_transform_destroy(void *args) NTSTATUS wg_transform_create(void *args) { struct wg_transform_create_params *params = args; + struct wg_format output_format = *params->output_format; + struct wg_format input_format = *params->input_format; + GstCaps *src_caps, *sink_caps; struct wg_transform *transform; + GstPadTemplate *template;
if (!init_gstreamer()) return E_FAIL; @@ -63,7 +84,31 @@ NTSTATUS wg_transform_create(void *args) if (!(transform = calloc(1, sizeof(*transform)))) return E_OUTOFMEMORY;
+ src_caps = wg_format_to_caps(&input_format); + assert(src_caps); + sink_caps = wg_format_to_caps(&output_format); + assert(sink_caps); + + template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps); + assert(template); + transform->my_src = gst_pad_new_from_template(template, "src"); + g_object_unref(template); + assert(transform->my_src); + + template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps); + assert(template); + transform->my_sink = gst_pad_new_from_template(template, "sink"); + g_object_unref(template); + assert(transform->my_sink); + + gst_pad_set_element_private(transform->my_sink, transform); + gst_pad_set_chain_function(transform->my_sink, transform_sink_chain_cb); + GST_INFO("Created winegstreamer transform %p.", transform); params->transform = transform; + + gst_caps_unref(src_caps); + gst_caps_unref(sink_caps); + return S_OK; } diff --git a/dlls/winegstreamer/wma_decoder.c b/dlls/winegstreamer/wma_decoder.c index b209ffc2e77..e8b47cc9bde 100644 --- a/dlls/winegstreamer/wma_decoder.c +++ b/dlls/winegstreamer/wma_decoder.c @@ -78,7 +78,7 @@ static HRESULT try_create_wg_transform(struct wma_decoder *decoder) if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) return MF_E_INVALIDMEDIATYPE;
- decoder->wg_transform = wg_transform_create(); + decoder->wg_transform = wg_transform_create(&input_format, &output_format); if (decoder->wg_transform) return S_OK;
On 2/14/22 04:19, Rémi Bernon wrote:
@@ -63,7 +84,31 @@ NTSTATUS wg_transform_create(void *args) if (!(transform = calloc(1, sizeof(*transform)))) return E_OUTOFMEMORY;
- src_caps = wg_format_to_caps(&input_format);
- assert(src_caps);
- sink_caps = wg_format_to_caps(&output_format);
- assert(sink_caps);
- template = gst_pad_template_new("src", GST_PAD_SRC, GST_PAD_ALWAYS, src_caps);
- assert(template);
- transform->my_src = gst_pad_new_from_template(template, "src");
- g_object_unref(template);
- assert(transform->my_src);
- template = gst_pad_template_new("sink", GST_PAD_SINK, GST_PAD_ALWAYS, sink_caps);
- assert(template);
- transform->my_sink = gst_pad_new_from_template(template, "sink");
- g_object_unref(template);
- assert(transform->my_sink);
Although I'd personally prefer to assert() the results of memory allocation, I suspect we don't want to do that in Wine.
On 2/14/22 04:19, Rémi Bernon wrote:
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 222bce3b2c7..df82b229143 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -96,6 +96,9 @@ uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream) DECLSPEC 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) DECLSPEC_HIDDEN;
+struct wg_transform *wg_transform_create(void) DECLSPEC_HIDDEN; +void wg_transform_destroy(struct wg_transform *transform) DECLSPEC_HIDDEN;
I believe we can omit DECLSPEC_HIDDEN in PE modules now. It still has an effect when compiling without MinGW, but I don't think we care enough about that case.
@@ -1963,6 +1963,16 @@ static void init_gstreamer_once(void) gst_version_string(), GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO); }
+bool init_gstreamer(void) +{
- static pthread_once_t init_once = PTHREAD_ONCE_INIT;
- if (pthread_once(&init_once, init_gstreamer_once))
return false;
- return true;
So "return !pthread_once(...)"? Or even return int from this function.
+struct wg_transform +{ +};
Unfortunately I don't think this is portable.