From a288b94831bcd9ef65c23475d8499e53fea69c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Wed, 16 Feb 2022 12:19:06 +0100 Subject: [PATCH] winegstreamer: Move format helpers to a dedicated source. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- dlls/winegstreamer/Makefile.in | 1 + dlls/winegstreamer/unix_private.h | 32 +++ dlls/winegstreamer/wg_format.c | 436 ++++++++++++++++++++++++++++++ dlls/winegstreamer/wg_parser.c | 397 +-------------------------- 4 files changed, 471 insertions(+), 395 deletions(-) create mode 100644 dlls/winegstreamer/unix_private.h create mode 100644 dlls/winegstreamer/wg_format.c diff --git a/dlls/winegstreamer/Makefile.in b/dlls/winegstreamer/Makefile.in index c53e914e246..d9805e3d797 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 \ wm_asyncreader.c \ wm_reader.c \ diff --git a/dlls/winegstreamer/unix_private.h b/dlls/winegstreamer/unix_private.h new file mode 100644 index 00000000000..b483638403d --- /dev/null +++ b/dlls/winegstreamer/unix_private.h @@ -0,0 +1,32 @@ +/* + * 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" + +#include + +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; + +#endif /* __WINE_WINEGSTREAMER_UNIX_PRIVATE_H */ 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 +#include +#include + +#include +#include +#include + +#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 013566b25e9..a73685a2e69 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); @@ -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;