0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 03:52:22 +02:00

ao_oss: switch to new AO API

This commit is contained in:
wm4 2013-06-07 15:05:20 +02:00
parent f8f4285671
commit 15202ebc76

View File

@ -34,6 +34,7 @@
#include <string.h>
#include "config.h"
#include "core/options.h"
#include "core/mp_msg.h"
#include "audio/mixer.h"
@ -48,17 +49,6 @@
#include "audio/format.h"
#include "ao.h"
#include "audio_out_internal.h"
static const ao_info_t info =
{
"OSS/ioctl audio output",
"oss",
"A'rpi",
""
};
LIBAO_EXTERN(oss)
static int format2oss(int format)
{
@ -173,7 +163,7 @@ static int volume_oss4(ao_control_vol_t *vol, int cmd)
#endif
// to set/get/query special features/parameters
static int control(int cmd, void *arg)
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
switch (cmd) {
case AOCONTROL_GET_VOLUME:
@ -188,7 +178,7 @@ static int control(int cmd, void *arg)
return CONTROL_OK;
#endif
if (AF_FORMAT_IS_AC3(ao_data.format))
if (AF_FORMAT_IS_AC3(ao->format))
return CONTROL_TRUE;
if ((fd = open(oss_mixer_device, O_RDONLY)) != -1) {
@ -216,16 +206,16 @@ static int control(int cmd, void *arg)
}
// open & setup audio device
// return: 1=success 0=fail
static int init(int rate, const struct mp_chmap *channels, int format,
int flags)
// return: 0=success -1=fail
static int init(struct ao *ao, char *params)
{
struct MPOpts *opts = ao->opts;
char *mixer_channels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
int oss_format;
char *mdev = mixer_device, *mchan = mixer_channel;
char *mdev = opts->mixer_device, *mchan = opts->mixer_channel;
mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n", rate,
ao_data.channels.num, af_fmt2str_short(format));
mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n", ao->samplerate,
ao->channels.num, af_fmt2str_short(ao->format));
if (ao_subdevice) {
char *m, *c;
@ -296,7 +286,7 @@ static int init(int rate, const struct mp_chmap *channels, int format,
mp_tmsg(MSGT_AO, MSGL_ERR,
"[AO OSS] audio_setup: Can't open audio device %s: %s\n",
dsp, strerror(errno));
return 0;
return -1;
}
#ifdef __linux__
@ -304,7 +294,7 @@ static int init(int rate, const struct mp_chmap *channels, int format,
if (fcntl(audio_fd, F_SETFL, 0) < 0) {
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO OSS] audio_setup: Can't make file "
"descriptor blocking: %s\n", strerror(errno));
return 0;
return -1;
}
#endif
@ -312,47 +302,45 @@ static int init(int rate, const struct mp_chmap *channels, int format,
fcntl(audio_fd, F_SETFD, FD_CLOEXEC);
#endif
if (AF_FORMAT_IS_AC3(format)) {
ao_data.samplerate = rate;
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
if (AF_FORMAT_IS_AC3(ao->format)) {
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
}
ac3_retry:
if (AF_FORMAT_IS_AC3(format))
format = AF_FORMAT_AC3_NE;
ao_data.format = format;
oss_format = format2oss(format);
if (AF_FORMAT_IS_AC3(ao->format))
ao->format = AF_FORMAT_AC3_NE;
oss_format = format2oss(ao->format);
if (oss_format == -1) {
#if BYTE_ORDER == BIG_ENDIAN
oss_format = AFMT_S16_BE;
#else
oss_format = AFMT_S16_LE;
#endif
format = AF_FORMAT_S16_NE;
ao->format = AF_FORMAT_S16_NE;
}
if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &oss_format) < 0 ||
oss_format != format2oss(format))
oss_format != format2oss(ao->format))
{
mp_tmsg(MSGT_AO, MSGL_WARN, "[AO OSS] Can't set audio device %s to %s "
"output, trying %s...\n", dsp, af_fmt2str_short(format),
"output, trying %s...\n", dsp, af_fmt2str_short(ao->format),
af_fmt2str_short(AF_FORMAT_S16_NE));
format = AF_FORMAT_S16_NE;
ao->format = AF_FORMAT_S16_NE;
goto ac3_retry;
}
ao_data.format = oss2format(oss_format);
if (ao_data.format == -1)
return 0;
ao->format = oss2format(oss_format);
if (ao->format == -1)
return -1;
mp_msg(MSGT_AO, MSGL_V, "audio_setup: sample format: %s (requested: %s)\n",
af_fmt2str_short(ao_data.format), af_fmt2str_short(format));
mp_msg(MSGT_AO, MSGL_V, "audio_setup: sample format: %s\n",
af_fmt2str_short(ao->format));
if (!AF_FORMAT_IS_AC3(format)) {
if (!AF_FORMAT_IS_AC3(ao->format)) {
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_alsa_def(&sel);
if (!ao_chmap_sel_adjust(&ao_data, &sel, &ao_data.channels))
return 0;
int reqchannels = ao_data.channels.num;
if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
return -1;
int reqchannels = ao->channels.num;
// We only use SNDCTL_DSP_CHANNELS for >2 channels, in case some drivers don't have it
if (reqchannels > 2) {
int nchannels = reqchannels;
@ -361,27 +349,25 @@ ac3_retry:
{
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO OSS] audio_setup: Failed to "
"set audio device to %d channels.\n", reqchannels);
return 0;
return -1;
}
} else {
int c = reqchannels - 1;
if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &c) == -1) {
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO OSS] audio_setup: Failed to "
"set audio device to %d channels.\n", reqchannels);
return 0;
return -1;
}
if (!ao_chmap_sel_get_def(&ao_data, &sel, &ao_data.channels, c + 1))
return 0;
if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, c + 1))
return -1;
}
mp_msg(MSGT_AO, MSGL_V,
"audio_setup: using %d channels (requested: %d)\n",
ao_data.channels.num, reqchannels);
ao->channels.num, reqchannels);
// set rate
ao_data.samplerate = rate;
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
mp_msg(MSGT_AO, MSGL_V,
"audio_setup: using %d Hz samplerate (requested: %d)\n",
ao_data.samplerate, rate);
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
mp_msg(MSGT_AO, MSGL_V, "audio_setup: using %d Hz samplerate\n",
ao->samplerate);
}
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz) == -1) {
@ -390,29 +376,29 @@ ac3_retry:
"support SNDCTL_DSP_GETOSPACE\n");
if (ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &r) == -1)
mp_msg(MSGT_AO, MSGL_V, "audio_setup: %d bytes/frag (config.h)\n",
ao_data.outburst);
ao->outburst);
else {
ao_data.outburst = r;
ao->outburst = r;
mp_msg(MSGT_AO, MSGL_V, "audio_setup: %d bytes/frag (GETBLKSIZE)\n",
ao_data.outburst);
ao->outburst);
}
} else {
mp_msg(MSGT_AO, MSGL_V,
"audio_setup: frags: %3d/%d (%d bytes/frag) free: %6d\n",
zz.fragments, zz.fragstotal, zz.fragsize, zz.bytes);
if (ao_data.buffersize == -1)
ao_data.buffersize = zz.bytes;
ao_data.outburst = zz.fragsize;
if (ao->buffersize == -1)
ao->buffersize = zz.bytes;
ao->outburst = zz.fragsize;
}
if (ao_data.buffersize == -1) {
if (ao->buffersize == -1) {
// Measuring buffer size:
void *data;
ao_data.buffersize = 0;
ao->buffersize = 0;
#ifdef HAVE_AUDIO_SELECT
data = malloc(ao_data.outburst);
memset(data, 0, ao_data.outburst);
while (ao_data.buffersize < 0x40000) {
data = malloc(ao->outburst);
memset(data, 0, ao->outburst);
while (ao->buffersize < 0x40000) {
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
@ -421,42 +407,28 @@ ac3_retry:
tv.tv_usec = 0;
if (!select(audio_fd + 1, NULL, &rfds, NULL, &tv))
break;
write(audio_fd, data, ao_data.outburst);
ao_data.buffersize += ao_data.outburst;
write(audio_fd, data, ao->outburst);
ao->buffersize += ao->outburst;
}
free(data);
if (ao_data.buffersize == 0) {
if (ao->buffersize == 0) {
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO OSS]\n *** Your audio driver "
"DOES NOT support select() ***\n Recompile mpv with "
"#undef HAVE_AUDIO_SELECT in config.h !\n\n");
return 0;
return -1;
}
#endif
}
ao_data.bps = ao_data.channels.num;
switch (ao_data.format & AF_FORMAT_BITS_MASK) {
case AF_FORMAT_8BIT:
break;
case AF_FORMAT_16BIT:
ao_data.bps *= 2;
break;
case AF_FORMAT_24BIT:
ao_data.bps *= 3;
break;
case AF_FORMAT_32BIT:
ao_data.bps *= 4;
break;
}
ao->bps = ao->channels.num * (af_fmt2bits(ao->format) / 8);
ao->outburst -= ao->outburst % ao->bps; // round down
ao->bps *= ao->samplerate;
ao_data.outburst -= ao_data.outburst % ao_data.bps; // round down
ao_data.bps *= ao_data.samplerate;
return 1;
return 0;
}
// close audio device
static void uninit(int immed)
static void uninit(struct ao *ao, bool immed)
{
if (audio_fd == -1)
return;
@ -474,10 +446,10 @@ static void uninit(int immed)
}
// stop playing and empty buffers (for seeking/pause)
static void reset(void)
static void reset(struct ao *ao)
{
int oss_format;
uninit(1);
uninit(ao, true);
audio_fd = open(dsp, O_WRONLY);
if (audio_fd < 0) {
mp_tmsg(MSGT_AO, MSGL_ERR, "[AO OSS]\nFatal error: *** CANNOT "
@ -489,46 +461,25 @@ static void reset(void)
fcntl(audio_fd, F_SETFD, FD_CLOEXEC);
#endif
oss_format = format2oss(ao_data.format);
if (AF_FORMAT_IS_AC3(ao_data.format))
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
oss_format = format2oss(ao->format);
if (AF_FORMAT_IS_AC3(ao->format))
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
ioctl(audio_fd, SNDCTL_DSP_SETFMT, &oss_format);
if (!AF_FORMAT_IS_AC3(ao_data.format)) {
if (ao_data.channels.num > 2)
ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao_data.channels.num);
if (!AF_FORMAT_IS_AC3(ao->format)) {
if (ao->channels.num > 2)
ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &ao->channels.num);
else {
int c = ao_data.channels.num - 1;
int c = ao->channels.num - 1;
ioctl(audio_fd, SNDCTL_DSP_STEREO, &c);
}
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao_data.samplerate);
ioctl(audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate);
}
}
// stop playing, keep buffers (for pause)
static void audio_pause(void)
{
prepause_space = get_space();
uninit(1);
}
// resume playing, after audio_pause()
static void audio_resume(void)
{
int fillcnt;
reset();
fillcnt = get_space() - prepause_space;
if (fillcnt > 0 && !(ao_data.format & AF_FORMAT_SPECIAL_MASK)) {
void *silence = calloc(fillcnt, 1);
play(silence, fillcnt, 0);
free(silence);
}
}
// return: how many bytes can be played without blocking
static int get_space(void)
static int get_space(struct ao *ao)
{
int playsize = ao_data.outburst;
int playsize = ao->outburst;
#ifdef SNDCTL_DSP_GETOSPACE
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz) != -1) {
@ -552,45 +503,83 @@ static int get_space(void)
}
#endif
return ao_data.outburst;
return ao->outburst;
}
// stop playing, keep buffers (for pause)
static void audio_pause(struct ao *ao)
{
prepause_space = get_space(ao);
uninit(ao, true);
}
// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
static int play(void *data, int len, int flags)
static int play(struct ao *ao, void *data, int len, int flags)
{
if (len == 0)
return len;
if (len > ao_data.outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
len /= ao_data.outburst;
len *= ao_data.outburst;
if (len > ao->outburst || !(flags & AOPLAY_FINAL_CHUNK)) {
len /= ao->outburst;
len *= ao->outburst;
}
len = write(audio_fd, data, len);
return len;
}
// resume playing, after audio_pause()
static void audio_resume(struct ao *ao)
{
int fillcnt;
reset(ao);
fillcnt = get_space(ao) - prepause_space;
if (fillcnt > 0 && !(ao->format & AF_FORMAT_SPECIAL_MASK)) {
void *silence = calloc(fillcnt, 1);
play(ao, silence, fillcnt, 0);
free(silence);
}
}
static int audio_delay_method = 2;
// return: delay in seconds between first and last sample in buffer
static float get_delay(void)
static float get_delay(struct ao *ao)
{
/* Calculate how many bytes/second is sent out */
if (audio_delay_method == 2) {
#ifdef SNDCTL_DSP_GETODELAY
int r = 0;
if (ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &r) != -1)
return ((float)r) / (float)ao_data.bps;
return ((float)r) / (float)ao->bps;
#endif
audio_delay_method = 1; // fallback if not supported
}
if (audio_delay_method == 1) {
// SNDCTL_DSP_GETOSPACE
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &zz) != -1) {
return ((float)(ao_data.buffersize -
zz.bytes)) / (float)ao_data.bps;
return ((float)(ao->buffersize -
zz.bytes)) / (float)ao->bps;
}
audio_delay_method = 0; // fallback if not supported
}
return ((float)ao_data.buffersize) / (float)ao_data.bps;
return ((float)ao->buffersize) / (float)ao->bps;
}
const struct ao_driver audio_out_oss = {
.info = &(const struct ao_info) {
"OSS/ioctl audio output",
"oss",
"A'rpi",
""
},
.init = init,
.uninit = uninit,
.control = control,
.get_space = get_space,
.play = play,
.get_delay = get_delay,
.pause = audio_pause,
.resume = audio_resume,
.reset = reset,
};