X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Falsa.c;h=d82ae18780e2201c280d14aa78a19ede97f5ccfd;hb=b50c7652c71962d64df75ec60a901131e577fdc7;hp=d8b4b45759533f9b1c5e077360212bca9964de59;hpb=78d87996ccb92d1dc91c9987685f976ed3be08a6;p=vlc diff --git a/modules/access/alsa.c b/modules/access/alsa.c index d8b4b45759..d82ae18780 100644 --- a/modules/access/alsa.c +++ b/modules/access/alsa.c @@ -1,670 +1,510 @@ /***************************************************************************** - * alsa.c : Alsa input module for vlc + * alsa.c: ALSA capture module for VLC ***************************************************************************** - * Copyright (C) 2002-2009 the VideoLAN team - * $Id$ + * Copyright (C) 2012 Rémi Denis-Courmont * - * Authors: Benjamin Pracht - * Richard Hosking - * Antoine Cellerier - * Dennis Lou - * - * This program 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/* - * ALSA support based on parts of - * http://www.equalarea.com/paul/alsa-audio.html - * and hints taken from alsa-utils (aplay/arecord) - * http://www.alsa-project.org - */ - -/***************************************************************************** - * Preamble + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #ifdef HAVE_CONFIG_H -# include "config.h" +# include #endif -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API -#include - +#include +#include #include +#include -/***************************************************************************** - * Module descriptior - *****************************************************************************/ - -static int DemuxOpen ( vlc_object_t * ); -static void DemuxClose( vlc_object_t * ); +#include +#include +#include +#include -#define STEREO_TEXT N_( "Stereo" ) -#define STEREO_LONGTEXT N_( \ - "Capture the audio stream in stereo." ) +#define HELP_TEXT N_( \ + "Pass alsa:// to open the default ALSA capture device, " \ + "or alsa://SOURCE to open a specific device named SOURCE.") +#define STEREO_TEXT N_("Stereo") +#define RATE_TEXT N_("Sample rate") -#define SAMPLERATE_TEXT N_( "Samplerate" ) -#define SAMPLERATE_LONGTEXT N_( \ - "Samplerate of the captured audio stream, in Hz (eg: 11025, 22050, 44100, 48000)" ) +static int Open (vlc_object_t *); +static void Close (vlc_object_t *); -#define CACHING_TEXT N_("Caching value in ms") -#define CACHING_LONGTEXT N_( \ - "Caching value for Alsa captures. This " \ - "value should be set in milliseconds." ) +static const int rate_values[] = { 192000, 176400, + 96000, 88200, 48000, 44100, + 32000, 22050, 24000, 16000, + 11025, 8000, 4000 +}; +static const const char *rate_names[] = { N_("192000 Hz"), N_("176400 Hz"), + N_("96000 Hz"), N_("88200 Hz"), N_("48000 Hz"), N_("44100 Hz"), + N_("32000 Hz"), N_("22050 Hz"), N_("24000 Hz"), N_("16000 Hz"), + N_("11025 Hz"), N_("8000 Hz"), N_("4000 Hz") +}; -#define ALSA_DEFAULT "hw" -#define CFG_PREFIX "alsa-" +vlc_module_begin () + set_shortname (N_("ALSA")) + set_description (N_("ALSA audio capture")) + set_capability ("access_demux", 0) + set_category (CAT_INPUT) + set_subcategory (SUBCAT_INPUT_ACCESS) + set_help (HELP_TEXT) + + add_obsolete_string ("alsa-format") /* since 2.1.0 */ + add_bool ("alsa-stereo", true, STEREO_TEXT, STEREO_TEXT, true) + add_integer ("alsa-samplerate", 48000, RATE_TEXT, RATE_TEXT, true) + change_integer_list (rate_values, rate_names) + + add_shortcut ("alsa") + set_callbacks (Open, Close) +vlc_module_end () + +/** Helper for ALSA -> VLC debugging output */ +/** XXX: duplicated from ALSA output */ +static void Dump (vlc_object_t *obj, const char *msg, + int (*cb)(void *, snd_output_t *), void *p) +{ + snd_output_t *output; + char *str; -vlc_module_begin() - set_shortname( N_("Alsa") ) - set_description( N_("Alsa audio capture input") ) - set_category( CAT_INPUT ) - set_subcategory( SUBCAT_INPUT_ACCESS ) + if (unlikely(snd_output_buffer_open (&output))) + return; - add_shortcut( "alsa" ) - set_capability( "access_demux", 10 ) - set_callbacks( DemuxOpen, DemuxClose ) + int val = cb (p, output); + if (val) + { + msg_Warn (obj, "cannot get info: %s", snd_strerror (val)); + return; + } - add_bool( CFG_PREFIX "stereo", true, NULL, STEREO_TEXT, STEREO_LONGTEXT, - true ) - add_integer( CFG_PREFIX "samplerate", 48000, NULL, SAMPLERATE_TEXT, - SAMPLERATE_LONGTEXT, true ) - add_integer( CFG_PREFIX "caching", DEFAULT_PTS_DELAY / 1000, NULL, - CACHING_TEXT, CACHING_LONGTEXT, true ) -vlc_module_end() + size_t len = snd_output_buffer_string (output, &str); + if (len > 0 && str[len - 1]) + len--; /* strip trailing newline */ + msg_Dbg (obj, "%s%.*s", msg, (int)len, str); + snd_output_close (output); +} +#define Dump(o, m, cb, p) \ + Dump(VLC_OBJECT(o), m, (int (*)(void *, snd_output_t *))(cb), p) -/***************************************************************************** - * Access: local prototypes - *****************************************************************************/ +static void DumpDevice (vlc_object_t *obj, snd_pcm_t *pcm) +{ + snd_pcm_info_t *info; -static int DemuxControl( demux_t *, int, va_list ); + Dump (obj, " ", snd_pcm_dump, pcm); + snd_pcm_info_alloca (&info); + if (snd_pcm_info (pcm, info) == 0) + { + msg_Dbg (obj, " device name : %s", snd_pcm_info_get_name (info)); + msg_Dbg (obj, " device ID : %s", snd_pcm_info_get_id (info)); + msg_Dbg (obj, " subdevice name: %s", + snd_pcm_info_get_subdevice_name (info)); + } +} -static int Demux( demux_t * ); +static void DumpDeviceStatus (vlc_object_t *obj, snd_pcm_t *pcm) +{ + snd_pcm_status_t *status; -static block_t* GrabAudio( demux_t *p_demux ); + snd_pcm_status_alloca (&status); + snd_pcm_status (pcm, status); + Dump (obj, "current status:\n", snd_pcm_status_dump, status); +} +#define DumpDeviceStatus(o, p) DumpDeviceStatus(VLC_OBJECT(o), p) -static int OpenAudioDev( demux_t * ); -static bool ProbeAudioDevAlsa( demux_t *, const char *psz_device ); struct demux_sys_t { - const char *psz_device; /* Alsa device from MRL */ - - /* Audio */ - int i_cache; - unsigned int i_sample_rate; - bool b_stereo; - size_t i_max_frame_size; - block_t *p_block; - es_out_id_t *p_es; - - /* ALSA Audio */ - snd_pcm_t *p_alsa_pcm; - size_t i_alsa_frame_size; - int i_alsa_chunk_size; - - int64_t i_next_demux_date; /* Used to handle alsa:// as input-slave properly */ + snd_pcm_t *pcm; + es_out_id_t *es; + vlc_thread_t thread; + + mtime_t start; + mtime_t caching; + snd_pcm_uframes_t period_size; + unsigned rate; }; -static int FindMainDevice( demux_t *p_demux ) +static void Poll (snd_pcm_t *pcm, int canc) { - /* TODO: if using default device, loop through all alsa devices until - * one works. */ - msg_Dbg( p_demux, "opening device '%s'", p_demux->p_sys->psz_device ); - if( ProbeAudioDevAlsa( p_demux, p_demux->p_sys->psz_device ) ) + int n = snd_pcm_poll_descriptors_count (pcm); + struct pollfd ufd[n]; + unsigned short revents; + + snd_pcm_poll_descriptors (pcm, ufd, n); + do { - msg_Dbg( p_demux, "'%s' is an audio device", - p_demux->p_sys->psz_device ); - OpenAudioDev( p_demux ); + vlc_restorecancel (canc); + poll (ufd, n, -1); + canc = vlc_savecancel (); + snd_pcm_poll_descriptors_revents (pcm, ufd, n, &revents); } - - if( p_demux->p_sys->p_alsa_pcm == NULL ) - return VLC_EGENERIC; - return VLC_SUCCESS; + while (!revents); } -static void ListAvailableDevices( demux_t *p_demux ) +static void *Thread (void *data) { - snd_ctl_card_info_t *p_info = NULL; - snd_ctl_card_info_alloca( &p_info ); - - snd_pcm_info_t *p_pcminfo = NULL; - snd_pcm_info_alloca( &p_pcminfo ); + demux_t *demux = data; + demux_sys_t *sys = demux->p_sys; + snd_pcm_t *pcm = sys->pcm; + size_t bytes; + int canc, val; + + canc = vlc_savecancel (); + bytes = snd_pcm_frames_to_bytes (pcm, sys->period_size); + val = snd_pcm_start (pcm); + if (val) + { + msg_Err (demux, "cannot prepare device: %s", snd_strerror (val)); + return NULL; + } - msg_Dbg( p_demux, "Available alsa capture devices:" ); - int i_card = -1; - while( !snd_card_next( &i_card ) && i_card >= 0 ) + for (;;) { - char psz_devname[10]; - snprintf( psz_devname, 10, "hw:%d", i_card ); + block_t *block = block_Alloc (bytes); + if (unlikely(block == NULL)) + break; - snd_ctl_t *p_ctl = NULL; - if( snd_ctl_open( &p_ctl, psz_devname, 0 ) < 0 ) continue; + /* Wait for data */ + Poll (pcm, canc); - snd_ctl_card_info( p_ctl, p_info ); - msg_Dbg( p_demux, " %s (%s)", - snd_ctl_card_info_get_id( p_info ), - snd_ctl_card_info_get_name( p_info ) ); + /* Read data */ + snd_pcm_sframes_t frames, delay; + mtime_t pts; - int i_dev = -1; - while( !snd_ctl_pcm_next_device( p_ctl, &i_dev ) && i_dev >= 0 ) + frames = snd_pcm_readi (pcm, block->p_buffer, sys->period_size); + pts = mdate (); + if (frames < 0) { - snd_pcm_info_set_device( p_pcminfo, i_dev ); - snd_pcm_info_set_subdevice( p_pcminfo, 0 ); - snd_pcm_info_set_stream( p_pcminfo, SND_PCM_STREAM_CAPTURE ); - if( snd_ctl_pcm_info( p_ctl, p_pcminfo ) < 0 ) continue; - - msg_Dbg( p_demux, " hw:%d,%d : %s (%s)", i_card, i_dev, - snd_pcm_info_get_id( p_pcminfo ), - snd_pcm_info_get_name( p_pcminfo ) ); + if (frames == -EAGAIN) + continue; + + val = snd_pcm_recover (pcm, frames, 1); + if (val == 0) + { + msg_Warn (demux, "cannot read samples: %s", + snd_strerror (frames)); + continue; + } + msg_Err (demux, "cannot recover record stream: %s", + snd_strerror (val)); + DumpDeviceStatus (demux, pcm); + break; } - snd_ctl_close( p_ctl ); - } -} + /* Compute time stamp */ + if (snd_pcm_delay (pcm, &delay)) + delay = 0; + delay += frames; + pts -= (CLOCK_FREQ * delay) / sys->rate; -/***************************************************************************** - * DemuxOpen: opens alsa device, access_demux callback - ***************************************************************************** - * - * url: :::: - * - *****************************************************************************/ -static int DemuxOpen( vlc_object_t *p_this ) -{ - demux_t *p_demux = (demux_t*)p_this; - demux_sys_t *p_sys; - - /* Only when selected */ - if( *p_demux->psz_access == '\0' ) return VLC_EGENERIC; - - /* Set up p_demux */ - p_demux->pf_control = DemuxControl; - p_demux->pf_demux = Demux; - p_demux->info.i_update = 0; - p_demux->info.i_title = 0; - p_demux->info.i_seekpoint = 0; - - p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) ); - if( p_sys == NULL ) return VLC_ENOMEM; - - p_sys->i_sample_rate = var_CreateGetInteger( p_demux, CFG_PREFIX "samplerate" ); - p_sys->b_stereo = var_CreateGetBool( p_demux, CFG_PREFIX "stereo" ); - p_sys->i_cache = var_CreateGetInteger( p_demux, CFG_PREFIX "caching" ); - p_sys->p_es = NULL; - p_sys->p_block = NULL; - p_sys->i_next_demux_date = -1; - - if( p_demux->psz_path && *p_demux->psz_path ) - p_sys->psz_device = p_demux->psz_path; - else - { - p_sys->psz_device = ALSA_DEFAULT; - ListAvailableDevices( p_demux ); - } + block->i_buffer = snd_pcm_frames_to_bytes (pcm, frames); + block->i_nb_samples = frames; + block->i_pts = pts; + block->i_length = (CLOCK_FREQ * frames) / sys->rate; - if( FindMainDevice( p_demux ) != VLC_SUCCESS ) - { - DemuxClose( p_this ); - return VLC_EGENERIC; + es_out_Control (demux->out, ES_OUT_SET_PCR, block->i_pts); + es_out_Send (demux->out, sys->es, block); } - - return VLC_SUCCESS; + return NULL; } -/***************************************************************************** - * Close: close device, free resources - *****************************************************************************/ -static void DemuxClose( vlc_object_t *p_this ) +static int Control (demux_t *demux, int query, va_list ap) { - demux_t *p_demux = (demux_t *)p_this; - demux_sys_t *p_sys = p_demux->p_sys; + demux_sys_t *sys = demux->p_sys; - if( p_sys->p_alsa_pcm ) + switch (query) { - snd_pcm_close( p_sys->p_alsa_pcm ); - } - - if( p_sys->p_block ) block_Release( p_sys->p_block ); + case DEMUX_GET_TIME: + *va_arg (ap, int64_t *) = mdate () - sys->start; + break; - free( p_sys ); -} + case DEMUX_GET_PTS_DELAY: + *va_arg (ap, int64_t *) = sys->caching; + break; -/***************************************************************************** - * DemuxControl: - *****************************************************************************/ -static int DemuxControl( demux_t *p_demux, int i_query, va_list args ) -{ - demux_sys_t *p_sys = p_demux->p_sys; - bool *pb; - int64_t *pi64; + //case DEMUX_SET_NEXT_DEMUX_TIME: still needed? - switch( i_query ) - { - /* Special for access_demux */ + case DEMUX_HAS_UNSUPPORTED_META: + case DEMUX_CAN_RECORD: case DEMUX_CAN_PAUSE: - case DEMUX_CAN_SEEK: - case DEMUX_SET_PAUSE_STATE: case DEMUX_CAN_CONTROL_PACE: - pb = (bool*)va_arg( args, bool * ); - *pb = false; - return VLC_SUCCESS; - - case DEMUX_GET_PTS_DELAY: - pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = (int64_t)p_sys->i_cache * 1000; - return VLC_SUCCESS; - - case DEMUX_GET_TIME: - pi64 = (int64_t*)va_arg( args, int64_t * ); - *pi64 = mdate(); - return VLC_SUCCESS; - - case DEMUX_SET_NEXT_DEMUX_TIME: - p_sys->i_next_demux_date = (int64_t)va_arg( args, int64_t ); - return VLC_SUCCESS; + case DEMUX_CAN_CONTROL_RATE: + case DEMUX_CAN_SEEK: + *va_arg (ap, bool *) = false; + break;; - /* TODO implement others */ default: return VLC_EGENERIC; } - return VLC_EGENERIC; + return VLC_SUCCESS; } -/***************************************************************************** - * Demux: Processes the audio frame - *****************************************************************************/ -static int Demux( demux_t *p_demux ) -{ - demux_sys_t *p_sys = p_demux->p_sys; - - block_t *p_block = NULL; - - do - { - if( p_block ) - { - es_out_Send( p_demux->out, p_sys->p_es, p_block ); - p_block = NULL; - } - - /* Wait for data */ - int i_wait = snd_pcm_wait( p_sys->p_alsa_pcm, 500 ); - switch( i_wait ) - { - case 1: - { - p_block = GrabAudio( p_demux ); - if( p_block ) - es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts ); - } - - /* FIXME: this is a copy paste from below. Shouldn't be needed - * twice. */ - case -EPIPE: - /* xrun */ - snd_pcm_prepare( p_sys->p_alsa_pcm ); - break; - case -ESTRPIPE: - { - /* suspend */ - int i_resume = snd_pcm_resume( p_sys->p_alsa_pcm ); - if( i_resume < 0 && i_resume != -EAGAIN ) snd_pcm_prepare( p_sys->p_alsa_pcm ); - break; - } - /* */ - } - } while( p_block && p_sys->i_next_demux_date > 0 && - p_block->i_pts < p_sys->i_next_demux_date ); +static const vlc_fourcc_t formats[] = { + [SND_PCM_FORMAT_S8] = VLC_CODEC_S8, + [SND_PCM_FORMAT_U8] = VLC_CODEC_U8, + [SND_PCM_FORMAT_S16_LE] = VLC_CODEC_S16L, + [SND_PCM_FORMAT_S16_BE] = VLC_CODEC_S16B, + [SND_PCM_FORMAT_U16_LE] = VLC_CODEC_U16L, + [SND_PCM_FORMAT_U16_BE] = VLC_CODEC_U16B, + [SND_PCM_FORMAT_S24_LE] = VLC_CODEC_S24L32, + [SND_PCM_FORMAT_S24_BE] = VLC_CODEC_S24B32, + [SND_PCM_FORMAT_U24_LE] = VLC_CODEC_U32L, // TODO: replay gain + [SND_PCM_FORMAT_U24_BE] = VLC_CODEC_U32B, // ^ + [SND_PCM_FORMAT_S32_LE] = VLC_CODEC_S32L, + [SND_PCM_FORMAT_S32_BE] = VLC_CODEC_S32B, + [SND_PCM_FORMAT_U32_LE] = VLC_CODEC_U32L, + [SND_PCM_FORMAT_U32_BE] = VLC_CODEC_U32B, + [SND_PCM_FORMAT_FLOAT_LE] = VLC_CODEC_F32L, + [SND_PCM_FORMAT_FLOAT_BE] = VLC_CODEC_F32B, + [SND_PCM_FORMAT_FLOAT64_LE] = VLC_CODEC_F32L, + [SND_PCM_FORMAT_FLOAT64_BE] = VLC_CODEC_F32B, + //[SND_PCM_FORMAT_IEC958_SUBFRAME_LE] = VLC_CODEC_SPDIFL, + //[SND_PCM_FORMAT_IEC958_SUBFRAME_BE] = VLC_CODEC_SPDIFB, + [SND_PCM_FORMAT_MU_LAW] = VLC_CODEC_MULAW, + [SND_PCM_FORMAT_A_LAW] = VLC_CODEC_ALAW, + //[SND_PCM_FORMAT_IMA_ADPCM] = VLC_CODEC_ADPCM_?, // XXX: which one? + [SND_PCM_FORMAT_MPEG] = VLC_CODEC_MPGA, + [SND_PCM_FORMAT_GSM] = VLC_CODEC_GSM, + //[SND_PCM_FORMAT_SPECIAL] = VLC_CODEC_? + [SND_PCM_FORMAT_S24_3LE] = VLC_CODEC_S24L, + [SND_PCM_FORMAT_S24_3BE] = VLC_CODEC_S24B, + [SND_PCM_FORMAT_U24_3LE] = VLC_CODEC_U24L, + [SND_PCM_FORMAT_U24_3BE] = VLC_CODEC_U24B, + [SND_PCM_FORMAT_S20_3LE] = VLC_CODEC_S24L, // TODO: replay gain + [SND_PCM_FORMAT_S20_3BE] = VLC_CODEC_S24B, // ^ + [SND_PCM_FORMAT_U20_3LE] = VLC_CODEC_U24L, // ^ + [SND_PCM_FORMAT_U20_3BE] = VLC_CODEC_U24B, // ^ + [SND_PCM_FORMAT_S18_3LE] = VLC_CODEC_S24L, // ^ + [SND_PCM_FORMAT_S18_3BE] = VLC_CODEC_S24B, // ^ + [SND_PCM_FORMAT_U18_3LE] = VLC_CODEC_U24L, // ^ + [SND_PCM_FORMAT_U18_3BE] = VLC_CODEC_U24B, // ^ +}; - if( p_block ) - es_out_Send( p_demux->out, p_sys->p_es, p_block ); +#ifdef WORDS_BIGENDIAN +# define C(f) f##BE, f##LE +#else +# define C(f) f##LE, f##BE +#endif - return 1; -} +/* Formats in order of decreasing preference */ +static const uint8_t choices[] = { + C(SND_PCM_FORMAT_FLOAT_), + C(SND_PCM_FORMAT_S32_), + C(SND_PCM_FORMAT_U32_), + C(SND_PCM_FORMAT_S16_), + C(SND_PCM_FORMAT_U16_), + C(SND_PCM_FORMAT_FLOAT64_), + C(SND_PCM_FORMAT_S24_3), + C(SND_PCM_FORMAT_U24_3), + SND_PCM_FORMAT_MPEG, + SND_PCM_FORMAT_GSM, + SND_PCM_FORMAT_MU_LAW, + SND_PCM_FORMAT_A_LAW, + SND_PCM_FORMAT_S8, + SND_PCM_FORMAT_U8, +}; +static uint16_t channel_maps[] = { + AOUT_CHAN_CENTER, AOUT_CHANS_2_0, AOUT_CHANS_3_0 /* ? */, + AOUT_CHANS_4_0, AOUT_CHANS_5_0 /* ? */, AOUT_CHANS_5_1, + /* TODO: support 7-8 channels - need channels reodering */ +}; -/***************************************************************************** - * GrabAudio: Grab an audio frame - *****************************************************************************/ -static block_t* GrabAudio( demux_t *p_demux ) +static int Open (vlc_object_t *obj) { - demux_sys_t *p_sys = p_demux->p_sys; - int i_read, i_correct; - block_t *p_block; - - if( p_sys->p_block ) p_block = p_sys->p_block; - else p_block = block_New( p_demux, p_sys->i_max_frame_size ); - - if( !p_block ) - { - msg_Warn( p_demux, "cannot get block" ); - return 0; - } - - p_sys->p_block = p_block; - - /* ALSA */ - i_read = snd_pcm_readi( p_sys->p_alsa_pcm, p_block->p_buffer, p_sys->i_alsa_chunk_size ); - if( i_read <= 0 ) - { - int i_resume; - switch( i_read ) - { - case -EAGAIN: - break; - case -EPIPE: - /* xrun */ - snd_pcm_prepare( p_sys->p_alsa_pcm ); - break; - case -ESTRPIPE: - /* suspend */ - i_resume = snd_pcm_resume( p_sys->p_alsa_pcm ); - if( i_resume < 0 && i_resume != -EAGAIN ) snd_pcm_prepare( p_sys->p_alsa_pcm ); - break; - default: - msg_Err( p_demux, "Failed to read alsa frame (%s)", snd_strerror( i_read ) ); - return 0; - } - } - else - { - /* convert from frames to bytes */ - i_read *= p_sys->i_alsa_frame_size; - } - - if( i_read <= 0 ) return 0; - - p_block->i_buffer = i_read; - p_sys->p_block = 0; - - /* Correct the date because of kernel buffering */ - i_correct = i_read; - /* ALSA */ - int i_err; - snd_pcm_sframes_t delay = 0; - if( ( i_err = snd_pcm_delay( p_sys->p_alsa_pcm, &delay ) ) >= 0 ) + demux_t *demux = (demux_t *)obj; + demux_sys_t *sys = malloc (sizeof (*sys)); + + if (unlikely(sys == NULL)) + return VLC_ENOMEM; + + /* Open the device */ + const char *device = demux->psz_location; + if (device == NULL || !device[0]) + device = "default"; + + const int mode = SND_PCM_NONBLOCK + /*| SND_PCM_NO_AUTO_RESAMPLE*/ + | SND_PCM_NO_AUTO_CHANNELS + /*| SND_PCM_NO_AUTO_FORMAT*/; + snd_pcm_t *pcm; + int val = snd_pcm_open (&pcm, device, SND_PCM_STREAM_CAPTURE, mode); + if (val != 0) { - size_t i_correction_delta = delay * p_sys->i_alsa_frame_size; - /* Test for overrun */ - if( i_correction_delta > p_sys->i_max_frame_size ) - { - msg_Warn( p_demux, "ALSA read overrun (%zu > %zu)", - i_correction_delta, p_sys->i_max_frame_size ); - i_correction_delta = p_sys->i_max_frame_size; - snd_pcm_prepare( p_sys->p_alsa_pcm ); - } - i_correct += i_correction_delta; - } - else - { - /* delay failed so reset */ - msg_Warn( p_demux, "ALSA snd_pcm_delay failed (%s)", snd_strerror( i_err ) ); - snd_pcm_prepare( p_sys->p_alsa_pcm ); + msg_Err (demux, "cannot open ALSA device \"%s\": %s", device, + snd_strerror (val)); + free (sys); + return VLC_EGENERIC; } + sys->pcm = pcm; + msg_Dbg (demux, "using ALSA device: %s", device); + DumpDevice (VLC_OBJECT(demux), pcm); - /* Timestamp */ - p_block->i_pts = p_block->i_dts = - mdate() - INT64_C(1000000) * (mtime_t)i_correct / - 2 / ( p_sys->b_stereo ? 2 : 1) / p_sys->i_sample_rate; + /* Negotiate capture parameters */ + snd_pcm_hw_params_t *hw; + es_format_t fmt; + unsigned param; + int dir; - return p_block; -} + snd_pcm_hw_params_alloca (&hw); + snd_pcm_hw_params_any (pcm, hw); + Dump (demux, "initial hardware setup:\n", snd_pcm_hw_params_dump, hw); -/***************************************************************************** - * OpenAudioDev: open and set up the audio device and probe for capabilities - *****************************************************************************/ -static int OpenAudioDevAlsa( demux_t *p_demux ) -{ - demux_sys_t *p_sys = p_demux->p_sys; - const char *psz_device = p_sys->psz_device; - p_sys->p_alsa_pcm = NULL; - snd_pcm_hw_params_t *p_hw_params = NULL; - snd_pcm_uframes_t buffer_size; - snd_pcm_uframes_t chunk_size; - - /* ALSA */ - int i_err; - - if( ( i_err = snd_pcm_open( &p_sys->p_alsa_pcm, psz_device, - SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK ) ) < 0) + val = snd_pcm_hw_params_set_rate_resample (pcm, hw, 0); + if (val) { - msg_Err( p_demux, "Cannot open ALSA audio device %s (%s)", - psz_device, snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot disable resampling: %s", snd_strerror (val)); + goto error; } - if( ( i_err = snd_pcm_nonblock( p_sys->p_alsa_pcm, 1 ) ) < 0) + val = snd_pcm_hw_params_set_access (pcm, hw, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (val) { - msg_Err( p_demux, "Cannot set ALSA nonblock (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot set access mode: %s", snd_strerror (val)); + goto error; } - /* Begin setting hardware parameters */ - - if( ( i_err = snd_pcm_hw_params_malloc( &p_hw_params ) ) < 0 ) - { - msg_Err( p_demux, - "ALSA: cannot allocate hardware parameter structure (%s)", - snd_strerror( i_err ) ); - goto adev_fail; - } + snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; + for (size_t i = 0; i < sizeof (choices) / sizeof (choices[0]); i++) + if (snd_pcm_hw_params_test_format (pcm, hw, choices[i]) == 0) + { + val = snd_pcm_hw_params_set_format (pcm, hw, choices[i]); + if (val) + { + msg_Err (demux, "cannot set sample format: %s", + snd_strerror (val)); + goto error; + } + format = choices[i]; + break; + } - if( ( i_err = snd_pcm_hw_params_any( p_sys->p_alsa_pcm, p_hw_params ) ) < 0 ) + if (format == SND_PCM_FORMAT_UNKNOWN) { - msg_Err( p_demux, - "ALSA: cannot initialize hardware parameter structure (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "no supported sample format"); + goto error; } - /* Set Interleaved access */ - if( ( i_err = snd_pcm_hw_params_set_access( p_sys->p_alsa_pcm, p_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) - { - msg_Err( p_demux, "ALSA: cannot set access type (%s)", - snd_strerror( i_err ) ); - goto adev_fail; - } + assert ((size_t)format < (sizeof (formats) / sizeof (formats[0]))); + es_format_Init (&fmt, AUDIO_ES, formats[format]); + fmt.audio.i_format = fmt.i_codec; - /* Set 16 bit little endian */ - if( ( i_err = snd_pcm_hw_params_set_format( p_sys->p_alsa_pcm, p_hw_params, SND_PCM_FORMAT_S16_LE ) ) < 0 ) + param = 1 + var_InheritBool (demux, "alsa-stereo"); + val = snd_pcm_hw_params_set_channels_max (pcm, hw, ¶m); + if (val) { - msg_Err( p_demux, "ALSA: cannot set sample format (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot restrict channels count: %s", + snd_strerror (val)); + goto error; } - - /* Set sample rate */ -#ifdef HAVE_ALSA_NEW_API - i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, &p_sys->i_sample_rate, NULL ); -#else - i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, p_sys->i_sample_rate, NULL ); -#endif - if( i_err < 0 ) + val = snd_pcm_hw_params_set_channels_last (pcm, hw, ¶m); + if (val) { - msg_Err( p_demux, "ALSA: cannot set sample rate (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot set channels count: %s", snd_strerror (val)); + goto error; } - - /* Set channels */ - unsigned int channels = p_sys->b_stereo ? 2 : 1; - if( ( i_err = snd_pcm_hw_params_set_channels( p_sys->p_alsa_pcm, p_hw_params, channels ) ) < 0 ) + assert (param > 0); + assert (param < (sizeof (channel_maps) / sizeof (channel_maps[0]))); + fmt.audio.i_channels = param; + fmt.audio.i_original_channels = + fmt.audio.i_physical_channels = channel_maps[param - 1]; + + param = var_InheritInteger (demux, "alsa-samplerate"); + val = snd_pcm_hw_params_set_rate_max (pcm, hw, ¶m, NULL); + if (val) { - channels = ( channels==1 ) ? 2 : 1; - msg_Warn( p_demux, "ALSA: cannot set channel count (%s). " - "Trying with channels=%d", - snd_strerror( i_err ), - channels ); - if( ( i_err = snd_pcm_hw_params_set_channels( p_sys->p_alsa_pcm, p_hw_params, channels ) ) < 0 ) - { - msg_Err( p_demux, "ALSA: cannot set channel count (%s)", - snd_strerror( i_err ) ); - goto adev_fail; - } - p_sys->b_stereo = ( channels == 2 ); + msg_Err (demux, "cannot restrict rate to %u Hz or less: %s", 192000, + snd_strerror (val)); + goto error; } - - /* Set metrics for buffer calculations later */ - unsigned int buffer_time; - if( ( i_err = snd_pcm_hw_params_get_buffer_time_max(p_hw_params, &buffer_time, 0) ) < 0 ) + val = snd_pcm_hw_params_set_rate_last (pcm, hw, ¶m, &dir); + if (val) { - msg_Err( p_demux, "ALSA: cannot get buffer time max (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot set sample rate: %s", snd_strerror (val)); + goto error; } - if( buffer_time > 500000 ) buffer_time = 500000; - - /* Set period time */ - unsigned int period_time = buffer_time / 4; -#ifdef HAVE_ALSA_NEW_API - i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, &period_time, 0 ); -#else - i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, period_time, 0 ); -#endif - if( i_err < 0 ) + if (dir) + msg_Warn (demux, "sample rate is not integral"); + fmt.audio.i_rate = param; + sys->rate = param; + + sys->start = mdate (); + sys->caching = INT64_C(1000) * var_InheritInteger (demux, "live-caching"); + param = sys->caching; + val = snd_pcm_hw_params_set_buffer_time_near (pcm, hw, ¶m, NULL); + if (val) { - msg_Err( p_demux, "ALSA: cannot set period time (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot set buffer duration: %s", snd_strerror (val)); + goto error; } - /* Set buffer time */ -#ifdef HAVE_ALSA_NEW_API - i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, &buffer_time, 0 ); -#else - i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, buffer_time, 0 ); -#endif - if( i_err < 0 ) + param /= 4; + val = snd_pcm_hw_params_set_period_time_near (pcm, hw, ¶m, NULL); + if (val) { - msg_Err( p_demux, "ALSA: cannot set buffer time (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot set period: %s", snd_strerror (val)); + goto error; } - /* Apply new hardware parameters */ - if( ( i_err = snd_pcm_hw_params( p_sys->p_alsa_pcm, p_hw_params ) ) < 0 ) + val = snd_pcm_hw_params_get_period_size (hw, &sys->period_size, &dir); + if (val) { - msg_Err( p_demux, "ALSA: cannot set hw parameters (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + msg_Err (demux, "cannot get period size: %s", snd_strerror (val)); + goto error; } + if (dir > 0) + sys->period_size++; - /* Get various buffer metrics */ - snd_pcm_hw_params_get_period_size( p_hw_params, &chunk_size, 0 ); - snd_pcm_hw_params_get_buffer_size( p_hw_params, &buffer_size ); - if( chunk_size == buffer_size ) + /* Commit hardware parameters */ + val = snd_pcm_hw_params (pcm, hw); + if (val) { - msg_Err( p_demux, - "ALSA: period cannot equal buffer size (%lu == %lu)", - chunk_size, buffer_size); - goto adev_fail; + msg_Err (demux, "cannot commit hardware parameters: %s", + snd_strerror (val)); + goto error; } + Dump (demux, "final HW setup:\n", snd_pcm_hw_params_dump, hw); - int bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE); - int bits_per_frame = bits_per_sample * channels; - - p_sys->i_alsa_chunk_size = chunk_size; - p_sys->i_alsa_frame_size = bits_per_frame / 8; - p_sys->i_max_frame_size = chunk_size * bits_per_frame / 8; - - snd_pcm_hw_params_free( p_hw_params ); - p_hw_params = NULL; + /* Kick recording */ + aout_FormatPrepare (&fmt.audio); + sys->es = es_out_Add (demux->out, &fmt); - /* Prep device */ - if( ( i_err = snd_pcm_prepare( p_sys->p_alsa_pcm ) ) < 0 ) + if (vlc_clone (&sys->thread, Thread, demux, VLC_THREAD_PRIORITY_INPUT)) { - msg_Err( p_demux, - "ALSA: cannot prepare audio interface for use (%s)", - snd_strerror( i_err ) ); - goto adev_fail; + es_out_Del (demux->out, sys->es); + goto error; } - snd_pcm_start( p_sys->p_alsa_pcm ); - + demux->p_sys = sys; + demux->pf_demux = NULL; + demux->pf_control = Control; return VLC_SUCCESS; - - adev_fail: - - if( p_hw_params ) snd_pcm_hw_params_free( p_hw_params ); - if( p_sys->p_alsa_pcm ) snd_pcm_close( p_sys->p_alsa_pcm ); - p_sys->p_alsa_pcm = NULL; - +error: + snd_pcm_close (pcm); + free (sys); return VLC_EGENERIC; - -} - -static int OpenAudioDev( demux_t *p_demux ) -{ - demux_sys_t *p_sys = p_demux->p_sys; - if( OpenAudioDevAlsa( p_demux ) != VLC_SUCCESS ) - return VLC_EGENERIC; - - msg_Dbg( p_demux, "opened adev=`%s' %s %dHz", - p_sys->psz_device, p_sys->b_stereo ? "stereo" : "mono", - p_sys->i_sample_rate ); - - es_format_t fmt; - es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC('a','r','a','w') ); - - fmt.audio.i_channels = p_sys->b_stereo ? 2 : 1; - fmt.audio.i_rate = p_sys->i_sample_rate; - fmt.audio.i_bitspersample = 16; - fmt.audio.i_blockalign = fmt.audio.i_channels * fmt.audio.i_bitspersample / 8; - fmt.i_bitrate = fmt.audio.i_channels * fmt.audio.i_rate * fmt.audio.i_bitspersample; - - msg_Dbg( p_demux, "new audio es %d channels %dHz", - fmt.audio.i_channels, fmt.audio.i_rate ); - - p_sys->p_es = es_out_Add( p_demux->out, &fmt ); - - return VLC_SUCCESS; } -/***************************************************************************** - * ProbeAudioDevAlsa: probe audio for capabilities - *****************************************************************************/ -static bool ProbeAudioDevAlsa( demux_t *p_demux, const char *psz_device ) +static void Close (vlc_object_t *obj) { - int i_err; - snd_pcm_t *p_alsa_pcm; - - if( ( i_err = snd_pcm_open( &p_alsa_pcm, psz_device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK ) ) < 0 ) - { - msg_Err( p_demux, "cannot open device %s for ALSA audio (%s)", psz_device, snd_strerror( i_err ) ); - return false; - } + demux_t *demux = (demux_t *)obj; + demux_sys_t *sys = demux->p_sys; - snd_pcm_close( p_alsa_pcm ); + vlc_cancel (sys->thread); + vlc_join (sys->thread, NULL); - return true; + snd_pcm_close (sys->pcm); + free (sys); }