Page MenuHomePhabricator

No OneTemporary

diff --git a/deps/pjsip/pjmedia/include/pjmedia-audiodev/audiodev.h b/deps/pjsip/pjmedia/include/pjmedia-audiodev/audiodev.h
index 4f1e9ee3..4d0d5afd 100644
--- a/deps/pjsip/pjmedia/include/pjmedia-audiodev/audiodev.h
+++ b/deps/pjsip/pjmedia/include/pjmedia-audiodev/audiodev.h
@@ -1,760 +1,760 @@
/* $Id: audiodev.h 4243 2012-08-31 11:42:17Z nanang $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* 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
* (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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __PJMEDIA_AUDIODEV_AUDIODEV_H__
#define __PJMEDIA_AUDIODEV_AUDIODEV_H__
/**
* @file audiodev.h
* @brief Audio device API.
*/
#include <pjmedia-audiodev/config.h>
#include <pjmedia-audiodev/errno.h>
#include <pjmedia/format.h>
#include <pjmedia/frame.h>
#include <pjmedia/types.h>
#include <pj/os.h>
#include <pj/pool.h>
PJ_BEGIN_DECL
/**
* @defgroup s2_audio_device_reference Audio Device API Reference
* @ingroup audio_device_api
* @brief API Reference
* @{
*/
/**
* Type for device index.
*/
typedef pj_int32_t pjmedia_aud_dev_index;
/**
* Device index constants.
*/
enum
{
/**
* Constant to denote default capture device
*/
PJMEDIA_AUD_DEFAULT_CAPTURE_DEV = -1,
/**
* Constant to denote default playback device
*/
PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV = -2,
/**
* Constant to denote invalid device index.
*/
PJMEDIA_AUD_INVALID_DEV = -3
};
/**
* This enumeration identifies various audio device capabilities. These audio
* capabilities indicates what features are supported by the underlying
* audio device implementation.
*
* Applications get these capabilities in the #pjmedia_aud_dev_info structure.
*
* Application can also set the specific features/capabilities when opening
* the audio stream by setting the \a flags member of #pjmedia_aud_param
* structure.
*
* Once audio stream is running, application can also retrieve or set some
* specific audio capability, by using #pjmedia_aud_stream_get_cap() and
* #pjmedia_aud_stream_set_cap() and specifying the desired capability. The
* value of the capability is specified as pointer, and application needs to
* supply the pointer with the correct value, according to the documentation
* of each of the capability.
*/
typedef enum pjmedia_aud_dev_cap
{
/**
* Support for audio formats other than PCM. The value of this capability
* is represented by #pjmedia_format structure.
*/
PJMEDIA_AUD_DEV_CAP_EXT_FORMAT = 1,
/**
* Support for audio input latency control or query. The value of this
* capability is an unsigned integer containing milliseconds value of
* the latency.
*/
PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY = 2,
/**
* Support for audio output latency control or query. The value of this
* capability is an unsigned integer containing milliseconds value of
* the latency.
*/
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY = 4,
/**
* Support for setting/retrieving the audio input device volume level.
* The value of this capability is an unsigned integer representing
* the input audio volume setting in percent.
*/
PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING = 8,
/**
* Support for setting/retrieving the audio output device volume level.
* The value of this capability is an unsigned integer representing
* the output audio volume setting in percent.
*/
PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING = 16,
/**
* Support for monitoring the current audio input signal volume.
* The value of this capability is an unsigned integer representing
* the audio volume in percent.
*/
PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32,
/**
* Support for monitoring the current audio output signal volume.
* The value of this capability is an unsigned integer representing
* the audio volume in percent.
*/
PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64,
/**
* Support for audio input routing. The value of this capability is an
* integer containing #pjmedia_aud_dev_route enumeration.
*/
PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE = 128,
/**
* Support for audio output routing (e.g. loudspeaker vs earpiece). The
* value of this capability is an integer containing #pjmedia_aud_dev_route
* enumeration.
*/
PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE = 256,
/**
* The audio device has echo cancellation feature. The value of this
* capability is a pj_bool_t containing boolean PJ_TRUE or PJ_FALSE.
*/
PJMEDIA_AUD_DEV_CAP_EC = 512,
/**
* The audio device supports setting echo cancellation fail length. The
* value of this capability is an unsigned integer representing the
* echo tail in milliseconds.
*/
PJMEDIA_AUD_DEV_CAP_EC_TAIL = 1024,
/**
* The audio device has voice activity detection feature. The value
* of this capability is a pj_bool_t containing boolean PJ_TRUE or
* PJ_FALSE.
*/
PJMEDIA_AUD_DEV_CAP_VAD = 2048,
/**
* The audio device has comfort noise generation feature. The value
* of this capability is a pj_bool_t containing boolean PJ_TRUE or
* PJ_FALSE.
*/
PJMEDIA_AUD_DEV_CAP_CNG = 4096,
/**
* The audio device has packet loss concealment feature. The value
* of this capability is a pj_bool_t containing boolean PJ_TRUE or
* PJ_FALSE.
*/
PJMEDIA_AUD_DEV_CAP_PLC = 8192,
/**
* End of capability
*/
PJMEDIA_AUD_DEV_CAP_MAX = 16384
} pjmedia_aud_dev_cap;
/**
* This enumeration describes audio routing setting.
*/
typedef enum pjmedia_aud_dev_route
{
/** Default route. */
PJMEDIA_AUD_DEV_ROUTE_DEFAULT = 0,
/** Route to loudspeaker */
PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER = 1,
/** Route to earpiece */
PJMEDIA_AUD_DEV_ROUTE_EARPIECE = 2,
/** Route to paired Bluetooth device */
PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH = 4
} pjmedia_aud_dev_route;
/**
* Device information structure returned by #pjmedia_aud_dev_get_info().
*/
typedef struct pjmedia_aud_dev_info
{
/**
* The device name
*/
- char name[64];
+ char name[128];
/**
* Maximum number of input channels supported by this device. If the
* value is zero, the device does not support input operation (i.e.
* it is a playback only device).
*/
unsigned input_count;
/**
* Maximum number of output channels supported by this device. If the
* value is zero, the device does not support output operation (i.e.
* it is an input only device).
*/
unsigned output_count;
/**
* Default sampling rate.
*/
unsigned default_samples_per_sec;
/**
* The underlying driver name
*/
char driver[32];
/**
* Device capabilities, as bitmask combination of #pjmedia_aud_dev_cap.
*/
unsigned caps;
/**
* Supported audio device routes, as bitmask combination of
* #pjmedia_aud_dev_route. The value may be zero if the device
* does not support audio routing.
*/
unsigned routes;
/**
* Number of audio formats supported by this device. The value may be
* zero if the device does not support non-PCM format.
*/
unsigned ext_fmt_cnt;
/**
* Array of supported extended audio formats
*/
pjmedia_format ext_fmt[8];
} pjmedia_aud_dev_info;
/**
* This callback is called by player stream when it needs additional data
* to be played by the device. Application must fill in the whole of output
* buffer with audio samples.
*
* The frame argument contains the following values:
* - timestamp Playback timestamp, in samples.
* - buf Buffer to be filled out by application.
* - size The size requested in bytes, which will be equal to
* the size of one whole packet.
*
* @param user_data User data associated with the stream.
* @param frame Audio frame, which buffer is to be filled in by
* the application.
*
* @return Returning non-PJ_SUCCESS will cause the audio stream
* to stop
*/
typedef pj_status_t (*pjmedia_aud_play_cb)(void *user_data,
pjmedia_frame *frame);
/**
* This callback is called by recorder stream when it has captured the whole
* packet worth of audio samples.
*
* @param user_data User data associated with the stream.
* @param frame Captured frame.
*
* @return Returning non-PJ_SUCCESS will cause the audio stream
* to stop
*/
typedef pj_status_t (*pjmedia_aud_rec_cb)(void *user_data,
pjmedia_frame *frame);
/**
* This structure specifies the parameters to open the audio stream.
*/
typedef struct pjmedia_aud_param
{
/**
* The audio direction. This setting is mandatory.
*/
pjmedia_dir dir;
/**
* The audio recorder device ID. This setting is mandatory if the audio
* direction includes input/capture direction.
*/
pjmedia_aud_dev_index rec_id;
/**
* The audio playback device ID. This setting is mandatory if the audio
* direction includes output/playback direction.
*/
pjmedia_aud_dev_index play_id;
/**
* Clock rate/sampling rate. This setting is mandatory.
*/
unsigned clock_rate;
/**
* Number of channels. This setting is mandatory.
*/
unsigned channel_count;
/**
* Number of samples per frame. This setting is mandatory.
*/
unsigned samples_per_frame;
/**
* Number of bits per sample. This setting is mandatory.
*/
unsigned bits_per_sample;
/**
* This flags specifies which of the optional settings are valid in this
* structure. The flags is bitmask combination of pjmedia_aud_dev_cap.
*/
unsigned flags;
/**
* Set the audio format. This setting is optional, and will only be used
* if PJMEDIA_AUD_DEV_CAP_EXT_FORMAT is set in the flags.
*/
pjmedia_format ext_fmt;
/**
* Input latency, in milliseconds. This setting is optional, and will
* only be used if PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY is set in the flags.
*/
unsigned input_latency_ms;
/**
* Input latency, in milliseconds. This setting is optional, and will
* only be used if PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY is set in the flags.
*/
unsigned output_latency_ms;
/**
* Input volume setting, in percent. This setting is optional, and will
* only be used if PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING is set in
* the flags.
*/
unsigned input_vol;
/**
* Output volume setting, in percent. This setting is optional, and will
* only be used if PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING is set in
* the flags.
*/
unsigned output_vol;
/**
* Set the audio input route. This setting is optional, and will only be
* used if PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE is set in the flags.
*/
pjmedia_aud_dev_route input_route;
/**
* Set the audio output route. This setting is optional, and will only be
* used if PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE is set in the flags.
*/
pjmedia_aud_dev_route output_route;
/**
* Enable/disable echo canceller, if the device supports it. This setting
* is optional, and will only be used if PJMEDIA_AUD_DEV_CAP_EC is set in
* the flags.
*/
pj_bool_t ec_enabled;
/**
* Set echo canceller tail length in milliseconds, if the device supports
* it. This setting is optional, and will only be used if
* PJMEDIA_AUD_DEV_CAP_EC_TAIL is set in the flags.
*/
unsigned ec_tail_ms;
/**
* Enable/disable PLC. This setting is optional, and will only be used
* if PJMEDIA_AUD_DEV_CAP_PLC is set in the flags.
*/
pj_bool_t plc_enabled;
/**
* Enable/disable CNG. This setting is optional, and will only be used
* if PJMEDIA_AUD_DEV_CAP_CNG is set in the flags.
*/
pj_bool_t cng_enabled;
/**
* Enable/disable VAD. This setting is optional, and will only be used
* if PJMEDIA_AUD_DEV_CAP_VAD is set in the flags.
*/
pj_bool_t vad_enabled;
} pjmedia_aud_param;
typedef enum pjmedia_aud_dev_event {
PJMEDIA_AUD_DEV_DEFAULT_INPUT_CHANGED,
PJMEDIA_AUD_DEV_DEFAULT_OUTPUT_CHANGED,
PJMEDIA_AUD_DEV_LIST_WILL_REFRESH,
PJMEDIA_AUD_DEV_LIST_DID_REFRESH
} pjmedia_aud_dev_event;
typedef void (*pjmedia_aud_dev_observer_callback)(pjmedia_aud_dev_event event);
/**
* This structure specifies the parameters to set an audio device observer
*/
typedef struct pjmedia_aud_dev_observer {
pjmedia_aud_dev_observer_callback cb;
pj_pool_t *pool;
pj_mutex_t *lock;
pj_thread_t *thread;
pj_thread_desc thread_desc;
} pjmedia_aud_dev_observer;
/** Forward declaration for pjmedia_aud_stream */
typedef struct pjmedia_aud_stream pjmedia_aud_stream;
/** Forward declaration for audio device factory */
typedef struct pjmedia_aud_dev_factory pjmedia_aud_dev_factory;
/* typedef for factory creation function */
typedef pjmedia_aud_dev_factory*
(*pjmedia_aud_dev_factory_create_func_ptr)(pj_pool_factory*);
/**
* Get string info for the specified capability.
*
* @param cap The capability ID.
* @param p_desc Optional pointer which will be filled with longer
* description about the capability.
*
* @return Capability name.
*/
PJ_DECL(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
const char **p_desc);
/**
* Set a capability field value in #pjmedia_aud_param structure. This will
* also set the flags field for the specified capability in the structure.
*
* @param param The structure.
* @param cap The audio capability which value is to be set.
* @param pval Pointer to value. Please see the type of value to
* be supplied in the pjmedia_aud_dev_cap documentation.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_param_set_cap(pjmedia_aud_param *param,
pjmedia_aud_dev_cap cap,
const void *pval);
/**
* Get a capability field value from #pjmedia_aud_param structure. This
* function will return PJMEDIA_EAUD_INVCAP error if the flag for that
* capability is not set in the flags field in the structure.
*
* @param param The structure.
* @param cap The audio capability which value is to be retrieved.
* @param pval Pointer to value. Please see the type of value to
* be supplied in the pjmedia_aud_dev_cap documentation.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_param_get_cap(const pjmedia_aud_param *param,
pjmedia_aud_dev_cap cap,
void *pval);
/**
* Initialize the audio subsystem. This will register all supported audio
* device factories to the audio subsystem. This function may be called
* more than once, but each call to this function must have the
* corresponding #pjmedia_aud_subsys_shutdown() call.
*
* @param pf The pool factory.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf);
/**
* Get the pool factory registered to the audio subsystem.
*
* @return The pool factory.
*/
PJ_DECL(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void);
/**
* Shutdown the audio subsystem. This will destroy all audio device factories
* registered in the audio subsystem. Note that currently opened audio streams
* may or may not be closed, depending on the implementation of the audio
* device factories.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_subsys_shutdown(void);
/**
* Register a supported audio device factory to the audio subsystem. This
* function can only be called after calling #pjmedia_aud_subsys_init().
*
* @param adf The audio device factory.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t)
pjmedia_aud_register_factory(pjmedia_aud_dev_factory_create_func_ptr adf);
/**
* Unregister an audio device factory from the audio subsystem. This
* function can only be called after calling #pjmedia_aud_subsys_init().
* Devices from this factory will be unlisted. If a device from this factory
* is currently in use, then the behavior is undefined.
*
* @param adf The audio device factory.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t)
pjmedia_aud_unregister_factory(pjmedia_aud_dev_factory_create_func_ptr adf);
/**
* Refresh the list of sound devices installed in the system. This function
* will only refresh the list of audio device so all active audio streams will
* be unaffected. After refreshing the device list, application MUST make sure
* to update all index references to audio devices (i.e. all variables of type
* pjmedia_aud_dev_index) before calling any function that accepts audio device
* index as its parameter.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_dev_refresh(void);
/**
* Get the number of sound devices installed in the system.
*
* @return The number of sound devices installed in the system.
*/
PJ_DECL(unsigned) pjmedia_aud_dev_count(void);
/**
* Get device information.
*
* @param id The audio device ID.
* @param info The device information which will be filled in by this
* function once it returns successfully.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
pjmedia_aud_dev_info *info);
/**
* Lookup device index based on the driver and device name.
*
* @param drv_name The driver name.
* @param dev_name The device name.
* @param id Pointer to store the returned device ID.
*
* @return PJ_SUCCESS if the device can be found.
*/
PJ_DECL(pj_status_t) pjmedia_aud_dev_lookup(const char *drv_name,
const char *dev_name,
pjmedia_aud_dev_index *id);
/**
* Initialize the audio device parameters with default values for the
* specified device.
*
* @param id The audio device ID.
* @param param The audio device parameters which will be initialized
* by this function once it returns successfully.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
pjmedia_aud_param *param);
/**
* Open audio stream object using the specified parameters.
*
* @param param Sound device parameters to be used for the stream.
* @param rec_cb Callback to be called on every input frame captured.
* @param play_cb Callback to be called everytime the sound device needs
* audio frames to be played back.
* @param user_data Arbitrary user data, which will be given back in the
* callbacks.
* @param p_strm Pointer to receive the audio stream.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *param,
pjmedia_aud_rec_cb rec_cb,
pjmedia_aud_play_cb play_cb,
void *user_data,
pjmedia_aud_stream **p_strm);
/**
* Get the running parameters for the specified audio stream.
*
* @param strm The audio stream.
* @param param Audio stream parameters to be filled in by this
* function once it returns successfully.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
pjmedia_aud_param *param);
/**
* Get the value of a specific capability of the audio stream.
*
* @param strm The audio stream.
* @param cap The audio capability which value is to be retrieved.
* @param value Pointer to value to be filled in by this function
* once it returns successfully. Please see the type
* of value to be supplied in the pjmedia_aud_dev_cap
* documentation.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
pjmedia_aud_dev_cap cap,
void *value);
/**
* Set the value of a specific capability of the audio stream.
*
* @param strm The audio stream.
* @param cap The audio capability which value is to be set.
* @param value Pointer to value. Please see the type of value to
* be supplied in the pjmedia_aud_dev_cap documentation.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
pjmedia_aud_dev_cap cap,
const void *value);
/**
* Start the stream.
*
* @param strm The audio stream.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm);
/**
* Stop the stream.
*
* @param strm The audio stream.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm);
/**
* Destroy the stream.
*
* @param strm The audio stream.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm);
/**
* Set an audio device observer callback.
*
* @param cb The callback that needs to be registred, or NULL in
* in case it needs to be unregistered. Only one callback
* can be registered.
*
* @return PJ_SUCCESS on successful operation or the appropriate
* error code.
*/
PJ_DECL(pj_status_t) pjmedia_aud_dev_set_observer_cb(pjmedia_aud_dev_observer_callback cb);
/**
* @}
*/
PJ_END_DECL
#endif /* __PJMEDIA_AUDIODEV_AUDIODEV_H__ */
diff --git a/deps/pjsip/pjmedia/src/pjmedia-audiodev/alsa_dev.c b/deps/pjsip/pjmedia/src/pjmedia-audiodev/alsa_dev.c
index c9d69fa7..d48d71d9 100644
--- a/deps/pjsip/pjmedia/src/pjmedia-audiodev/alsa_dev.c
+++ b/deps/pjsip/pjmedia/src/pjmedia-audiodev/alsa_dev.c
@@ -1,1005 +1,1073 @@
/* $Id: alsa_dev.c 4283 2012-10-12 06:19:32Z ming $ */
/*
* Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2007-2009 Keystream AB and Konftel AB, All rights reserved.
* Author: <dan.aberg@keystream.se>
*
* 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
* (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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia_audiodev.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
#include <pjmedia/errno.h>
#if defined(PJMEDIA_AUDIO_DEV_HAS_ALSA) && PJMEDIA_AUDIO_DEV_HAS_ALSA
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <pthread.h>
#include <errno.h>
#include <alsa/asoundlib.h>
#define THIS_FILE "alsa_dev.c"
#define MAX_DEVICES 128
/* Set to 1 to enable tracing */
#if 0
# define TRACE_(expr) PJ_LOG(5,expr)
#else
# define TRACE_(expr)
#endif
/*
* Factory prototypes
*/
static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f);
static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f);
static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f);
static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f);
static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f,
unsigned index,
pjmedia_aud_dev_info *info);
static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f,
unsigned index,
pjmedia_aud_param *param);
static pj_status_t alsa_factory_create_stream(pjmedia_aud_dev_factory *f,
const pjmedia_aud_param *param,
pjmedia_aud_rec_cb rec_cb,
pjmedia_aud_play_cb play_cb,
void *user_data,
pjmedia_aud_stream **p_strm);
static void alsa_factory_set_observer(pjmedia_aud_dev_factory *f,
pjmedia_aud_dev_change_callback cb);
static int alsa_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f);
static int alsa_factory_get_default_play_dev(pjmedia_aud_dev_factory *f);
/*
* Stream prototypes
*/
static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *strm,
pjmedia_aud_param *param);
static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *strm,
pjmedia_aud_dev_cap cap,
void *value);
static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
pjmedia_aud_dev_cap cap,
const void *value);
static pj_status_t alsa_stream_start(pjmedia_aud_stream *strm);
static pj_status_t alsa_stream_stop(pjmedia_aud_stream *strm);
static pj_status_t alsa_stream_destroy(pjmedia_aud_stream *strm);
+/* alsa device info */
+struct alsa_dev_info
+{
+ pjmedia_aud_dev_info info;
+ char alsa_name[64];
+};
+
struct alsa_factory
{
pjmedia_aud_dev_factory base;
pj_pool_factory *pf;
pj_pool_t *pool;
pj_pool_t *base_pool;
unsigned dev_cnt;
- pjmedia_aud_dev_info devs[MAX_DEVICES];
+ struct alsa_dev_info devs[MAX_DEVICES];
};
struct alsa_stream
{
pjmedia_aud_stream base;
/* Common */
pj_pool_t *pool;
struct alsa_factory *af;
void *user_data;
pjmedia_aud_param param; /* Running parameter */
int rec_id; /* Capture device id */
int quit;
/* Playback */
snd_pcm_t *pb_pcm;
snd_pcm_uframes_t pb_frames; /* samples_per_frame */
pjmedia_aud_play_cb pb_cb;
unsigned pb_buf_size;
char *pb_buf;
pj_thread_t *pb_thread;
/* Capture */
snd_pcm_t *ca_pcm;
snd_pcm_uframes_t ca_frames; /* samples_per_frame */
pjmedia_aud_rec_cb ca_cb;
unsigned ca_buf_size;
char *ca_buf;
pj_thread_t *ca_thread;
};
static pjmedia_aud_dev_factory_op alsa_factory_op =
{
&alsa_factory_init,
&alsa_factory_destroy,
&alsa_factory_get_dev_count,
&alsa_factory_get_dev_info,
&alsa_factory_default_param,
&alsa_factory_create_stream,
&alsa_factory_refresh,
&alsa_factory_set_observer,
&alsa_factory_get_default_rec_dev,
&alsa_factory_get_default_play_dev
};
static pjmedia_aud_stream_op alsa_stream_op =
{
&alsa_stream_get_param,
&alsa_stream_get_cap,
&alsa_stream_set_cap,
&alsa_stream_start,
&alsa_stream_stop,
&alsa_stream_destroy
};
static void null_alsa_error_handler (const char *file,
int line,
const char *function,
int err,
const char *fmt,
...)
{
PJ_UNUSED_ARG(file);
PJ_UNUSED_ARG(line);
PJ_UNUSED_ARG(function);
PJ_UNUSED_ARG(err);
PJ_UNUSED_ARG(fmt);
}
static void alsa_error_handler (const char *file,
int line,
const char *function,
int err,
const char *fmt,
...)
{
char err_msg[128];
int index;
va_list arg;
#ifndef NDEBUG
index = snprintf (err_msg, sizeof(err_msg), "ALSA lib %s:%i:(%s) ",
file, line, function);
#else
index = snprintf (err_msg, sizeof(err_msg), "ALSA lib: ");
#endif
va_start (arg, fmt);
if (index < sizeof(err_msg)-1)
index += vsnprintf (err_msg+index, sizeof(err_msg)-index, fmt, arg);
va_end(arg);
if (err && index < sizeof(err_msg)-1)
index += snprintf (err_msg+index, sizeof(err_msg)-index, ": %s",
snd_strerror(err));
PJ_LOG (4,(THIS_FILE, "%s", err_msg));
}
-static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name)
+static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name, const char *dev_desc)
{
- pjmedia_aud_dev_info *adi;
+ struct alsa_dev_info *adi;
snd_pcm_t* pcm;
int pb_result, ca_result;
if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
return PJ_ETOOMANY;
adi = &af->devs[af->dev_cnt];
TRACE_((THIS_FILE, "add_dev (%s): Enter", dev_name));
/* Try to open the device in playback mode */
pb_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_PLAYBACK, 0);
if (pb_result >= 0) {
TRACE_((THIS_FILE, "Try to open the device for playback - success"));
snd_pcm_close (pcm);
} else {
TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
}
/* Try to open the device in capture mode */
ca_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_CAPTURE, 0);
if (ca_result >= 0) {
TRACE_((THIS_FILE, "Try to open the device for capture - success"));
snd_pcm_close (pcm);
} else {
TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
}
/* Check if the device could be opened in playback or capture mode */
if (pb_result<0 && ca_result<0) {
TRACE_((THIS_FILE, "Unable to open sound device %s", dev_name));
return PJMEDIA_EAUD_NODEV;
}
/* Reset device info */
pj_bzero(adi, sizeof(*adi));
/* Set device name */
- strncpy(adi->name, dev_name, sizeof(adi->name));
+ strncpy(adi->alsa_name, dev_name, sizeof(adi->alsa_name));
+
+ /* Set comprehensive device name */
+ int name_size = sizeof(adi->info.name);
+ if (dev_desc) {
+ pj_bool_t name_set = PJ_FALSE;
+ if (strncmp("sysdefault", dev_name, 10) == 0) {
+ /* Only use first line for default device*/
+ char *ptr = strstr(dev_desc, "\n");
+ if (ptr) {
+ int len = ptr - dev_desc;
+ strncpy(adi->info.name, dev_desc, (len >= name_size-1)?name_size:len);
+ name_set = PJ_TRUE;
+ }
+ } else if (strncmp("iec958", dev_name, 6) == 0) {
+ /* Mangle name for SPDIF devices*/
+ char *ptr = strstr(dev_desc, ",");
+ if (ptr) {
+ int len = ptr - dev_desc;
+ if (len + 18 < name_size) {
+ strncpy(adi->info.name, dev_desc, len);
+ strncpy(adi->info.name+len, ", Digital (S/PDIF)", 18);
+ name_set = PJ_TRUE;
+ }
+ }
+ }
+
+ if (!name_set) {
+ /* Use the entire description for other device names */
+ int i = 0;
+ while (i < name_size-1 && dev_desc[i] != '\0') {
+ if (dev_desc[i] == '\n' || dev_desc[i] == '\r')
+ adi->info.name[i] = ' ';
+ else
+ adi->info.name[i] = dev_desc[i];
+ i++;
+ }
+ }
+ } else {
+ strncpy(adi->info.name, dev_name, name_size);
+ }
/* Check the number of playback channels */
- adi->output_count = (pb_result>=0) ? 1 : 0;
+ adi->info.output_count = (pb_result>=0) ? 1 : 0;
/* Check the number of capture channels */
- adi->input_count = (ca_result>=0) ? 1 : 0;
+ adi->info.input_count = (ca_result>=0) ? 1 : 0;
/* Set the default sample rate */
- adi->default_samples_per_sec = 8000;
+ adi->info.default_samples_per_sec = 8000;
/* Driver name */
- strcpy(adi->driver, "ALSA");
+ strcpy(adi->info.driver, "ALSA");
++af->dev_cnt;
- PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->name));
+ PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->alsa_name));
return PJ_SUCCESS;
}
/* Create ALSA audio driver. */
pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf)
{
struct alsa_factory *af;
pj_pool_t *pool;
pool = pj_pool_create(pf, "alsa_aud_base", 256, 256, NULL);
af = PJ_POOL_ZALLOC_T(pool, struct alsa_factory);
af->pf = pf;
af->base_pool = pool;
af->base.op = &alsa_factory_op;
return &af->base;
}
/* API: init factory */
static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f)
{
pj_status_t status = alsa_factory_refresh(f);
if (PJ_SUCCESS != status)
return status;
PJ_LOG(4,(THIS_FILE, "ALSA initialized"));
return PJ_SUCCESS;
}
/* API: destroy factory */
static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f)
{
struct alsa_factory *af = (struct alsa_factory*)f;
if (af->pool)
pj_pool_release(af->pool);
if (af->base_pool) {
pj_pool_t *pool = af->base_pool;
af->base_pool = NULL;
pj_pool_release(pool);
}
/* Restore handler */
snd_lib_error_set_handler(NULL);
return PJ_SUCCESS;
}
/* API: refresh the device list */
static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f)
{
struct alsa_factory *af = (struct alsa_factory*)f;
char **hints, **n;
int err;
TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices"));
if (af->pool != NULL) {
pj_pool_release(af->pool);
af->pool = NULL;
}
af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL);
af->dev_cnt = 0;
/* Enumerate sound devices */
err = snd_device_name_hint(-1, "pcm", (void***)&hints);
if (err != 0)
return PJMEDIA_EAUD_SYSERR;
/* Set a null error handler prior to enumeration to suppress errors */
snd_lib_error_set_handler(null_alsa_error_handler);
n = hints;
while (*n != NULL) {
char *name = snd_device_name_get_hint(*n, "NAME");
- if (name != NULL && 0 != strcmp("null", name)) {
- add_dev(af, name);
+ char *desc = snd_device_name_get_hint(*n, "DESC");
+ if (name != NULL) {
+ if (strncmp("null", name, 4) == 0 ||
+ strncmp("front", name, 5) == 0 ||
+ strncmp("rear", name, 4) == 0 ||
+ strncmp("side", name, 4) == 0 ||
+ strncmp("dmix", name, 4) == 0 ||
+ strncmp("dsnoop", name, 6) == 0 ||
+ strncmp("hw", name, 2) == 0 ||
+ strncmp("plughw", name, 6) == 0 ||
+ strncmp("center_lfe", name, 10) == 0 ||
+ strncmp("surround40", name, 10) == 0 ||
+ strncmp("surround41", name, 10) == 0 ||
+ strncmp("surround50", name, 10) == 0 ||
+ strncmp("surround51", name, 10) == 0 ||
+ strncmp("surround71", name, 10) == 0 ||
+ (strncmp("default", name, 7) == 0 && strstr(name, ":CARD=") != NULL)) {
+ /* skip these devices, 'sysdefault' always contains the relevant information */
+ ;
+ } else {
+ add_dev(af, name, desc);
+ }
free(name);
+ free(desc);
}
n++;
}
/* Install error handler after enumeration, otherwise we'll get many
* error messages about invalid card/device ID.
*/
snd_lib_error_set_handler(alsa_error_handler);
err = snd_device_name_free_hint((void**)hints);
PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt));
return PJ_SUCCESS;
}
/* API: get device count */
static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f)
{
struct alsa_factory *af = (struct alsa_factory*)f;
return af->dev_cnt;
}
/* API: get device info */
static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f,
unsigned index,
pjmedia_aud_dev_info *info)
{
struct alsa_factory *af = (struct alsa_factory*)f;
PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
- pj_memcpy(info, &af->devs[index], sizeof(*info));
+ pj_memcpy(info, &af->devs[index].info, sizeof(*info));
info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
return PJ_SUCCESS;
}
/* API: create default parameter */
static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f,
unsigned index,
pjmedia_aud_param *param)
{
struct alsa_factory *af = (struct alsa_factory*)f;
- pjmedia_aud_dev_info *adi;
+ struct alsa_dev_info *adi;
PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
adi = &af->devs[index];
pj_bzero(param, sizeof(*param));
- if (adi->input_count && adi->output_count) {
+ if (adi->info.input_count && adi->info.output_count) {
param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
param->rec_id = index;
param->play_id = index;
- } else if (adi->input_count) {
+ } else if (adi->info.input_count) {
param->dir = PJMEDIA_DIR_CAPTURE;
param->rec_id = index;
param->play_id = PJMEDIA_AUD_INVALID_DEV;
- } else if (adi->output_count) {
+ } else if (adi->info.output_count) {
param->dir = PJMEDIA_DIR_PLAYBACK;
param->play_id = index;
param->rec_id = PJMEDIA_AUD_INVALID_DEV;
} else {
return PJMEDIA_EAUD_INVDEV;
}
- param->clock_rate = adi->default_samples_per_sec;
+ param->clock_rate = adi->info.default_samples_per_sec;
param->channel_count = 1;
- param->samples_per_frame = adi->default_samples_per_sec * 20 / 1000;
+ param->samples_per_frame = adi->info.default_samples_per_sec * 20 / 1000;
param->bits_per_sample = 16;
- param->flags = adi->caps;
+ param->flags = adi->info.caps;
param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
return PJ_SUCCESS;
}
static int pb_thread_func (void *arg)
{
struct alsa_stream* stream = (struct alsa_stream*) arg;
snd_pcm_t* pcm = stream->pb_pcm;
int size = stream->pb_buf_size;
snd_pcm_uframes_t nframes = stream->pb_frames;
void* user_data = stream->user_data;
char* buf = stream->pb_buf;
pj_timestamp tstamp;
int result;
pj_bzero (buf, size);
tstamp.u64 = 0;
TRACE_((THIS_FILE, "pb_thread_func(%u): Started",
(unsigned)syscall(SYS_gettid)));
snd_pcm_prepare (pcm);
while (!stream->quit) {
pjmedia_frame frame;
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
frame.buf = buf;
frame.size = size;
frame.timestamp.u64 = tstamp.u64;
frame.bit_info = 0;
result = stream->pb_cb (user_data, &frame);
if (result != PJ_SUCCESS || stream->quit)
break;
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
pj_bzero (buf, size);
result = snd_pcm_writei (pcm, buf, nframes);
if (result == -EPIPE) {
PJ_LOG (4,(THIS_FILE, "pb_thread_func: underrun!"));
snd_pcm_prepare (pcm);
} else if (result < 0) {
PJ_LOG (4,(THIS_FILE, "pb_thread_func: error writing data!"));
}
tstamp.u64 += nframes;
}
snd_pcm_drain (pcm);
TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
return PJ_SUCCESS;
}
static int ca_thread_func (void *arg)
{
struct alsa_stream* stream = (struct alsa_stream*) arg;
snd_pcm_t* pcm = stream->ca_pcm;
int size = stream->ca_buf_size;
snd_pcm_uframes_t nframes = stream->ca_frames;
void* user_data = stream->user_data;
char* buf = stream->ca_buf;
pj_timestamp tstamp;
int result;
struct sched_param param;
pthread_t* thid;
thid = (pthread_t*) pj_thread_get_os_handle (pj_thread_this());
param.sched_priority = sched_get_priority_max (SCHED_RR);
PJ_LOG (5,(THIS_FILE, "ca_thread_func(%u): Set thread priority "
"for audio capture thread.",
(unsigned)syscall(SYS_gettid)));
result = pthread_setschedparam (*thid, SCHED_RR, &param);
if (result) {
if (result == EPERM)
PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, "
"root access needed."));
else
PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, "
"error: %d",
result));
}
pj_bzero (buf, size);
tstamp.u64 = 0;
TRACE_((THIS_FILE, "ca_thread_func(%u): Started",
(unsigned)syscall(SYS_gettid)));
snd_pcm_prepare (pcm);
while (!stream->quit) {
pjmedia_frame frame;
pj_bzero (buf, size);
result = snd_pcm_readi (pcm, buf, nframes);
if (result == -EPIPE) {
PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!"));
snd_pcm_prepare (pcm);
continue;
} else if (result < 0) {
PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!"));
}
if (stream->quit)
break;
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
frame.buf = (void*) buf;
frame.size = size;
frame.timestamp.u64 = tstamp.u64;
frame.bit_info = 0;
result = stream->ca_cb (user_data, &frame);
if (result != PJ_SUCCESS || stream->quit)
break;
tstamp.u64 += nframes;
}
snd_pcm_drain (pcm);
TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
return PJ_SUCCESS;
}
static pj_status_t open_playback (struct alsa_stream* stream,
const pjmedia_aud_param *param)
{
snd_pcm_hw_params_t* params;
snd_pcm_format_t format;
int result;
unsigned int rate;
snd_pcm_uframes_t tmp_buf_size;
snd_pcm_uframes_t tmp_period_size;
if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt)
return PJMEDIA_EAUD_INVDEV;
/* Open PCM for playback */
PJ_LOG (5,(THIS_FILE, "open_playback: Open playback device '%s'",
- stream->af->devs[param->play_id].name));
+ stream->af->devs[param->play_id].alsa_name));
result = snd_pcm_open (&stream->pb_pcm,
- stream->af->devs[param->play_id].name,
+ stream->af->devs[param->play_id].alsa_name,
SND_PCM_STREAM_PLAYBACK,
0);
if (result < 0)
return PJMEDIA_EAUD_SYSERR;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca (&params);
/* Fill it in with default values. */
snd_pcm_hw_params_any (stream->pb_pcm, params);
/* Set interleaved mode */
snd_pcm_hw_params_set_access (stream->pb_pcm, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
/* Set format */
switch (param->bits_per_sample) {
case 8:
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S8"));
format = SND_PCM_FORMAT_S8;
break;
case 16:
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE"));
format = SND_PCM_FORMAT_S16_LE;
break;
case 24:
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S24_LE"));
format = SND_PCM_FORMAT_S24_LE;
break;
case 32:
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S32_LE"));
format = SND_PCM_FORMAT_S32_LE;
break;
default:
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE"));
format = SND_PCM_FORMAT_S16_LE;
break;
}
snd_pcm_hw_params_set_format (stream->pb_pcm, params, format);
/* Set number of channels */
TRACE_((THIS_FILE, "open_playback: set channels: %d",
param->channel_count));
snd_pcm_hw_params_set_channels (stream->pb_pcm, params,
param->channel_count);
/* Set clock rate */
rate = param->clock_rate;
TRACE_((THIS_FILE, "open_playback: set clock rate: %d", rate));
snd_pcm_hw_params_set_rate_near (stream->pb_pcm, params, &rate, NULL);
TRACE_((THIS_FILE, "open_playback: clock rate set to: %d", rate));
/* Set period size to samples_per_frame frames. */
stream->pb_frames = (snd_pcm_uframes_t) param->samples_per_frame /
param->channel_count;
TRACE_((THIS_FILE, "open_playback: set period size: %d",
stream->pb_frames));
tmp_period_size = stream->pb_frames;
snd_pcm_hw_params_set_period_size_near (stream->pb_pcm, params,
&tmp_period_size, NULL);
TRACE_((THIS_FILE, "open_playback: period size set to: %d",
tmp_period_size));
/* Set the sound device buffer size and latency */
if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
tmp_buf_size = (rate / 1000) * param->output_latency_ms;
else
tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
snd_pcm_hw_params_set_buffer_size_near (stream->pb_pcm, params,
&tmp_buf_size);
stream->param.output_latency_ms = tmp_buf_size / (rate / 1000);
/* Set our buffer */
stream->pb_buf_size = stream->pb_frames * param->channel_count *
(param->bits_per_sample/8);
stream->pb_buf = (char*) pj_pool_alloc(stream->pool, stream->pb_buf_size);
TRACE_((THIS_FILE, "open_playback: buffer size set to: %d",
(int)tmp_buf_size));
TRACE_((THIS_FILE, "open_playback: playback_latency set to: %d ms",
(int)stream->param.output_latency_ms));
/* Activate the parameters */
result = snd_pcm_hw_params (stream->pb_pcm, params);
if (result < 0) {
snd_pcm_close (stream->pb_pcm);
return PJMEDIA_EAUD_SYSERR;
}
PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for playing, sample rate=%d"
", ch=%d, bits=%d, period size=%d frames, latency=%d ms",
- stream->af->devs[param->play_id].name,
+ stream->af->devs[param->play_id].alsa_name,
rate, param->channel_count,
param->bits_per_sample, stream->pb_frames,
(int)stream->param.output_latency_ms));
return PJ_SUCCESS;
}
static pj_status_t open_capture (struct alsa_stream* stream,
const pjmedia_aud_param *param)
{
snd_pcm_hw_params_t* params;
snd_pcm_format_t format;
int result;
unsigned int rate;
snd_pcm_uframes_t tmp_buf_size;
snd_pcm_uframes_t tmp_period_size;
if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
return PJMEDIA_EAUD_INVDEV;
/* Open PCM for capture */
PJ_LOG (5,(THIS_FILE, "open_capture: Open capture device '%s'",
- stream->af->devs[param->rec_id].name));
+ stream->af->devs[param->rec_id].alsa_name));
result = snd_pcm_open (&stream->ca_pcm,
- stream->af->devs[param->rec_id].name,
+ stream->af->devs[param->rec_id].alsa_name,
SND_PCM_STREAM_CAPTURE,
0);
if (result < 0)
return PJMEDIA_EAUD_SYSERR;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca (&params);
/* Fill it in with default values. */
snd_pcm_hw_params_any (stream->ca_pcm, params);
/* Set interleaved mode */
snd_pcm_hw_params_set_access (stream->ca_pcm, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
/* Set format */
switch (param->bits_per_sample) {
case 8:
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S8"));
format = SND_PCM_FORMAT_S8;
break;
case 16:
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE"));
format = SND_PCM_FORMAT_S16_LE;
break;
case 24:
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S24_LE"));
format = SND_PCM_FORMAT_S24_LE;
break;
case 32:
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S32_LE"));
format = SND_PCM_FORMAT_S32_LE;
break;
default:
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE"));
format = SND_PCM_FORMAT_S16_LE;
break;
}
snd_pcm_hw_params_set_format (stream->ca_pcm, params, format);
/* Set number of channels */
TRACE_((THIS_FILE, "open_capture: set channels: %d",
param->channel_count));
snd_pcm_hw_params_set_channels (stream->ca_pcm, params,
param->channel_count);
/* Set clock rate */
rate = param->clock_rate;
TRACE_((THIS_FILE, "open_capture: set clock rate: %d", rate));
snd_pcm_hw_params_set_rate_near (stream->ca_pcm, params, &rate, NULL);
TRACE_((THIS_FILE, "open_capture: clock rate set to: %d", rate));
/* Set period size to samples_per_frame frames. */
stream->ca_frames = (snd_pcm_uframes_t) param->samples_per_frame /
param->channel_count;
TRACE_((THIS_FILE, "open_capture: set period size: %d",
stream->ca_frames));
tmp_period_size = stream->ca_frames;
snd_pcm_hw_params_set_period_size_near (stream->ca_pcm, params,
&tmp_period_size, NULL);
TRACE_((THIS_FILE, "open_capture: period size set to: %d",
tmp_period_size));
/* Set the sound device buffer size and latency */
if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
tmp_buf_size = (rate / 1000) * param->input_latency_ms;
else
tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY;
snd_pcm_hw_params_set_buffer_size_near (stream->ca_pcm, params,
&tmp_buf_size);
stream->param.input_latency_ms = tmp_buf_size / (rate / 1000);
/* Set our buffer */
stream->ca_buf_size = stream->ca_frames * param->channel_count *
(param->bits_per_sample/8);
stream->ca_buf = (char*) pj_pool_alloc (stream->pool, stream->ca_buf_size);
TRACE_((THIS_FILE, "open_capture: buffer size set to: %d",
(int)tmp_buf_size));
TRACE_((THIS_FILE, "open_capture: capture_latency set to: %d ms",
(int)stream->param.input_latency_ms));
/* Activate the parameters */
result = snd_pcm_hw_params (stream->ca_pcm, params);
if (result < 0) {
snd_pcm_close (stream->ca_pcm);
return PJMEDIA_EAUD_SYSERR;
}
PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for capture, sample rate=%d"
", ch=%d, bits=%d, period size=%d frames, latency=%d ms",
- stream->af->devs[param->rec_id].name,
+ stream->af->devs[param->rec_id].alsa_name,
rate, param->channel_count,
param->bits_per_sample, stream->ca_frames,
(int)stream->param.input_latency_ms));
return PJ_SUCCESS;
}
/* API: create stream */
static pj_status_t alsa_factory_create_stream(pjmedia_aud_dev_factory *f,
const pjmedia_aud_param *param,
pjmedia_aud_rec_cb rec_cb,
pjmedia_aud_play_cb play_cb,
void *user_data,
pjmedia_aud_stream **p_strm)
{
struct alsa_factory *af = (struct alsa_factory*)f;
pj_status_t status;
pj_pool_t* pool;
struct alsa_stream* stream;
pool = pj_pool_create (af->pf, "alsa%p", 1024, 1024, NULL);
if (!pool)
return PJ_ENOMEM;
/* Allocate and initialize comon stream data */
stream = PJ_POOL_ZALLOC_T (pool, struct alsa_stream);
stream->base.op = &alsa_stream_op;
stream->pool = pool;
stream->af = af;
stream->user_data = user_data;
stream->pb_cb = play_cb;
stream->ca_cb = rec_cb;
stream->quit = 0;
pj_memcpy(&stream->param, param, sizeof(*param));
/* Init playback */
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
status = open_playback (stream, param);
if (status != PJ_SUCCESS) {
pj_pool_release (pool);
return status;
}
}
/* Init capture */
if (param->dir & PJMEDIA_DIR_CAPTURE) {
status = open_capture (stream, param);
if (status != PJ_SUCCESS) {
if (param->dir & PJMEDIA_DIR_PLAYBACK)
snd_pcm_close (stream->pb_pcm);
pj_pool_release (pool);
return status;
}
}
*p_strm = &stream->base;
return PJ_SUCCESS;
}
/* API: set audio device change observer */
static void alsa_factory_set_observer(pjmedia_aud_dev_factory *f,
pjmedia_aud_dev_change_callback cb)
{
PJ_UNUSED_ARG(f);
PJ_UNUSED_ARG(cb);
}
/* API: get default recording device */
static int alsa_factory_get_default_rec_dev(pjmedia_aud_dev_factory *f)
{
PJ_UNUSED_ARG(f);
return -1;
}
/* API: get default playback device */
static int alsa_factory_get_default_play_dev(pjmedia_aud_dev_factory *f)
{
PJ_UNUSED_ARG(f);
return -1;
}
/* API: get running parameter */
static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *s,
pjmedia_aud_param *pi)
{
struct alsa_stream *stream = (struct alsa_stream*)s;
PJ_ASSERT_RETURN(s && pi, PJ_EINVAL);
pj_memcpy(pi, &stream->param, sizeof(*pi));
return PJ_SUCCESS;
}
/* API: get capability */
static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *s,
pjmedia_aud_dev_cap cap,
void *pval)
{
struct alsa_stream *stream = (struct alsa_stream*)s;
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
(stream->param.dir & PJMEDIA_DIR_CAPTURE))
{
/* Recording latency */
*(unsigned*)pval = stream->param.input_latency_ms;
return PJ_SUCCESS;
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
(stream->param.dir & PJMEDIA_DIR_PLAYBACK))
{
/* Playback latency */
*(unsigned*)pval = stream->param.output_latency_ms;
return PJ_SUCCESS;
} else {
return PJMEDIA_EAUD_INVCAP;
}
}
/* API: set capability */
static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
pjmedia_aud_dev_cap cap,
const void *value)
{
PJ_UNUSED_ARG(strm);
PJ_UNUSED_ARG(cap);
PJ_UNUSED_ARG(value);
return PJMEDIA_EAUD_INVCAP;
}
/* API: start stream */
static pj_status_t alsa_stream_start (pjmedia_aud_stream *s)
{
struct alsa_stream *stream = (struct alsa_stream*)s;
pj_status_t status = PJ_SUCCESS;
stream->quit = 0;
if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
status = pj_thread_create (stream->pool,
"alsasound_playback",
pb_thread_func,
stream,
0, //ZERO,
0,
&stream->pb_thread);
if (status != PJ_SUCCESS)
return status;
}
if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
status = pj_thread_create (stream->pool,
"alsasound_playback",
ca_thread_func,
stream,
0, //ZERO,
0,
&stream->ca_thread);
if (status != PJ_SUCCESS) {
stream->quit = PJ_TRUE;
pj_thread_join(stream->pb_thread);
pj_thread_destroy(stream->pb_thread);
stream->pb_thread = NULL;
}
}
return status;
}
/* API: stop stream */
static pj_status_t alsa_stream_stop (pjmedia_aud_stream *s)
{
struct alsa_stream *stream = (struct alsa_stream*)s;
stream->quit = 1;
if (stream->pb_thread) {
TRACE_((THIS_FILE,
"alsa_stream_stop(%u): Waiting for playback to stop.",
(unsigned)syscall(SYS_gettid)));
pj_thread_join (stream->pb_thread);
TRACE_((THIS_FILE,
"alsa_stream_stop(%u): playback stopped.",
(unsigned)syscall(SYS_gettid)));
pj_thread_destroy(stream->pb_thread);
stream->pb_thread = NULL;
}
if (stream->ca_thread) {
TRACE_((THIS_FILE,
"alsa_stream_stop(%u): Waiting for capture to stop.",
(unsigned)syscall(SYS_gettid)));
pj_thread_join (stream->ca_thread);
TRACE_((THIS_FILE,
"alsa_stream_stop(%u): capture stopped.",
(unsigned)syscall(SYS_gettid)));
pj_thread_destroy(stream->ca_thread);
stream->ca_thread = NULL;
}
return PJ_SUCCESS;
}
static pj_status_t alsa_stream_destroy (pjmedia_aud_stream *s)
{
struct alsa_stream *stream = (struct alsa_stream*)s;
alsa_stream_stop (s);
if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
snd_pcm_close (stream->pb_pcm);
stream->pb_pcm = NULL;
}
if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
snd_pcm_close (stream->ca_pcm);
stream->ca_pcm = NULL;
}
pj_pool_release (stream->pool);
return PJ_SUCCESS;
}
#endif /* PJMEDIA_AUDIO_DEV_HAS_ALSA */

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 4:50 AM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408810
Default Alt Text
(56 KB)

Event Timeline