Sunday, September 25, 2022

Re: xine's ffmpegaudio doesn't downmix, sio_getpar() reports 6 channels instead of 2

Index: src/audio_out/audio_sndio_out.c
--- src/audio_out/audio_sndio_out.c.orig
+++ src/audio_out/audio_sndio_out.c
@@ -37,6 +37,8 @@
#include <xine/audio_out.h>
#include "bswap.h"

+#include "speakers.h"
+
#define GAP_TOLERANCE AO_MAX_GAP
#define PCT_TO_MIDI(p) (((p) * SIO_MAXVOL + 50) / 100)

@@ -248,11 +250,15 @@ static void ao_sndio_exit(ao_driver_t *this_gen)
{
sndio_driver_t *this = (sndio_driver_t *) this_gen;

+ this->xine->config->unregister_callbacks (this->xine->config, "audio.output.speaker_arrangement", NULL, this, sizeof (*this));
+
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_sndio_out: ao_sndio_exit called\n");

if (this->hdl != NULL)
sio_close(this->hdl);
+
+ free (this);
}

static int ao_sndio_get_property (ao_driver_t *this_gen, int property)
@@ -316,13 +322,27 @@ static int ao_sndio_ctrl(ao_driver_t *this_gen, int cm
return 0;
}

+static void sndio_speaker_arrangement_cb (void *user_data,
+ xine_cfg_entry_t *entry);
+
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data)
{
sndio_class_t *class = (sndio_class_t *) class_gen;
+ config_values_t *config = class->xine->config;
sndio_driver_t *this;

lprintf ("audio_sndio_out: open_plugin called\n");

+ AUDIO_DEVICE_SPEAKER_ARRANGEMENT_TYPES;
+ int speakers;
+
+ speakers = config->register_enum (config,
+ "audio.output.speaker_arrangement",
+ STEREO,
+ (char **)speaker_arrangement,
+ AUDIO_DEVICE_SPEAKER_ARRANGEMENT_HELP,
+ 0, sndio_speaker_arrangement_cb, this);
+
this = calloc(1, sizeof (sndio_driver_t));
if (!this)
return NULL;
@@ -333,10 +353,16 @@ static ao_driver_t *open_plugin (audio_driver_class_t
* Set capabilities
*/
this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO |
- AO_CAP_MODE_4CHANNEL | AO_CAP_MODE_4_1CHANNEL |
- AO_CAP_MODE_5CHANNEL | AO_CAP_MODE_5_1CHANNEL |
AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL | AO_CAP_8BITS |
AO_CAP_16BITS;
+ if (speakers == SURROUND4)
+ this->capabilities |= AO_CAP_MODE_4CHANNEL;
+ if (speakers == SURROUND41)
+ this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
+ if (speakers == SURROUND5)
+ this->capabilities |= AO_CAP_MODE_5CHANNEL;
+ if (speakers == SURROUND51)
+ this->capabilities |= AO_CAP_MODE_5_1CHANNEL;

this->ao_driver.get_capabilities = ao_sndio_get_capabilities;
this->ao_driver.get_property = ao_sndio_get_property;
@@ -352,6 +378,32 @@ static ao_driver_t *open_plugin (audio_driver_class_t
this->ao_driver.control = ao_sndio_ctrl;

return &this->ao_driver;
+}
+
+static void sndio_speaker_arrangement_cb (void *user_data,
+ xine_cfg_entry_t *entry) {
+ sndio_driver_t *this = (sndio_driver_t *) user_data;
+ int32_t value = entry->num_value;
+ if (value == SURROUND4) {
+ this->capabilities |= AO_CAP_MODE_4CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_4CHANNEL;
+ }
+ if (value == SURROUND41) {
+ this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_4_1CHANNEL;
+ }
+ if (value == SURROUND5) {
+ this->capabilities |= AO_CAP_MODE_5CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_5CHANNEL;
+ }
+ if (value >= SURROUND51) {
+ this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_5_1CHANNEL;
+ }
}

/*
On Thu, 22 Sep 2022, adr wrote:
> Hi,
>
> first of all, I've never contributed to the xine project, and
> I don't have any experience with sndio, so bear with me...
>
> Frustrated with the bad sound playing a 6 channels aac audio I decided to
> take a look. The device is a usb one:
>
> $ dmesg | grep -i audio
> uaudio0 at uhub2 port 3 configuration 1 interface 1 "GeneralPlus USB Audio
> Device" rev 1.10/1.00 addr 6
> uaudio0: class v1, full-speed, sync, channels: 2 play, 1 rec, 8 ctls
> audio0 at uaudio0
> uhidev2 at uhub2 port 3 configuration 1 interface 3 "GeneralPlus USB Audio
> Device" rev 1.10/1.00 addr 6
>
> It has only 2 channels for playback.
>
> The file has 5.1 aac audio:
> $ ffprobe file.mp4
> [...]
> Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, 5.1, fltp,
> 224 kb/s (default)
> [...]
>
> Xine's ffmpegaudio plugin is supposed to do downmix (please, correct
> me if I'm wrong), but:
>
> $ xine --verbose=2 file.mp4
> [...]
> audio_out: ao_open (0x585415540)
> audio_sndio_out: ao_sndio_open bits=16 rate=48000, mode=128
> audio_sndio_out: ao_sndio_open 6 channels output
> [...]
>
> The code in xine-lib-1.2.12/src/audio_out/audio_sndio_out.c by Brad
> Smith is following the recommendations at sio_open(3), there is no
> problem there.
>
> sio_getpar() is returning 6 channels as valid, so the sound ends
> messed up. The right speaker gets mostly ambient sound and the
> voices are almost only audible through the left speaker.
>
> mplayer and mpv have means to force downmix, but xine is the only
> media player which can play 1080p h264 video with a decent performance
> (if the bitrate is not too high) on an rpi4 (no hardware acceleration,
> cpu at 2.2Gz)
>
> One workaround for this particular case could be adding the
> configuration setting "audio.output.speaker_arrangement" to
> audio_sndio_out.c.

This patch does that, following the alsa and oss drivers.
I CC'd it to ports@

============================================
Index: src/audio_out/audio_sndio_out.c
--- src/audio_out/audio_sndio_out.c.orig
+++ src/audio_out/audio_sndio_out.c
@@ -37,6 +37,8 @@
#include <xine/audio_out.h>
#include "bswap.h"

+#include "speakers.h"
+
#define GAP_TOLERANCE AO_MAX_GAP
#define PCT_TO_MIDI(p) (((p) * SIO_MAXVOL + 50) / 100)

@@ -248,11 +250,15 @@ static void ao_sndio_exit(ao_driver_t *this_gen)
{
sndio_driver_t *this = (sndio_driver_t *) this_gen;

+ this->xine->config->unregister_callbacks (this->xine->config, "audio.output.speaker_arrangement", NULL, this, sizeof (*this));
+
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_sndio_out: ao_sndio_exit called\n");

if (this->hdl != NULL)
sio_close(this->hdl);
+
+ free (this);
}

static int ao_sndio_get_property (ao_driver_t *this_gen, int property)
@@ -316,13 +322,27 @@ static int ao_sndio_ctrl(ao_driver_t *this_gen, int cm
return 0;
}

+static void sndio_speaker_arrangement_cb (void *user_data,
+ xine_cfg_entry_t *entry);
+
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data)
{
sndio_class_t *class = (sndio_class_t *) class_gen;
+ config_values_t *config = class->xine->config;
sndio_driver_t *this;

lprintf ("audio_sndio_out: open_plugin called\n");

+ AUDIO_DEVICE_SPEAKER_ARRANGEMENT_TYPES;
+ int speakers;
+
+ speakers = config->register_enum (config,
+ "audio.output.speaker_arrangement",
+ STEREO,
+ (char **)speaker_arrangement,
+ AUDIO_DEVICE_SPEAKER_ARRANGEMENT_HELP,
+ 0, sndio_speaker_arrangement_cb, this);
+
this = calloc(1, sizeof (sndio_driver_t));
if (!this)
return NULL;
@@ -333,10 +353,16 @@ static ao_driver_t *open_plugin (audio_driver_class_t
* Set capabilities
*/
this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO |
- AO_CAP_MODE_4CHANNEL | AO_CAP_MODE_4_1CHANNEL |
- AO_CAP_MODE_5CHANNEL | AO_CAP_MODE_5_1CHANNEL |
AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL | AO_CAP_8BITS |
AO_CAP_16BITS;
+ if (speakers == SURROUND4)
+ this->capabilities |= AO_CAP_MODE_4CHANNEL;
+ if (speakers == SURROUND41)
+ this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
+ if (speakers == SURROUND5)
+ this->capabilities |= AO_CAP_MODE_5CHANNEL;
+ if (speakers == SURROUND51)
+ this->capabilities |= AO_CAP_MODE_5_1CHANNEL;

this->ao_driver.get_capabilities = ao_sndio_get_capabilities;
this->ao_driver.get_property = ao_sndio_get_property;
@@ -352,6 +378,32 @@ static ao_driver_t *open_plugin (audio_driver_class_t
this->ao_driver.control = ao_sndio_ctrl;

return &this->ao_driver;
+}
+
+static void sndio_speaker_arrangement_cb (void *user_data,
+ xine_cfg_entry_t *entry) {
+ sndio_driver_t *this = (sndio_driver_t *) user_data;
+ int32_t value = entry->num_value;
+ if (value == SURROUND4) {
+ this->capabilities |= AO_CAP_MODE_4CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_4CHANNEL;
+ }
+ if (value == SURROUND41) {
+ this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_4_1CHANNEL;
+ }
+ if (value == SURROUND5) {
+ this->capabilities |= AO_CAP_MODE_5CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_5CHANNEL;
+ }
+ if (value >= SURROUND51) {
+ this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_5_1CHANNEL;
+ }
}

/*

No comments:

Post a Comment