Added feature in conference bridge to get and set the signal level of individual port and individual stream direction

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@323 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2006-03-17 00:16:01 +00:00
parent d0659a3264
commit b100d69072
5 changed files with 542 additions and 158 deletions

View File

@ -58,12 +58,36 @@ enum pjmedia_conf_option
{
PJMEDIA_CONF_NO_MIC = 1, /**< Disable audio streams from the
microphone device. */
PJMEDIA_CONF_NO_DEVICE = 2, /**< Do not create sound device. */
};
/**
* Create conference bridge. This normally will also create instances of
* sound device to be attached to the port zero of the bridge.
* Create conference bridge with the specified parameters. The sampling rate,
* samples per frame, and bits per sample will be used for the internal
* operation of the bridge (e.g. when mixing audio frames). However, ports
* with different configuration may be connected to the bridge. In this case,
* the bridge is able to perform sampling rate conversion, and buffering in
* case the samples per frame is different.
*
* For this version of PJMEDIA, only 16bits per sample is supported.
*
* For this version of PJMEDIA, the channel count of the ports MUST match
* the channel count of the bridge.
*
* Under normal operation (i.e. when PJMEDIA_CONF_NO_DEVICE option is NOT
* specified), the bridge internally create an instance of sound device
* and connect the sound device to port zero of the bridge.
*
* If PJMEDIA_CONF_NO_DEVICE options is specified, no sound device will
* be created in the conference bridge. Application MUST acquire the port
* interface of the bridge by calling #pjmedia_conf_get_master_port(), and
* connect this port interface to a sound device port by calling
* #pjmedia_snd_port_connect().
*
* The sound device is crucial for the bridge's operation, because it provides
* the bridge with necessary clock to process the audio frames periodically.
* Internally, the bridge runs when get_frame() to port zero is called.
*
* @param pool Pool to use to allocate the bridge and
* additional buffers for the sound device.
@ -112,6 +136,27 @@ PJ_DECL(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
PJ_DECL(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf );
/**
* Get the master port interface of the conference bridge. The master port
* corresponds to the port zero of the bridge. This is only usefull when
* application wants to manage the sound device by itself, instead of
* allowing the bridge to automatically create a sound device implicitly.
*
* This function will only return a port interface if PJMEDIA_CONF_NO_DEVICE
* option was specified when the bridge was created.
*
* Application can connect the port returned by this function to a
* sound device by calling #pjmedia_snd_port_connect().
*
* @param conf The conference bridge.
*
* @return The port interface of port zero of the bridge,
* only when PJMEDIA_CONF_NO_DEVICE options was
* specified when the bridge was created.
*/
PJ_DECL(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf);
/**
* Add stream port to the conference bridge. By default, the new conference
* port will have both TX and RX enabled, but it is not connected to any
@ -160,12 +205,17 @@ PJ_DECL(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
* @param conf The conference bridge.
* @param src_slot Source slot.
* @param sink_slot Sink slot.
* @param level This argument is reserved for future improvements
* where it is possible to adjust the level of signal
* transmitted in a specific connection. For now,
* this argument MUST be zero.
*
* @return PJ_SUCCES on success.
*/
PJ_DECL(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
unsigned src_slot,
unsigned sink_slot );
unsigned sink_slot,
int level );
/**
@ -223,7 +273,75 @@ PJ_DECL(pj_status_t) pjmedia_conf_get_port_info( pjmedia_conf *conf,
*/
PJ_DECL(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
unsigned *size,
pjmedia_conf_port_info info[]);
pjmedia_conf_port_info info[]
);
/**
* Get last signal level transmitted to or received from the specified port.
* The signal level is an integer value in zero to 255, with zero indicates
* no signal, and 255 indicates the loudest signal level.
*
* @param conf The conference bridge.
* @param slot Slot number.
* @param tx_level Optional argument to receive the level of signal
* transmitted to the specified port (i.e. the direction
* is from the bridge to the port).
* @param rx_level Optional argument to receive the level of signal
* received from the port (i.e. the direction is from the
* port to the bridge).
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_conf_get_signal_level(pjmedia_conf *conf,
unsigned slot,
unsigned *tx_level,
unsigned *rx_level);
/**
* Adjust the level of signal received from the specified port.
* Application may adjust the level to make the signal received from the port
* either louder or more quiet, by giving the value from +127 to -128. The
* value zero indicates no adjustment, the value -128 will mute the signal,
* and the value of +127 will make the signal twice as loud.
*
* @param conf The conference bridge.
* @param slot Slot number.
* @param adj_level Adjustment level, with valid values are from -128
* to +127. A value of zero means there is no level
* adjustment to be made, the value -128 will mute the
* signal, and the value of +127 will make the signal
* twice as loud.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
unsigned slot,
int adj_level );
/**
* Adjust the level of signal to be transmitted to the specified port.
* Application may adjust the level to make the signal transmitted to the port
* either louder or more quiet, by giving the value from +127 to -128. The
* value zero indicates no adjustment, the value -128 will mute the signal,
* and the value of +127 will make the signal twice as loud.
*
* @param conf The conference bridge.
* @param slot Slot number.
* @param adj_level Adjustment level, with valid values are from -128
* to +127. A value of zero means there is no level
* adjustment to be made, the value -128 will mute the
* signal, and the value of +127 will make the signal
* twice as loud.
*
* @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
unsigned slot,
int adj_level );
PJ_END_DECL

View File

@ -21,7 +21,7 @@
#include <pjmedia/port.h>
#include <pjmedia/resample.h>
#include <pjmedia/silencedet.h>
#include <pjmedia/sound.h>
#include <pjmedia/sound_port.h>
#include <pjmedia/stream.h>
#include <pj/assert.h>
#include <pj/log.h>
@ -46,7 +46,9 @@
* DON'T GET CONFUSED!!
*
* TX and RX directions are always viewed from the conference bridge's point
* of view, and NOT from the port's point of view.
* of view, and NOT from the port's point of view. So TX means the bridge
* is transmitting to the port, RX means the bridge is receiving from the
* port.
*/
@ -67,14 +69,29 @@ struct conf_port
unsigned clock_rate; /**< Port's clock rate. */
unsigned samples_per_frame; /**< Port's samples per frame. */
/* Calculated signal levels: */
pj_bool_t need_tx_level; /**< Need to calculate tx level? */
unsigned tx_level; /**< Last tx level to this port. */
unsigned rx_level; /**< Last rx level from this port. */
/* The normalized signal level adjustment.
* A value of 128 means there's no adjustment.
*/
unsigned tx_adj_level; /**< Adjustment for TX. */
unsigned rx_adj_level; /**< Adjustment for RX. */
/* Resample, for converting clock rate, if they're different. */
pjmedia_resample *rx_resample;
pjmedia_resample *tx_resample;
/* RX buffer is temporary buffer to be used when there is mismatch
* between port's sample rate or ptime with conference's sample rate
* or ptime. When both sample rate and ptime of the port match the
* conference settings, this buffer will not be used.
* or ptime. The buffer is used for sampling rate conversion AND/OR to
* buffer the samples until there are enough samples to fulfill a
* complete frame to be processed by the bridge.
*
* When both sample rate AND ptime of the port match the conference
* settings, this buffer will not be created.
*
* This buffer contains samples at port's clock rate.
* The size of this buffer is the sum between port's samples per frame
@ -85,7 +102,10 @@ struct conf_port
unsigned rx_buf_count; /**< # of samples in the buf. */
/* Mix buf is a temporary buffer used to calculate the average signal
* received by this port from all other ports.
* received by this port from all other ports. Samples from all ports
* that are transmitting to this port will be accumulated here, then
* they will be divided by the sources count before the samples are put
* to the TX buffer of this port.
*
* This buffer contains samples at bridge's clock rate.
* The size of this buffer is equal to samples per frame of the bridge.
@ -97,8 +117,12 @@ struct conf_port
/* Tx buffer is a temporary buffer to be used when there's mismatch
* between port's clock rate or ptime with conference's sample rate
* or ptime. When both sample rate and ptime of the port match the
* conference's settings, this buffer will not be used.
* or ptime. This buffer is used as the source of the sampling rate
* conversion AND/OR to buffer the samples until there are enough
* samples to fulfill a complete frame to be transmitted to the port.
*
* When both sample rate and ptime of the port match the bridge's
* settings, this buffer will not be created.
*
* This buffer contains samples at port's clock rate.
* The size of this buffer is the sum between port's samples per frame
@ -108,8 +132,8 @@ struct conf_port
unsigned tx_buf_cap; /**< Max size, in samples. */
unsigned tx_buf_count; /**< # of samples in the buffer. */
/* Snd buffers is a special buffer for sound device port (port 0).
* It's not used by other ports.
/* Snd buffers is a special buffer for sound device port (port 0, master
* port). It's not used by other ports.
*
* There are multiple numbers of this buffer, because we can not expect
* the mic and speaker thread to run equally after one another. In most
@ -131,8 +155,9 @@ struct pjmedia_conf
unsigned max_ports; /**< Maximum ports. */
unsigned port_cnt; /**< Current number of ports. */
unsigned connect_cnt; /**< Total number of connections */
pjmedia_snd_stream *snd_rec; /**< Sound recorder stream. */
pjmedia_snd_stream *snd_player; /**< Sound player stream. */
pjmedia_snd_port *snd_rec; /**< Sound recorder stream. */
pjmedia_snd_port *snd_player; /**< Sound player stream. */
pjmedia_port *master_port; /**< Port zero's port. */
pj_mutex_t *mutex; /**< Conference mutex. */
struct conf_port **ports; /**< Array of ports. */
pj_uint16_t *uns_buf; /**< Buf for unsigned conversion */
@ -147,14 +172,11 @@ struct pjmedia_conf
unsigned char linear2ulaw(int pcm_val);
/* Prototypes */
static pj_status_t play_cb( /* in */ void *user_data,
/* in */ pj_uint32_t timestamp,
/* out */ void *output,
/* out */ unsigned size);
static pj_status_t rec_cb( /* in */ void *user_data,
/* in */ pj_uint32_t timestamp,
/* in */ const void *input,
/* in*/ unsigned size);
static pj_status_t put_frame(pjmedia_port *this_port,
const pjmedia_frame *frame);
static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame *frame);
/*
* Create port.
@ -179,6 +201,10 @@ static pj_status_t create_conf_port( pj_pool_t *pool,
conf_port->rx_setting = PJMEDIA_PORT_ENABLE;
conf_port->tx_setting = PJMEDIA_PORT_ENABLE;
/* Default level adjustment is 128 (which means no adjustment) */
conf_port->tx_adj_level = 128;
conf_port->rx_adj_level = 128;
/* Create transmit flag array */
conf_port->listeners = pj_pool_zalloc(pool,
conf->max_ports*sizeof(pj_bool_t));
@ -309,6 +335,44 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
conf->ports[0] = conf_port;
conf->port_cnt++;
/* Create sound devices: */
/* Create recorder only if mic is not disabled. */
if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0 &&
(conf->options & PJMEDIA_CONF_NO_MIC) == 0)
{
status = pjmedia_snd_port_create_rec( pool, -1, conf->clock_rate,
conf->channel_count,
conf->samples_per_frame,
conf->bits_per_sample,
0, /* Options */
&conf->snd_rec);
if (status != PJ_SUCCESS) {
conf->snd_rec = NULL;
return status;
}
}
/* Create player device */
if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate,
conf->channel_count,
conf->samples_per_frame,
conf->bits_per_sample,
0, /* options */
&conf->snd_player);
if (status != PJ_SUCCESS) {
if (conf->snd_rec) {
pjmedia_snd_port_destroy(conf->snd_rec);
conf->snd_rec = NULL;
}
conf->snd_player = NULL;
return status;
}
}
PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
return PJ_SUCCESS;
@ -348,9 +412,34 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
conf->options = options;
conf->max_ports = max_ports;
conf->clock_rate = clock_rate;
conf->channel_count = channel_count;
conf->samples_per_frame = samples_per_frame;
conf->bits_per_sample = bits_per_sample;
/* Create and initialize the master port interface. */
conf->master_port = pj_pool_zalloc(pool, sizeof(pjmedia_port));
PJ_ASSERT_RETURN(conf->master_port, PJ_ENOMEM);
conf->master_port->info.bits_per_sample = bits_per_sample;
conf->master_port->info.bytes_per_frame = samples_per_frame *
bits_per_sample / 8;
conf->master_port->info.channel_count = channel_count;
conf->master_port->info.encoding_name = pj_str("pcm");
conf->master_port->info.has_info = 1;
conf->master_port->info.name = pj_str("master port");
conf->master_port->info.need_info = 0;
conf->master_port->info.pt = 0xFF;
conf->master_port->info.sample_rate = clock_rate;
conf->master_port->info.samples_per_frame = samples_per_frame;
conf->master_port->info.signature = 0;
conf->master_port->info.type = PJMEDIA_TYPE_AUDIO;
conf->master_port->get_frame = &get_frame;
conf->master_port->put_frame = &put_frame;
conf->master_port->user_data = conf;
/* Create port zero for sound device. */
status = create_sound_port(pool, conf);
@ -366,6 +455,27 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
if (status != PJ_SUCCESS)
return status;
/* If sound device was created, connect sound device to the
* master port.
*/
if (conf->snd_player) {
status = pjmedia_snd_port_connect( conf->snd_player,
conf->master_port );
if (status != PJ_SUCCESS) {
pjmedia_conf_destroy(conf);
return status;
}
}
if (conf->snd_rec) {
status = pjmedia_snd_port_connect( conf->snd_rec,
conf->master_port);
if (status != PJ_SUCCESS) {
pjmedia_conf_destroy(conf);
return status;
}
}
/* Done */
@ -376,92 +486,23 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
/*
* Create sound device
* Pause sound device.
*/
static pj_status_t create_sound( pjmedia_conf *conf )
static pj_status_t pause_sound( pjmedia_conf *conf )
{
pj_status_t status;
/* Open recorder only if mic is not disabled. */
if ((conf->options & PJMEDIA_CONF_NO_MIC) == 0) {
status = pjmedia_snd_open_recorder(-1, conf->clock_rate, 1,
conf->samples_per_frame,
conf->bits_per_sample,
&rec_cb, conf, &conf->snd_rec);
if (status != PJ_SUCCESS) {
conf->snd_rec = NULL;
return status;
}
}
/* Open player */
status = pjmedia_snd_open_player(-1, conf->clock_rate, 1,
conf->samples_per_frame,
conf->bits_per_sample,
&play_cb, conf, &conf->snd_player);
if (status != PJ_SUCCESS) {
if (conf->snd_rec) {
pjmedia_snd_stream_close(conf->snd_rec);
conf->snd_rec = NULL;
}
conf->snd_player = NULL;
return status;
}
/* Do nothing. */
PJ_UNUSED_ARG(conf);
return PJ_SUCCESS;
}
/*
* Destroy sound device
*/
static pj_status_t destroy_sound( pjmedia_conf *conf )
{
if (conf->snd_rec) {
pjmedia_snd_stream_close(conf->snd_rec);
conf->snd_rec = NULL;
}
if (conf->snd_player) {
pjmedia_snd_stream_close(conf->snd_player);
conf->snd_player = NULL;
}
return PJ_SUCCESS;
}
/*
* Activate sound device.
* Resume sound device.
*/
static pj_status_t resume_sound( pjmedia_conf *conf )
{
char errmsg[PJ_ERR_MSG_SIZE];
pj_status_t status;
if (conf->snd_player == NULL) {
status = create_sound(conf);
if (status != PJ_SUCCESS)
return status;
}
/* Start recorder. */
if (conf->snd_rec) {
status = pjmedia_snd_stream_start(conf->snd_rec);
if (status != PJ_SUCCESS)
goto on_error;
}
/* Start player. */
if (conf->snd_player) {
status = pjmedia_snd_stream_start(conf->snd_player);
if (status != PJ_SUCCESS)
goto on_error;
}
/* Do nothing. */
PJ_UNUSED_ARG(conf);
return PJ_SUCCESS;
on_error:
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error starting sound player/recorder: %s",
errmsg));
return status;
}
@ -472,14 +513,40 @@ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )
{
PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
//suspend_sound(conf);
destroy_sound(conf);
/* Destroy sound devices. */
if (conf->snd_rec) {
pjmedia_snd_port_destroy(conf->snd_rec);
conf->snd_rec = NULL;
}
if (conf->snd_player) {
pjmedia_snd_port_destroy(conf->snd_player);
conf->snd_player = NULL;
}
/* Destroy mutex */
pj_mutex_destroy(conf->mutex);
return PJ_SUCCESS;
}
/*
* Get port zero interface.
*/
PJ_DEF(pjmedia_port*) pjmedia_conf_get_master_port(pjmedia_conf *conf)
{
/* Sanity check. */
PJ_ASSERT_RETURN(conf != NULL, NULL);
/* Can only return port interface when PJMEDIA_CONF_NO_DEVICE was
* present in the option.
*/
PJ_ASSERT_RETURN((conf->options & PJMEDIA_CONF_NO_DEVICE) != 0, NULL);
return conf->master_port;
}
/*
* Add stream port to the conference bridge.
*/
@ -519,7 +586,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
pj_assert(index != conf->max_ports);
/* Create port structure. */
/* Create conf port structure. */
status = create_conf_port(pool, conf, strm_port, port_name, &conf_port);
if (status != PJ_SUCCESS) {
pj_mutex_unlock(conf->mutex);
@ -574,7 +641,8 @@ PJ_DECL(pj_status_t) pjmedia_conf_configure_port( pjmedia_conf *conf,
*/
PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
unsigned src_slot,
unsigned sink_slot )
unsigned sink_slot,
int level )
{
struct conf_port *src_port, *dst_port;
pj_bool_t start_sound = PJ_FALSE;
@ -587,6 +655,9 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf,
PJ_ASSERT_RETURN(conf->ports[src_slot] != NULL, PJ_EINVAL);
PJ_ASSERT_RETURN(conf->ports[sink_slot] != NULL, PJ_EINVAL);
/* For now, level MUST be zero. */
PJ_ASSERT_RETURN(level == 0, PJ_EINVAL);
pj_mutex_lock(conf->mutex);
src_port = conf->ports[src_slot];
@ -658,7 +729,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
pj_mutex_unlock(conf->mutex);
if (conf->connect_cnt == 0) {
destroy_sound(conf);
pause_sound(conf);
}
return PJ_SUCCESS;
@ -723,7 +794,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
/* Stop sound if there's no connection. */
if (conf->connect_cnt == 0) {
destroy_sound(conf);
pause_sound(conf);
}
return PJ_SUCCESS;
@ -779,6 +850,90 @@ PJ_DEF(pj_status_t) pjmedia_conf_get_ports_info(pjmedia_conf *conf,
}
/*
* Get signal level.
*/
PJ_DEF(pj_status_t) pjmedia_conf_get_signal_level( pjmedia_conf *conf,
unsigned slot,
unsigned *tx_level,
unsigned *rx_level)
{
struct conf_port *conf_port;
/* Check arguments */
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
/* Port must be valid. */
PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
conf_port = conf->ports[slot];
if (tx_level != NULL) {
conf_port->need_tx_level = 1;
*tx_level = conf_port->tx_level;
}
if (rx_level != NULL)
*rx_level = conf_port->rx_level;
return PJ_SUCCESS;
}
/*
* Adjust RX level of individual port.
*/
PJ_DEF(pj_status_t) pjmedia_conf_adjust_rx_level( pjmedia_conf *conf,
unsigned slot,
int adj_level )
{
struct conf_port *conf_port;
/* Check arguments */
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
/* Port must be valid. */
PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
/* Value must be from -128 to +127 */
PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
conf_port = conf->ports[slot];
/* Set normalized adjustment level. */
conf_port->rx_adj_level = adj_level + 128;
return PJ_SUCCESS;
}
/*
* Adjust TX level of individual port.
*/
PJ_DEF(pj_status_t) pjmedia_conf_adjust_tx_level( pjmedia_conf *conf,
unsigned slot,
int adj_level )
{
struct conf_port *conf_port;
/* Check arguments */
PJ_ASSERT_RETURN(conf && slot<conf->max_ports, PJ_EINVAL);
/* Port must be valid. */
PJ_ASSERT_RETURN(conf->ports[slot] != NULL, PJ_EINVAL);
/* Value must be from -128 to +127 */
PJ_ASSERT_RETURN(adj_level >= -128 && adj_level <= 127, PJ_EINVAL);
conf_port = conf->ports[slot];
/* Set normalized adjustment level. */
conf_port->tx_adj_level = adj_level + 128;
return PJ_SUCCESS;
}
/* Convert signed 16bit pcm sample to unsigned 16bit sample */
static pj_uint16_t pcm2unsigned(pj_int32_t pcm)
{
@ -924,19 +1079,74 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
if (cport->port)
pjmedia_port_put_frame(cport->port, &frame);
cport->tx_level = 0;
return PJ_SUCCESS;
} else if (cport->tx_setting != PJMEDIA_PORT_ENABLE) {
cport->tx_level = 0;
return PJ_SUCCESS;
}
/* If there are sources in the mix buffer, convert the mixed samples
* to the mixed samples itself. This is possible because mixed sample
* is 32bit.
*
* In addition to this process, if we need to change the level of
* TX signal, we adjust is here too.
*/
buf = (pj_int16_t*)cport->mix_buf;
for (j=0; j<conf->samples_per_frame; ++j) {
buf[j] = unsigned2pcm(cport->mix_buf[j] / cport->sources);
if (cport->tx_adj_level != 128) {
unsigned adj_level = cport->tx_adj_level;
/* We need to adjust signal level. */
for (j=0; j<conf->samples_per_frame; ++j) {
pj_int32_t itemp;
/* Calculate average level, and convert the sample to
* 16bit signed integer.
*/
itemp = unsigned2pcm(cport->mix_buf[j] / cport->sources);
/* Adjust the level */
itemp = itemp * adj_level / 128;
/* Clip the signal if it's too loud */
if (itemp > 32767) itemp = 32767;
else if (itemp < -32768) itemp = -32768;
/* Put back in the buffer. */
buf[j] = (pj_int16_t) itemp;
}
} else {
/* No need to adjust signal level. */
for (j=0; j<conf->samples_per_frame; ++j) {
buf[j] = unsigned2pcm(cport->mix_buf[j] / cport->sources);
}
}
/* Calculate TX level if we need to do so.
* This actually is not the most correct place to calculate TX signal
* level of the port; it should calculate the level of the actual
* frame just before put_frame() is called.
* But doing so would make the code more complicated than it is
* necessary, since the purpose of level calculation mostly is just
* for VU meter display. By doing it here, it should give the acceptable
* indication of the signal level of the port.
*/
if (cport->need_tx_level) {
pj_uint32_t level;
/* Get the signal level. */
level = pjmedia_calc_avg_signal(buf, conf->samples_per_frame);
/* Convert level to 8bit complement ulaw */
cport->tx_level = linear2ulaw(level) ^ 0xff;
} else {
cport->tx_level = 0;
}
/* If port has the same clock_date and samples_per_frame settings as
@ -1012,17 +1222,17 @@ static pj_status_t write_port(pjmedia_conf *conf, struct conf_port *cport,
/*
* Player callback.
*/
static pj_status_t play_cb( /* in */ void *user_data,
/* in */ pj_uint32_t timestamp,
/* out */ void *output,
/* out */ unsigned size)
static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame *frame)
{
pjmedia_conf *conf = user_data;
pjmedia_conf *conf = this_port->user_data;
unsigned ci, cj, i, j;
PJ_UNUSED_ARG(timestamp);
PJ_UNUSED_ARG(size);
/* Check that correct size is specified. */
pj_assert(frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8);
/* Must lock mutex (must we??) */
pj_mutex_lock(conf->mutex);
TRACE_(("p"));
@ -1056,16 +1266,20 @@ static pj_status_t play_cb( /* in */ void *user_data,
if (!conf_port)
continue;
/* Var "ci" is to count how many ports have been visited so far. */
++ci;
/* Skip if we're not allowed to receive from this port. */
if (conf_port->rx_setting == PJMEDIA_PORT_DISABLE) {
conf_port->rx_level = 0;
continue;
}
/* Also skip if this port doesn't have listeners. */
if (conf_port->listener_cnt == 0)
if (conf_port->listener_cnt == 0) {
conf_port->rx_level = 0;
continue;
}
/* Get frame from this port.
* For port zero (sound port), get the frame from the rx_buffer
@ -1082,18 +1296,13 @@ static pj_status_t play_cb( /* in */ void *user_data,
/* Skip if this port is muted/disabled. */
if (conf_port->rx_setting != PJMEDIA_PORT_ENABLE) {
continue;
}
/* Skip if no port is listening to the microphone */
if (conf_port->listener_cnt == 0) {
conf_port->rx_level = 0;
continue;
}
snd_buf = conf_port->snd_buf[conf_port->snd_read_pos];
for (j=0; j<conf->samples_per_frame; ++j) {
((pj_int16_t*)output)[j] = snd_buf[j];
((pj_int16_t*)frame->buf)[j] = snd_buf[j];
}
conf_port->snd_read_pos = (conf_port->snd_read_pos+1) % RX_BUF_COUNT;
@ -1102,7 +1311,7 @@ static pj_status_t play_cb( /* in */ void *user_data,
pj_status_t status;
pjmedia_frame_type frame_type;
status = read_port(conf, conf_port, output,
status = read_port(conf, conf_port, frame->buf,
conf->samples_per_frame, &frame_type);
if (status != PJ_SUCCESS) {
@ -1116,15 +1325,49 @@ static pj_status_t play_cb( /* in */ void *user_data,
}
}
/* Get the signal level. */
level = pjmedia_calc_avg_signal(output, conf->samples_per_frame);
/* If we need to adjust the RX level from this port, adjust the level
* and calculate the average level at the same time.
* Otherwise just calculate the averate level.
*/
if (conf_port->rx_adj_level != 128) {
pj_int16_t *input = frame->buf;
pj_int32_t adj = conf_port->rx_adj_level;
level = 0;
for (j=0; j<conf->samples_per_frame; ++j) {
pj_int32_t itemp;
/* For the level adjustment, we need to store the sample to
* a temporary 32bit integer value to avoid overflowing the
* 16bit sample storage.
*/
itemp = input[j];
itemp = itemp * adj / 128;
/* Clip the signal if it's too loud */
if (itemp > 32767) itemp = 32767;
else if (itemp < -32768) itemp = -32768;
input[j] = (pj_int16_t) itemp;
level += itemp;
}
level /= conf->samples_per_frame;
} else {
level = pjmedia_calc_avg_signal(frame->buf,
conf->samples_per_frame);
}
/* Convert level to 8bit complement ulaw */
level = linear2ulaw(level) ^ 0xff;
/* Put this level to port's last RX level. */
conf_port->rx_level = level;
/* Convert the buffer to unsigned 16bit value */
for (j=0; j<conf->samples_per_frame; ++j)
conf->uns_buf[j] = pcm2unsigned(((pj_int16_t*)output)[j]);
conf->uns_buf[j] = pcm2unsigned(((pj_int16_t*)frame->buf)[j]);
/* Add the signal to all listeners. */
for (j=0, cj=0;
@ -1142,6 +1385,7 @@ static pj_status_t play_cb( /* in */ void *user_data,
if (!conf_port->listeners[j])
continue;
/* Var "cj" is the number of listeners we have visited so far */
++cj;
/* Skip if this listener doesn't want to receive audio */
@ -1157,7 +1401,9 @@ static pj_status_t play_cb( /* in */ void *user_data,
}
}
/* For all ports, calculate avg signal. */
/* Time for all ports to transmit whetever they have in their
* buffer.
*/
for (i=0, ci=0; i<conf->max_ports && ci<conf->port_cnt; ++i) {
struct conf_port *conf_port = conf->ports[i];
pj_status_t status;
@ -1165,9 +1411,10 @@ static pj_status_t play_cb( /* in */ void *user_data,
if (!conf_port)
continue;
/* Var "ci" is to count how many ports have been visited. */
++ci;
status = write_port( conf, conf_port, timestamp);
status = write_port( conf, conf_port, frame->timestamp.u32.lo);
if (status != PJ_SUCCESS) {
PJ_LOG(4,(THIS_FILE, "Port %.*s put_frame() returned %d. "
"Port is now disabled",
@ -1181,10 +1428,10 @@ static pj_status_t play_cb( /* in */ void *user_data,
/* Return sound playback frame. */
if (conf->ports[0]->sources) {
copy_samples( output, (pj_int16_t*)conf->ports[0]->mix_buf,
copy_samples( frame->buf, (pj_int16_t*)conf->ports[0]->mix_buf,
conf->samples_per_frame);
} else {
zero_samples( output, conf->samples_per_frame );
zero_samples( frame->buf, conf->samples_per_frame );
}
pj_mutex_unlock(conf->mutex);
@ -1196,23 +1443,21 @@ static pj_status_t play_cb( /* in */ void *user_data,
/*
* Recorder callback.
*/
static pj_status_t rec_cb( /* in */ void *user_data,
/* in */ pj_uint32_t timestamp,
/* in */ const void *input,
/* in */ unsigned size)
static pj_status_t put_frame(pjmedia_port *this_port,
const pjmedia_frame *frame)
{
pjmedia_conf *conf = user_data;
pjmedia_conf *conf = this_port->user_data;
struct conf_port *snd_port = conf->ports[0];
const pj_int16_t *input = frame->buf;
pj_int16_t *target_snd_buf;
unsigned i;
PJ_UNUSED_ARG(timestamp);
TRACE_(("r"));
if (size != conf->samples_per_frame*2) {
TRACE_(("rxerr "));
}
/* Check for correct size. */
PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8,
PJMEDIA_ENCSAMPLESPFRAME);
/* Skip if this port is muted/disabled. */
if (snd_port->rx_setting != PJMEDIA_PORT_ENABLE) {

View File

@ -19,18 +19,38 @@
/*
* Based on:
* resample-1.2.tar.Z (from ftp://ccrma-ftp.stanford.edu/pub/NeXT)
* resample-1.8.tar.gz from the
* Digital Audio Resampling Home Page located at
* http://www-ccrma.stanford.edu/~jos/resample/.
*
* SOFTWARE FOR SAMPLING-RATE CONVERSION AND FIR DIGITAL FILTER DESIGN
*
* COPYING
* Snippet from the resample.1 man page:
*
* This software package is Copyright 1994 by Julius O. Smith
* (jos@ccrma.stanford.edu), all rights reserved. Permission to use and copy
* is granted subject to the terms of the "GNU Software General Public
* License" (see ftp://prep.ai.mit.edu/pub/gnu/COPYING). In addition, we
* request that a copy of any modified files be sent by email to
* jos@ccrma.stanford.edu so that we may incorporate them in the CCRMA
* version.
* HISTORY
*
* The first version of this software was written by Julius O. Smith III
* <jos@ccrma.stanford.edu> at CCRMA <http://www-ccrma.stanford.edu> in
* 1981. It was called SRCONV and was written in SAIL for PDP-10
* compatible machines. The algorithm was first published in
*
* Smith, Julius O. and Phil Gossett. ``A Flexible Sampling-Rate
* Conversion Method,'' Proceedings (2): 19.4.1-19.4.4, IEEE Conference
* on Acoustics, Speech, and Signal Processing, San Diego, March 1984.
*
* An expanded tutorial based on this paper is available at the Digital
* Audio Resampling Home Page given above.
*
* Circa 1988, the SRCONV program was translated from SAIL to C by
* Christopher Lee Fraley working with Roger Dannenberg at CMU.
*
* Since then, the C version has been maintained by jos.
*
* Sndlib support was added 6/99 by John Gibson <jgg9c@virginia.edu>.
*
* The resample program is free software distributed in accordance
* with the Lesser GNU Public License (LGPL). There is NO warranty; not
* even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
/* PJMEDIA modification:

View File

@ -812,7 +812,8 @@ static void ui_console_main(void)
if (menuin[1]=='c') {
status = pjmedia_conf_connect_port(pjsua.mconf,
atoi(src_port),
atoi(dst_port));
atoi(dst_port),
0);
} else {
status = pjmedia_conf_disconnect_port(pjsua.mconf,
atoi(src_port),

View File

@ -961,19 +961,19 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
{
pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot,
call->conf_slot);
call->conf_slot, 0);
} else if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
call->conf_slot);
call->conf_slot, 0);
} else if (pjsua.auto_conf) {
int i;
pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
for (i=0; i < pjsua.max_calls; ++i) {
@ -981,9 +981,9 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
continue;
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
pjsua.calls[i].conf_slot);
pjsua.calls[i].conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
call->conf_slot);
call->conf_slot, 0);
}
} else {
@ -991,8 +991,8 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
/* Connect new call to the sound device port (port zero) in the
* main conference bridge.
*/
pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
}