winegstreamer: Explicitly translate the channel mask.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zebediah Figura 2021-02-18 17:01:25 -06:00 committed by Alexandre Julliard
parent b44d3a3908
commit 8697c6c0db
3 changed files with 129 additions and 16 deletions

View file

@ -128,6 +128,7 @@ struct wg_format
} format;
uint32_t channels;
uint32_t channel_mask; /* In WinMM format. */
uint32_t rate;
} audio;
} u;

View file

@ -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)

View file

@ -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);
}