diff options
author | wm4 <wm4@nowhere> | 2012-11-05 17:02:04 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2012-11-12 20:06:14 +0100 |
commit | d4bdd0473d6f43132257c9fb3848d829755167a3 (patch) | |
tree | 8021c2f7da1841393c8c832105e20cd527826d6c /audio/out | |
parent | bd48deba77bd5582c5829d6fe73a7d2571088aba (diff) | |
download | mpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.bz2 mpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.xz |
Rename directories, move files (step 1 of 2) (does not compile)
Tis drops the silly lib prefixes, and attempts to organize the tree in
a more logical way. Make the top-level directory less cluttered as
well.
Renames the following directories:
libaf -> audio/filter
libao2 -> audio/out
libvo -> video/out
libmpdemux -> demux
Split libmpcodecs:
vf* -> video/filter
vd*, dec_video.* -> video/decode
mp_image*, img_format*, ... -> video/
ad*, dec_audio.* -> audio/decode
libaf/format.* is moved to audio/ - this is similar to how mp_image.*
is located in video/.
Move most top-level .c/.h files to core. (talloc.c/.h is left on top-
level, because it's external.) Park some of the more annoying files
in compat/. Some of these are relicts from the time mplayer used
ffmpeg internals.
sub/ is not split, because it's too much of a mess (subtitle code is
mixed with OSD display and rendering).
Maybe the organization of core is not ideal: it mixes playback core
(like mplayer.c) and utility helpers (like bstr.c/h). Should the need
arise, the playback core will be moved somewhere else, while core
contains all helper and common code.
Diffstat (limited to 'audio/out')
-rw-r--r-- | audio/out/ao.c | 294 | ||||
-rw-r--r-- | audio/out/ao.h | 140 | ||||
-rw-r--r-- | audio/out/ao_alsa.c | 868 | ||||
-rw-r--r-- | audio/out/ao_coreaudio.c | 1283 | ||||
-rw-r--r-- | audio/out/ao_dsound.c | 648 | ||||
-rw-r--r-- | audio/out/ao_jack.c | 361 | ||||
-rw-r--r-- | audio/out/ao_lavc.c | 621 | ||||
-rw-r--r-- | audio/out/ao_null.c | 129 | ||||
-rw-r--r-- | audio/out/ao_openal.c | 280 | ||||
-rw-r--r-- | audio/out/ao_oss.c | 560 | ||||
-rw-r--r-- | audio/out/ao_pcm.c | 256 | ||||
-rw-r--r-- | audio/out/ao_portaudio.c | 431 | ||||
-rw-r--r-- | audio/out/ao_pulse.c | 554 | ||||
-rw-r--r-- | audio/out/ao_rsound.c | 214 | ||||
-rw-r--r-- | audio/out/audio_out_internal.h | 65 |
15 files changed, 6704 insertions, 0 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c new file mode 100644 index 0000000000..ab8e60b753 --- /dev/null +++ b/audio/out/ao.c @@ -0,0 +1,294 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "talloc.h" + +#include "config.h" +#include "audio_out.h" + +#include "mp_msg.h" + +// there are some globals: +struct ao *global_ao; +char *ao_subdevice = NULL; + +extern const struct ao_driver audio_out_oss; +extern const struct ao_driver audio_out_coreaudio; +extern const struct ao_driver audio_out_rsound; +extern const struct ao_driver audio_out_pulse; +extern const struct ao_driver audio_out_jack; +extern const struct ao_driver audio_out_openal; +extern const struct ao_driver audio_out_null; +extern const struct ao_driver audio_out_alsa; +extern const struct ao_driver audio_out_dsound; +extern const struct ao_driver audio_out_pcm; +extern const struct ao_driver audio_out_pss; +extern const struct ao_driver audio_out_lavc; +extern const struct ao_driver audio_out_portaudio; + +static const struct ao_driver * const audio_out_drivers[] = { +// native: +#ifdef CONFIG_COREAUDIO + &audio_out_coreaudio, +#endif +#ifdef CONFIG_PULSE + &audio_out_pulse, +#endif +#ifdef CONFIG_ALSA + &audio_out_alsa, +#endif +#ifdef CONFIG_OSS_AUDIO + &audio_out_oss, +#endif +#ifdef CONFIG_PORTAUDIO + &audio_out_portaudio, +#endif +#ifdef CONFIG_DSOUND + &audio_out_dsound, +#endif + // wrappers: +#ifdef CONFIG_JACK + &audio_out_jack, +#endif +#ifdef CONFIG_OPENAL + &audio_out_openal, +#endif + &audio_out_null, + // should not be auto-selected: + &audio_out_pcm, +#ifdef CONFIG_ENCODING + &audio_out_lavc, +#endif +#ifdef CONFIG_RSOUND + &audio_out_rsound, +#endif + NULL +}; + +void list_audio_out(void) +{ + int i=0; + mp_tmsg(MSGT_AO, MSGL_INFO, "Available audio output drivers:\n"); + mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_AUDIO_OUTPUTS\n"); + while (audio_out_drivers[i]) { + const ao_info_t *info = audio_out_drivers[i++]->info; + mp_msg(MSGT_GLOBAL, MSGL_INFO, "\t%s\t%s\n", info->short_name, + info->name); + } + mp_msg(MSGT_GLOBAL, MSGL_INFO,"\n"); +} + +struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input) +{ + struct ao *r = talloc(NULL, struct ao); + *r = (struct ao){.outburst = 512, .buffersize = -1, + .opts = opts, .input_ctx = input }; + return r; +} + +void ao_init(struct ao *ao, char **ao_list) +{ + /* Caller adding child blocks is not supported as we may call + * talloc_free_children() to clean up after failed open attempts. + */ + assert(talloc_total_blocks(ao) == 1); + struct ao backup = *ao; + + if (!ao_list) + goto try_defaults; + + // first try the preferred drivers, with their optional subdevice param: + while (*ao_list) { + char *ao_name = *ao_list; + if (!*ao_name) + goto try_defaults; // empty entry means try defaults + int ao_len; + char *params = strchr(ao_name, ':'); + if (params) { + ao_len = params - ao_name; + params++; + } else + ao_len = strlen(ao_name); + + mp_tmsg(MSGT_AO, MSGL_V, + "Trying preferred audio driver '%.*s', options '%s'\n", + ao_len, ao_name, params ? params : "[none]"); + + const struct ao_driver *audio_out = NULL; + for (int i = 0; audio_out_drivers[i]; i++) { + audio_out = audio_out_drivers[i]; + if (!strncmp(audio_out->info->short_name, ao_name, ao_len)) + break; + audio_out = NULL; + } + if (audio_out) { + // name matches, try it + ao->driver = audio_out; + if (audio_out->init(ao, params) >= 0) { + ao->driver = audio_out; + ao->initialized = true; + return; + } + mp_tmsg(MSGT_AO, MSGL_WARN, + "Failed to initialize audio driver '%s'\n", ao_name); + talloc_free_children(ao); + *ao = backup; + } else + mp_tmsg(MSGT_AO, MSGL_WARN, "No such audio driver '%.*s'\n", + ao_len, ao_name); + ++ao_list; + } + return; + + try_defaults: + mp_tmsg(MSGT_AO, MSGL_V, "Trying every known audio driver...\n"); + + // now try the rest... + for (int i = 0; audio_out_drivers[i]; i++) { + const struct ao_driver *audio_out = audio_out_drivers[i]; + ao->driver = audio_out; + ao->probing = true; + if (audio_out->init(ao, NULL) >= 0) { + ao->probing = false; + ao->initialized = true; + ao->driver = audio_out; + return; + } + talloc_free_children(ao); + *ao = backup; + } + return; +} + +void ao_uninit(struct ao *ao, bool cut_audio) +{ + assert(ao->buffer.len >= ao->buffer_playable_size); + ao->buffer.len = ao->buffer_playable_size; + if (ao->initialized) + ao->driver->uninit(ao, cut_audio); + if (!cut_audio && ao->buffer.len) + mp_msg(MSGT_AO, MSGL_WARN, "Audio output truncated at end.\n"); + talloc_free(ao); +} + +int ao_play(struct ao *ao, void *data, int len, int flags) +{ + return ao->driver->play(ao, data, len, flags); +} + +int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) +{ + if (ao->driver->control) + return ao->driver->control(ao, cmd, arg); + return CONTROL_UNKNOWN; +} + +double ao_get_delay(struct ao *ao) +{ + if (!ao->driver->get_delay) { + assert(ao->untimed); + return 0; + } + return ao->driver->get_delay(ao); +} + +int ao_get_space(struct ao *ao) +{ + return ao->driver->get_space(ao); +} + +void ao_reset(struct ao *ao) +{ + ao->buffer.len = 0; + ao->buffer_playable_size = 0; + if (ao->driver->reset) + ao->driver->reset(ao); +} + +void ao_pause(struct ao *ao) +{ + if (ao->driver->pause) + ao->driver->pause(ao); +} + +void ao_resume(struct ao *ao) +{ + if (ao->driver->resume) + ao->driver->resume(ao); +} + + + +int old_ao_init(struct ao *ao, char *params) +{ + assert(!global_ao); + global_ao = ao; + ao_subdevice = params ? talloc_strdup(ao, params) : NULL; + if (ao->driver->old_functions->init(ao->samplerate, ao->channels, + ao->format, 0) == 0) { + global_ao = NULL; + return -1; + } + return 0; +} + +void old_ao_uninit(struct ao *ao, bool cut_audio) +{ + ao->driver->old_functions->uninit(cut_audio); + global_ao = NULL; +} + +int old_ao_play(struct ao *ao, void *data, int len, int flags) +{ + return ao->driver->old_functions->play(data, len, flags); +} + +int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg) +{ + return ao->driver->old_functions->control(cmd, arg); +} + +float old_ao_get_delay(struct ao *ao) +{ + return ao->driver->old_functions->get_delay(); +} + +int old_ao_get_space(struct ao *ao) +{ + return ao->driver->old_functions->get_space(); +} + +void old_ao_reset(struct ao *ao) +{ + ao->driver->old_functions->reset(); +} + +void old_ao_pause(struct ao *ao) +{ + ao->driver->old_functions->pause(); +} + +void old_ao_resume(struct ao *ao) +{ + ao->driver->old_functions->resume(); +} diff --git a/audio/out/ao.h b/audio/out/ao.h new file mode 100644 index 0000000000..9e172fd06c --- /dev/null +++ b/audio/out/ao.h @@ -0,0 +1,140 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AUDIO_OUT_H +#define MPLAYER_AUDIO_OUT_H + +#include <stdbool.h> + +#include "bstr.h" + +#define CONTROL_OK 1 +#define CONTROL_TRUE 1 +#define CONTROL_FALSE 0 +#define CONTROL_UNKNOWN -1 +#define CONTROL_ERROR -2 +#define CONTROL_NA -3 + +enum aocontrol { + // _VOLUME commands take struct ao_control_vol pointer for input/output. + // If there's only one volume, SET should use average of left/right. + AOCONTROL_GET_VOLUME, + AOCONTROL_SET_VOLUME, + // _MUTE commands take a pointer to bool + AOCONTROL_GET_MUTE, + AOCONTROL_SET_MUTE, +}; + +#define AOPLAY_FINAL_CHUNK 1 + +typedef struct ao_control_vol { + float left; + float right; +} ao_control_vol_t; + +typedef struct ao_info { + /* driver name ("Matrox Millennium G200/G400" */ + const char *name; + /* short name (for config strings) ("mga") */ + const char *short_name; + /* author ("Aaron Holtzman <aholtzma@ess.engr.uvic.ca>") */ + const char *author; + /* any additional comments */ + const char *comment; +} ao_info_t; + +/* interface towards mplayer and */ +typedef struct ao_old_functions { + int (*control)(int cmd, void *arg); + int (*init)(int rate, int channels, int format, int flags); + void (*uninit)(int immed); + void (*reset)(void); + int (*get_space)(void); + int (*play)(void *data, int len, int flags); + float (*get_delay)(void); + void (*pause)(void); + void (*resume)(void); +} ao_functions_t; + +struct ao; + +struct ao_driver { + bool is_new; + const struct ao_info *info; + const struct ao_old_functions *old_functions; + int (*control)(struct ao *ao, enum aocontrol cmd, void *arg); + int (*init)(struct ao *ao, char *params); + void (*uninit)(struct ao *ao, bool cut_audio); + void (*reset)(struct ao*ao); + int (*get_space)(struct ao *ao); + int (*play)(struct ao *ao, void *data, int len, int flags); + float (*get_delay)(struct ao *ao); + void (*pause)(struct ao *ao); + void (*resume)(struct ao *ao); +}; + +/* global data used by mplayer and plugins */ +struct ao { + int samplerate; + int channels; + int format; + int bps; + int outburst; + int buffersize; + int brokenpts; + double pts; + struct bstr buffer; + int buffer_playable_size; + bool probing; + bool initialized; + bool untimed; + bool no_persistent_volume; + bool per_application_mixer; + const struct ao_driver *driver; + void *priv; + struct encode_lavc_context *encode_lavc_ctx; + struct MPOpts *opts; + struct input_ctx *input_ctx; +}; + +extern char *ao_subdevice; + +void list_audio_out(void); + +struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input); +void ao_init(struct ao *ao, char **ao_list); +void ao_uninit(struct ao *ao, bool cut_audio); +int ao_play(struct ao *ao, void *data, int len, int flags); +int ao_control(struct ao *ao, enum aocontrol cmd, void *arg); +double ao_get_delay(struct ao *ao); +int ao_get_space(struct ao *ao); +void ao_reset(struct ao *ao); +void ao_pause(struct ao *ao); +void ao_resume(struct ao *ao); + +int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg); +int old_ao_init(struct ao *ao, char *params); +void old_ao_uninit(struct ao *ao, bool cut_audio); +void old_ao_reset(struct ao*ao); +int old_ao_get_space(struct ao *ao); +int old_ao_play(struct ao *ao, void *data, int len, int flags); +float old_ao_get_delay(struct ao *ao); +void old_ao_pause(struct ao *ao); +void old_ao_resume(struct ao *ao); + +#endif /* MPLAYER_AUDIO_OUT_H */ diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c new file mode 100644 index 0000000000..27119112cb --- /dev/null +++ b/audio/out/ao_alsa.c @@ -0,0 +1,868 @@ +/* + * ALSA 0.9.x-1.x audio output driver + * + * Copyright (C) 2004 Alex Beregszaszi + * + * modified for real ALSA 0.9.0 support by Zsolt Barat <joy@streamminister.de> + * additional AC-3 passthrough support by Andy Lo A Foe <andy@alsaplayer.org> + * 08/22/2002 iec958-init rewritten and merged with common init, zsolt + * 04/13/2004 merged with ao_alsa1.x, fixes provided by Jindrich Makovicka + * 04/25/2004 printfs converted to mp_msg, Zsolt. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <errno.h> +#include <sys/time.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <math.h> +#include <string.h> +#include <alloca.h> + +#include "config.h" +#include "subopt-helper.h" +#include "mixer.h" +#include "mp_msg.h" + +#define ALSA_PCM_NEW_HW_PARAMS_API +#define ALSA_PCM_NEW_SW_PARAMS_API + +#include <alsa/asoundlib.h> + +#include "audio_out.h" +#include "audio_out_internal.h" +#include "libaf/format.h" + +static const ao_info_t info = +{ + "ALSA-0.9.x-1.x audio output", + "alsa", + "Alex Beregszaszi, Zsolt Barat <joy@streamminister.de>", + "under development" +}; + +LIBAO_EXTERN(alsa) + +static snd_pcm_t *alsa_handler; +static snd_pcm_format_t alsa_format; + +#define BUFFER_TIME 500000 // 0.5 s +#define FRAGCOUNT 16 + +static size_t bytes_per_sample; + +static int alsa_can_pause; +static snd_pcm_sframes_t prepause_frames; + +#define ALSA_DEVICE_SIZE 256 + +static void alsa_error_handler(const char *file, int line, const char *function, + int err, const char *format, ...) +{ + char tmp[0xc00]; + va_list va; + + va_start(va, format); + vsnprintf(tmp, sizeof tmp, format, va); + va_end(va); + + if (err) + mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s: %s\n", + file, line, function, tmp, snd_strerror(err)); + else + mp_msg(MSGT_AO, MSGL_ERR, "[AO_ALSA] alsa-lib: %s:%i:(%s) %s\n", + file, line, function, tmp); +} + +/* to set/get/query special features/parameters */ +static int control(int cmd, void *arg) +{ + switch(cmd) { + case AOCONTROL_GET_MUTE: + case AOCONTROL_SET_MUTE: + case AOCONTROL_GET_VOLUME: + case AOCONTROL_SET_VOLUME: + { + int err; + snd_mixer_t *handle; + snd_mixer_elem_t *elem; + snd_mixer_selem_id_t *sid; + + char *mix_name = "Master"; + char *card = "default"; + int mix_index = 0; + + long pmin, pmax; + long get_vol, set_vol; + float f_multi; + + if(AF_FORMAT_IS_AC3(ao_data.format) || AF_FORMAT_IS_IEC61937(ao_data.format)) + return CONTROL_TRUE; + + if(mixer_channel) { + char *test_mix_index; + + mix_name = strdup(mixer_channel); + if ((test_mix_index = strchr(mix_name, ','))){ + *test_mix_index = 0; + test_mix_index++; + mix_index = strtol(test_mix_index, &test_mix_index, 0); + + if (*test_mix_index){ + mp_tmsg(MSGT_AO,MSGL_ERR, + "[AO_ALSA] Invalid mixer index. Defaulting to 0.\n"); + mix_index = 0 ; + } + } + } + if(mixer_device) card = mixer_device; + + //allocate simple id + snd_mixer_selem_id_alloca(&sid); + + //sets simple-mixer index and name + snd_mixer_selem_id_set_index(sid, mix_index); + snd_mixer_selem_id_set_name(sid, mix_name); + + if (mixer_channel) { + free(mix_name); + mix_name = NULL; + } + + if ((err = snd_mixer_open(&handle, 0)) < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer open error: %s\n", snd_strerror(err)); + return CONTROL_ERROR; + } + + if ((err = snd_mixer_attach(handle, card)) < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer attach %s error: %s\n", + card, snd_strerror(err)); + snd_mixer_close(handle); + return CONTROL_ERROR; + } + + if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return CONTROL_ERROR; + } + err = snd_mixer_load(handle); + if (err < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Mixer load error: %s\n", snd_strerror(err)); + snd_mixer_close(handle); + return CONTROL_ERROR; + } + + elem = snd_mixer_find_selem(handle, sid); + if (!elem) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Unable to find simple control '%s',%i.\n", + snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid)); + snd_mixer_close(handle); + return CONTROL_ERROR; + } + + snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); + f_multi = (100 / (float)(pmax - pmin)); + + switch (cmd) { + case AOCONTROL_SET_VOLUME: { + ao_control_vol_t *vol = arg; + set_vol = vol->left / f_multi + pmin + 0.5; + + //setting channels + if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting left channel, %s\n", + snd_strerror(err)); + goto mixer_error; + } + mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol); + + set_vol = vol->right / f_multi + pmin + 0.5; + + if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting right channel, %s\n", + snd_strerror(err)); + goto mixer_error; + } + mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n", + set_vol, pmin, pmax, f_multi); + break; + } + case AOCONTROL_GET_VOLUME: { + ao_control_vol_t *vol = arg; + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); + vol->left = (get_vol - pmin) * f_multi; + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); + vol->right = (get_vol - pmin) * f_multi; + mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); + break; + } + case AOCONTROL_SET_MUTE: { + bool *mute = arg; + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_set_playback_switch( + elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute); + } + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, + !*mute); + break; + } + case AOCONTROL_GET_MUTE: { + bool *mute = arg; + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + int tmp = 1; + snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, + &tmp); + *mute = !tmp; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_get_playback_switch( + elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); + *mute &= !tmp; + } + break; + } + } + snd_mixer_close(handle); + return CONTROL_OK; + mixer_error: + snd_mixer_close(handle); + return CONTROL_ERROR; + } + + } //end switch + return CONTROL_UNKNOWN; +} + +static void parse_device (char *dest, const char *src, int len) +{ + char *tmp; + memmove(dest, src, len); + dest[len] = 0; + while ((tmp = strrchr(dest, '.'))) + tmp[0] = ','; + while ((tmp = strrchr(dest, '='))) + tmp[0] = ':'; +} + +static void print_help (void) +{ + mp_tmsg (MSGT_AO, MSGL_FATAL, + "\n[AO_ALSA] -ao alsa commandline help:\n"\ + "[AO_ALSA] Example: mpv -ao alsa:device=hw=0.3\n"\ + "[AO_ALSA] Sets first card fourth hardware device.\n\n"\ + "[AO_ALSA] Options:\n"\ + "[AO_ALSA] noblock\n"\ + "[AO_ALSA] Opens device in non-blocking mode.\n"\ + "[AO_ALSA] device=<device-name>\n"\ + "[AO_ALSA] Sets device (change , to . and : to =)\n"); +} + +static int str_maxlen(void *strp) { + strarg_t *str = strp; + return str->len <= ALSA_DEVICE_SIZE; +} + +static int try_open_device(const char *device, int open_mode, int try_ac3) +{ + int err, len; + char *ac3_device, *args; + + if (try_ac3) { + /* to set the non-audio bit, use AES0=6 */ + len = strlen(device); + ac3_device = malloc(len + 7 + 1); + if (!ac3_device) + return -ENOMEM; + strcpy(ac3_device, device); + args = strchr(ac3_device, ':'); + if (!args) { + /* no existing parameters: add it behind device name */ + strcat(ac3_device, ":AES0=6"); + } else { + do + ++args; + while (isspace(*args)); + if (*args == '\0') { + /* ":" but no parameters */ + strcat(ac3_device, "AES0=6"); + } else if (*args != '{') { + /* a simple list of parameters: add it at the end of the list */ + strcat(ac3_device, ",AES0=6"); + } else { + /* parameters in config syntax: add it inside the { } block */ + do + --len; + while (len > 0 && isspace(ac3_device[len])); + if (ac3_device[len] == '}') + strcpy(ac3_device + len, " AES0=6}"); + } + } + err = snd_pcm_open(&alsa_handler, ac3_device, SND_PCM_STREAM_PLAYBACK, + open_mode); + free(ac3_device); + if (!err) + return 0; + } + return snd_pcm_open(&alsa_handler, device, SND_PCM_STREAM_PLAYBACK, + open_mode); +} + +/* + open & setup audio device + return: 1=success 0=fail +*/ +static int init(int rate_hz, int channels, int format, int flags) +{ + int err; + int block; + strarg_t device; + snd_pcm_uframes_t chunk_size; + snd_pcm_uframes_t bufsize; + snd_pcm_uframes_t boundary; + const opt_t subopts[] = { + {"block", OPT_ARG_BOOL, &block, NULL}, + {"device", OPT_ARG_STR, &device, str_maxlen}, + {NULL} + }; + + char alsa_device[ALSA_DEVICE_SIZE + 1]; + // make sure alsa_device is null-terminated even when using strncpy etc. + memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1); + + mp_msg(MSGT_AO,MSGL_V,"alsa-init: requested format: %d Hz, %d channels, %x\n", rate_hz, + channels, format); + alsa_handler = NULL; + mp_msg(MSGT_AO,MSGL_V,"alsa-init: using ALSA %s\n", snd_asoundlib_version()); + + prepause_frames = 0; + + snd_lib_error_set_handler(alsa_error_handler); + + ao_data.samplerate = rate_hz; + ao_data.format = format; + ao_data.channels = channels; + + switch (format) + { + case AF_FORMAT_S8: + alsa_format = SND_PCM_FORMAT_S8; + break; + case AF_FORMAT_U8: + alsa_format = SND_PCM_FORMAT_U8; + break; + case AF_FORMAT_U16_LE: + alsa_format = SND_PCM_FORMAT_U16_LE; + break; + case AF_FORMAT_U16_BE: + alsa_format = SND_PCM_FORMAT_U16_BE; + break; + case AF_FORMAT_AC3_LE: + case AF_FORMAT_S16_LE: + case AF_FORMAT_IEC61937_LE: + alsa_format = SND_PCM_FORMAT_S16_LE; + break; + case AF_FORMAT_AC3_BE: + case AF_FORMAT_S16_BE: + case AF_FORMAT_IEC61937_BE: + alsa_format = SND_PCM_FORMAT_S16_BE; + break; + case AF_FORMAT_U32_LE: + alsa_format = SND_PCM_FORMAT_U32_LE; + break; + case AF_FORMAT_U32_BE: + alsa_format = SND_PCM_FORMAT_U32_BE; + break; + case AF_FORMAT_S32_LE: + alsa_format = SND_PCM_FORMAT_S32_LE; + break; + case AF_FORMAT_S32_BE: + alsa_format = SND_PCM_FORMAT_S32_BE; + break; + case AF_FORMAT_U24_LE: + alsa_format = SND_PCM_FORMAT_U24_3LE; + break; + case AF_FORMAT_U24_BE: + alsa_format = SND_PCM_FORMAT_U24_3BE; + break; + case AF_FORMAT_S24_LE: + alsa_format = SND_PCM_FORMAT_S24_3LE; + break; + case AF_FORMAT_S24_BE: + alsa_format = SND_PCM_FORMAT_S24_3BE; + break; + case AF_FORMAT_FLOAT_LE: + alsa_format = SND_PCM_FORMAT_FLOAT_LE; + break; + case AF_FORMAT_FLOAT_BE: + alsa_format = SND_PCM_FORMAT_FLOAT_BE; + break; + case AF_FORMAT_MU_LAW: + alsa_format = SND_PCM_FORMAT_MU_LAW; + break; + case AF_FORMAT_A_LAW: + alsa_format = SND_PCM_FORMAT_A_LAW; + break; + + default: + alsa_format = SND_PCM_FORMAT_MPEG; //? default should be -1 + break; + } + + //subdevice parsing + // set defaults + block = 1; + /* switch for spdif + * sets opening sequence for SPDIF + * sets also the playback and other switches 'on the fly' + * while opening the abstract alias for the spdif subdevice + * 'iec958' + */ + if (AF_FORMAT_IS_AC3(format) || AF_FORMAT_IS_IEC61937(format)) { + device.str = "iec958"; + mp_msg(MSGT_AO,MSGL_V,"alsa-spdif-init: playing AC3/iec61937/iec958, %i channels\n", channels); + } + else + /* in any case for multichannel playback we should select + * appropriate device + */ + switch (channels) { + case 1: + case 2: + device.str = "default"; + mp_msg(MSGT_AO,MSGL_V,"alsa-init: setup for 1/2 channel(s)\n"); + break; + case 4: + if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) + // hack - use the converter plugin + device.str = "plug:surround40"; + else + device.str = "surround40"; + mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround40\n"); + break; + case 6: + if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) + device.str = "plug:surround51"; + else + device.str = "surround51"; + mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround51\n"); + break; + case 8: + if (alsa_format == SND_PCM_FORMAT_FLOAT_LE) + device.str = "plug:surround71"; + else + device.str = "surround71"; + mp_msg(MSGT_AO,MSGL_V,"alsa-init: device set to surround71\n"); + break; + default: + device.str = "default"; + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] %d channels are not supported.\n",channels); + } + device.len = strlen(device.str); + if (subopt_parse(ao_subdevice, subopts) != 0) { + print_help(); + return 0; + } + parse_device(alsa_device, device.str, device.len); + + mp_msg(MSGT_AO,MSGL_V,"alsa-init: using device %s\n", alsa_device); + + alsa_can_pause = 1; + + if (!alsa_handler) { + int open_mode = block ? 0 : SND_PCM_NONBLOCK; + int isac3 = AF_FORMAT_IS_AC3(format) || AF_FORMAT_IS_IEC61937(format); + //modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC + if ((err = try_open_device(alsa_device, open_mode, isac3)) < 0) + { + if (err != -EBUSY && !block) { + mp_tmsg(MSGT_AO,MSGL_INFO,"[AO_ALSA] Open in nonblock-mode failed, trying to open in block-mode.\n"); + if ((err = try_open_device(alsa_device, 0, isac3)) < 0) { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err)); + return 0; + } + } else { + mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Playback open error: %s\n", snd_strerror(err)); + return 0; |