Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/winegstreamer/gst_private.h | 1 + dlls/winegstreamer/gstdemux.c | 35 +++++----- dlls/winegstreamer/wg_parser.c | 109 ++++++++++++++++++++++++++++++- 3 files changed, 129 insertions(+), 16 deletions(-)
diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 75025fdf7d5..128546a0265 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -128,6 +128,7 @@ struct wg_format } format;
uint32_t channels; + uint32_t channel_mask; /* In WinMM format. */ uint32_t rate; } audio; } u; diff --git a/dlls/winegstreamer/gstdemux.c b/dlls/winegstreamer/gstdemux.c index d85bb02160e..75b1cece095 100644 --- a/dlls/winegstreamer/gstdemux.c +++ b/dlls/winegstreamer/gstdemux.c @@ -102,20 +102,6 @@ static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface); static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface); static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface);
-static DWORD channel_mask_from_count(uint32_t count) -{ - switch (count) - { - case 1: return KSAUDIO_SPEAKER_MONO; - case 2: return KSAUDIO_SPEAKER_STEREO; - case 4: return KSAUDIO_SPEAKER_SURROUND; - case 5: return KSAUDIO_SPEAKER_5POINT1 & ~SPEAKER_LOW_FREQUENCY; - case 6: return KSAUDIO_SPEAKER_5POINT1; - case 8: return KSAUDIO_SPEAKER_7POINT1; - default: return 0; - } -} - static bool amt_from_wg_format_audio(AM_MEDIA_TYPE *mt, const struct wg_format *format) { mt->majortype = MEDIATYPE_Audio; @@ -220,7 +206,7 @@ static bool amt_from_wg_format_audio(AM_MEDIA_TYPE *mt, const struct wg_format * wave_format->Format.wBitsPerSample = depth; wave_format->Format.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX); wave_format->Samples.wValidBitsPerSample = depth; - wave_format->dwChannelMask = channel_mask_from_count(format->u.audio.channels); + wave_format->dwChannelMask = format->u.audio.channel_mask; wave_format->SubFormat = is_float ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; mt->lSampleSize = wave_format->Format.nBlockAlign; } @@ -423,6 +409,25 @@ static bool amt_to_wg_format_audio(const AM_MEDIA_TYPE *mt, struct wg_format *fo format->u.audio.channels = audio_format->nChannels; format->u.audio.rate = audio_format->nSamplesPerSec;
+ if (audio_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + const WAVEFORMATEXTENSIBLE *ext_format = (const WAVEFORMATEXTENSIBLE *)mt->pbFormat; + + format->u.audio.channel_mask = ext_format->dwChannelMask; + } + else + { + if (audio_format->nChannels == 1) + format->u.audio.channel_mask = KSAUDIO_SPEAKER_MONO; + else if (audio_format->nChannels == 2) + format->u.audio.channel_mask = KSAUDIO_SPEAKER_STEREO; + else + { + ERR("Unexpected channel count %u.\n", audio_format->nChannels); + return false; + } + } + for (i = 0; i < ARRAY_SIZE(format_map); ++i) { if (IsEqualGUID(&mt->subtype, format_map[i].subtype) diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 272029217a9..2540ecd80df 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -109,11 +109,70 @@ static enum wg_audio_format wg_audio_format_from_gst(GstAudioFormat format) } }
+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 < 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); }
@@ -273,15 +332,63 @@ static GstAudioFormat wg_audio_format_to_gst(enum wg_audio_format format) } }
+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;
- gst_audio_info_set_format(&info, audio_format, format->u.audio.rate, format->u.audio.channels, 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); }