diff options
author | wm4 <wm4@nowhere> | 2014-09-15 21:01:27 +0200 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2014-09-15 22:02:04 +0200 |
commit | b951326a38df056b95727e91a4461c0512be7bb3 (patch) | |
tree | 9ab8b1a36db8c35a91b914585d13a2d2fa0c815e | |
parent | 9ca1582953bd13838139fc30fb819e9cfce67041 (diff) | |
download | mpv-b951326a38df056b95727e91a4461c0512be7bb3.tar.bz2 mpv-b951326a38df056b95727e91a4461c0512be7bb3.tar.xz |
ao_oss: remove duplicate audio device open code
The code for reopening the audio device was separate, and duplicated
some of the "real" open code. This was very badly done, and major
required parts of initialization were skipped. Fix this by removing
the code duplication. This consists mainly of moving the code for
opening the device to a separate function, and adding some changes
to handle format changes gracefully. (We can't change the audio
format on the fly, but we can at least not explode and play noise
when that happens.)
As a minor change, actually always use SNDCTL_DSP_RESET when closing
the audio device. We don't want to wait until the rest of the buffer
is played.
Also, don't use strerror() when printing the error message that
reopening failed, simply because reopen_device() takes care of this,
and also errno might be clobbered at this point.
-rw-r--r-- | audio/out/ao_oss.c | 212 |
1 files changed, 108 insertions, 104 deletions
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c index a2d2243ebd..3f7a6edb47 100644 --- a/audio/out/ao_oss.c +++ b/audio/out/ao_oss.c @@ -233,8 +233,7 @@ static int device_writable(struct ao *ao) return poll(&fd, 1, 0); } -// close audio device -static void uninit(struct ao *ao) +static void close_device(struct ao *ao) { struct priv *p = ao->priv; if (p->audio_fd == -1) @@ -246,51 +245,20 @@ static void uninit(struct ao *ao) p->audio_fd = -1; } -// open & setup audio device -// return: 0=success -1=fail -static int init(struct ao *ao) +// close audio device +static void uninit(struct ao *ao) +{ + close_device(ao); +} + +static int reopen_device(struct ao *ao, bool allow_format_changes) { struct priv *p = ao->priv; int oss_format; - const char *mchan = NULL; - if (p->cfg_oss_mixer_channel && p->cfg_oss_mixer_channel[0]) - mchan = p->cfg_oss_mixer_channel; - - if (mchan) { - int fd, devs, i; - - if ((fd = open(p->oss_mixer_device, O_RDONLY)) == -1) { - MP_ERR(ao, "Can't open mixer device %s: %s\n", - p->oss_mixer_device, strerror(errno)); - } else { - ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs); - close(fd); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!strcasecmp(mixer_channels[i], mchan)) { - if (!(devs & (1 << i))) { - MP_ERR(ao, "Audio card mixer does not have " - "channel '%s', using default.\n", mchan); - i = SOUND_MIXER_NRDEVICES + 1; - break; - } - p->oss_mixer_channel = i; - break; - } - } - if (i == SOUND_MIXER_NRDEVICES) { - MP_ERR(ao, "Audio card mixer does not have " - "channel '%s', using default.\n", mchan); - } - } - } else { - p->oss_mixer_channel = SOUND_MIXER_PCM; - } - - MP_VERBOSE(ao, "using '%s' dsp device\n", p->dsp); - MP_VERBOSE(ao, "using '%s' mixer device\n", p->oss_mixer_device); - MP_VERBOSE(ao, "using '%s' mixer device\n", mixer_channels[p->oss_mixer_channel]); + int samplerate = ao->samplerate; + int format = ao->format; + struct mp_chmap channels = ao->channels; #ifdef __linux__ p->audio_fd = open(p->dsp, O_WRONLY | O_NONBLOCK); @@ -314,67 +282,65 @@ static int init(struct ao *ao) fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC); #endif - ao->format = af_fmt_from_planar(ao->format); - - if (AF_FORMAT_IS_AC3(ao->format)) { - ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate); + if (AF_FORMAT_IS_AC3(format)) { + ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &samplerate); } ac3_retry: - if (AF_FORMAT_IS_AC3(ao->format)) - ao->format = AF_FORMAT_AC3; - oss_format = format2oss(ao->format); + if (AF_FORMAT_IS_AC3(format)) + format = AF_FORMAT_AC3; + oss_format = format2oss(format); if (oss_format == -1) { MP_VERBOSE(ao, "Unknown/not supported internal format: %s\n", - af_fmt_to_str(ao->format)); + af_fmt_to_str(format)); #if defined(AFMT_S32_LE) && defined(AFMT_S32_BE) #if BYTE_ORDER == BIG_ENDIAN oss_format = AFMT_S32_BE; #else oss_format = AFMT_S32_LE; #endif - ao->format = AF_FORMAT_S32; + format = AF_FORMAT_S32; #elif defined(AFMT_S24_LE) && defined(AFMT_S24_BE) #if BYTE_ORDER == BIG_ENDIAN oss_format = AFMT_S24_BE; #else oss_format = AFMT_S24_LE; #endif - ao->format = AF_FORMAT_S24; + format = AF_FORMAT_S24; #else #if BYTE_ORDER == BIG_ENDIAN oss_format = AFMT_S16_BE; #else oss_format = AFMT_S16_LE; #endif - ao->format = AF_FORMAT_S16; + format = AF_FORMAT_S16; #endif } if (ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format) < 0 || - oss_format != format2oss(ao->format)) + oss_format != format2oss(format)) { MP_WARN(ao, "Can't set audio device %s to %s output, trying %s...\n", - p->dsp, af_fmt_to_str(ao->format), + p->dsp, af_fmt_to_str(format), af_fmt_to_str(AF_FORMAT_S16)); - ao->format = AF_FORMAT_S16; + format = AF_FORMAT_S16; goto ac3_retry; } - ao->format = oss2format(oss_format); - if (ao->format == -1) { + format = oss2format(oss_format); + if (format == -1) { MP_ERR(ao, "Unknown/Unsupported OSS format: %x.\n", oss_format); goto fail; } - MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(ao->format)); + MP_VERBOSE(ao, "sample format: %s\n", af_fmt_to_str(format)); - if (!AF_FORMAT_IS_AC3(ao->format)) { + if (!AF_FORMAT_IS_AC3(format)) { struct mp_chmap_sel sel = {0}; for (int n = 0; n < MP_NUM_CHANNELS + 1; n++) mp_chmap_sel_add_map(&sel, &oss_layouts[n]); - if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels)) + if (!ao_chmap_sel_adjust(ao, &sel, &channels)) goto fail; - int reqchannels = ao->channels.num; + int reqchannels = 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; @@ -392,14 +358,14 @@ ac3_retry: reqchannels); goto fail; } - if (!ao_chmap_sel_get_def(ao, &sel, &ao->channels, c + 1)) + if (!ao_chmap_sel_get_def(ao, &sel, &channels, c + 1)) goto fail; } MP_VERBOSE(ao, "using %d channels (requested: %d)\n", - ao->channels.num, reqchannels); + channels.num, reqchannels); // set rate - ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate); - MP_VERBOSE(ao, "using %d Hz samplerate\n", ao->samplerate); + ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &samplerate); + MP_VERBOSE(ao, "using %d Hz samplerate\n", samplerate); } if (ioctl(p->audio_fd, SNDCTL_DSP_GETOSPACE, &p->zz) == -1) { @@ -418,6 +384,78 @@ ac3_retry: p->outburst = p->zz.fragsize; } + if (allow_format_changes) { + ao->format = format; + ao->samplerate = samplerate; + ao->channels = channels; + } else { + if (format != ao->format || samplerate != ao->samplerate || + !mp_chmap_equals(&channels, &ao->channels)) + { + MP_ERR(ao, "Could not reselect previous audio format.\n"); + goto fail; + } + } + + p->outburst -= p->outburst % (channels.num * af_fmt2bps(format)); // round down + + return 0; + +fail: + close_device(ao); + return -1; +} + +// open & setup audio device +// return: 0=success -1=fail +static int init(struct ao *ao) +{ + struct priv *p = ao->priv; + + const char *mchan = NULL; + if (p->cfg_oss_mixer_channel && p->cfg_oss_mixer_channel[0]) + mchan = p->cfg_oss_mixer_channel; + + if (mchan) { + int fd, devs, i; + + if ((fd = open(p->oss_mixer_device, O_RDONLY)) == -1) { + MP_ERR(ao, "Can't open mixer device %s: %s\n", + p->oss_mixer_device, strerror(errno)); + } else { + ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs); + close(fd); + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!strcasecmp(mixer_channels[i], mchan)) { + if (!(devs & (1 << i))) { + MP_ERR(ao, "Audio card mixer does not have " + "channel '%s', using default.\n", mchan); + i = SOUND_MIXER_NRDEVICES + 1; + break; + } + p->oss_mixer_channel = i; + break; + } + } + if (i == SOUND_MIXER_NRDEVICES) { + MP_ERR(ao, "Audio card mixer does not have " + "channel '%s', using default.\n", mchan); + } + } + } else { + p->oss_mixer_channel = SOUND_MIXER_PCM; + } + + MP_VERBOSE(ao, "using '%s' dsp device\n", p->dsp); + MP_VERBOSE(ao, "using '%s' mixer device\n", p->oss_mixer_device); + MP_VERBOSE(ao, "using '%s' mixer device\n", mixer_channels[p->oss_mixer_channel]); + + ao->format = af_fmt_from_planar(ao->format); + + if (reopen_device(ao, true) < 0) + goto fail; + if (p->buffersize == -1) { // Measuring buffer size: void *data = malloc(p->outburst); @@ -438,10 +476,6 @@ ac3_retry: } } - ao->bps = ao->channels.num * af_fmt2bps(ao->format); - p->outburst -= p->outburst % ao->bps; // round down - ao->bps *= ao->samplerate; - return 0; fail: @@ -459,48 +493,18 @@ static void drain(struct ao *ao) #endif } -#if !KEEP_DEVICE -static void close_device(struct ao *ao) -{ - struct priv *p = ao->priv; - close(p->audio_fd); - p->audio_fd = -1; -} -#endif - // stop playing and empty buffers (for seeking/pause) static void reset(struct ao *ao) { - struct priv *p = ao->priv; #if KEEP_DEVICE + struct priv *p = ao->priv; ioctl(p->audio_fd, SNDCTL_DSP_RESET, NULL); #else close_device(ao); - p->audio_fd = open(p->dsp, O_WRONLY); - if (p->audio_fd < 0) { - MP_ERR(ao, "Fatal error: *** CANNOT " - "RE-OPEN / RESET AUDIO DEVICE *** %s\n", strerror(errno)); + if (reopen_device(ao, false) < 0) { + MP_ERR(ao, "Fatal error: *** CANNOT RE-OPEN / RESET AUDIO DEVICE ***\n"); return; } - -#if defined(FD_CLOEXEC) && defined(F_SETFD) - fcntl(p->audio_fd, F_SETFD, FD_CLOEXEC); -#endif - - int oss_format = format2oss(ao->format); - if (AF_FORMAT_IS_AC3(ao->format)) - ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate); - ioctl(p->audio_fd, SNDCTL_DSP_SETFMT, &oss_format); - if (!AF_FORMAT_IS_AC3(ao->format)) { - int c = ao->channels.num; - if (ao->channels.num > 2) - ioctl(p->audio_fd, SNDCTL_DSP_CHANNELS, &c); - else { - c--; - ioctl(p->audio_fd, SNDCTL_DSP_STEREO, &c); - } - ioctl(p->audio_fd, SNDCTL_DSP_SPEED, &ao->samplerate); - } #endif } |