📄 conference.c
字号:
/* Calculate average level, and convert the sample to
* 16bit signed integer.
*/
itemp = unsigned2pcm(cport->mix_buf[j] / cport->src_level);
/* Adjust the level */
/*itemp = itemp * adj_level / NORMAL_LEVEL;*/
itemp = (itemp * adj_level) >> 7;
/* 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 if (cport->src_level) {
/* No need to adjust signal level. */
for (j=0; j<conf->samples_per_frame; ++j) {
buf[j] = unsigned2pcm(cport->mix_buf[j] / cport->src_level);
}
} else {
// Not necessarry. Buffer has been zeroed before.
// pjmedia_zero_samples(buf, conf->samples_per_frame);
//pj_assert(buf[0] == 0);
// This shouldn't happen. Function should've already bailed out when
// cport->src_level == 0.
pj_assert(0);
}
/* 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->src_cnt) {
cport->tx_level = cport->src_level / cport->src_cnt;
} else {
cport->tx_level = 0;
}
/* If port has the same clock_rate and samples_per_frame settings as
* the conference bridge, transmit the frame as is.
*/
if (cport->clock_rate == conf->clock_rate &&
cport->samples_per_frame == conf->samples_per_frame)
{
if (cport->port != NULL) {
pjmedia_frame frame;
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
frame.buf = (pj_int16_t*)cport->mix_buf;
frame.size = conf->samples_per_frame * BYTES_PER_SAMPLE;
/* No need to adjust timestamp, port has the same
* clock rate as conference bridge
*/
frame.timestamp = *timestamp;
TRACE_((THIS_FILE, "put_frame %.*s, count=%d",
(int)cport->name.slen, cport->name.ptr,
frame.size / BYTES_PER_SAMPLE));
return pjmedia_port_put_frame(cport->port, &frame);
} else
return PJ_SUCCESS;
}
/* If it has different clock_rate, must resample. */
if (cport->clock_rate != conf->clock_rate) {
unsigned dst_count;
pjmedia_resample_run( cport->tx_resample, buf,
cport->tx_buf + cport->tx_buf_count );
dst_count = (unsigned)(conf->samples_per_frame * 1.0 *
cport->clock_rate / conf->clock_rate);
cport->tx_buf_count += dst_count;
} else {
/* Same clock rate.
* Just copy the samples to tx_buffer.
*/
pjmedia_copy_samples( cport->tx_buf + cport->tx_buf_count,
buf, conf->samples_per_frame );
cport->tx_buf_count += conf->samples_per_frame;
}
/* Transmit while we have enough frame in the tx_buf. */
status = PJ_SUCCESS;
ts = 0;
while (cport->tx_buf_count >= cport->samples_per_frame &&
status == PJ_SUCCESS)
{
TRACE_((THIS_FILE, "write_port %.*s: count=%d",
(int)cport->name.slen, cport->name.ptr,
cport->samples_per_frame));
if (cport->port) {
pjmedia_frame frame;
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
frame.buf = cport->tx_buf;
frame.size = cport->samples_per_frame * BYTES_PER_SAMPLE;
/* Adjust timestamp as port may have different clock rate
* than the bridge.
*/
frame.timestamp.u64 = timestamp->u64 * cport->clock_rate /
conf->clock_rate;
/* Add timestamp for individual frame */
frame.timestamp.u64 += ts;
ts += cport->samples_per_frame;
TRACE_((THIS_FILE, "put_frame %.*s, count=%d",
(int)cport->name.slen, cport->name.ptr,
frame.size / BYTES_PER_SAMPLE));
status = pjmedia_port_put_frame(cport->port, &frame);
} else
status = PJ_SUCCESS;
cport->tx_buf_count -= cport->samples_per_frame;
if (cport->tx_buf_count) {
pjmedia_copy_samples(cport->tx_buf,
cport->tx_buf + cport->samples_per_frame,
cport->tx_buf_count);
}
TRACE_((THIS_FILE, " tx_buf count now is %d",
cport->tx_buf_count));
}
return status;
}
/*
* Player callback.
*/
static pj_status_t get_frame(pjmedia_port *this_port,
pjmedia_frame *frame)
{
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
pjmedia_frame_type speaker_frame_type = PJMEDIA_FRAME_TYPE_NONE;
unsigned ci, cj, i, j;
TRACE_((THIS_FILE, "- clock -"));
/* Check that correct size is specified. */
pj_assert(frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8);
/* Must lock mutex */
pj_mutex_lock(conf->mutex);
/* Reset port source count. We will only reset port's mix
* buffer when we have someone transmitting to it.
*/
for (i=0, ci=0; i<conf->max_ports && ci < conf->port_cnt; ++i) {
struct conf_port *conf_port = conf->ports[i];
/* Skip empty slot. */
if (!conf_port)
continue;
++ci;
/* Reset sources */
conf_port->src_level = 0;
conf_port->src_cnt = 0;
}
/* Get frames from all ports, and "mix" the signal
* to mix_buf of all listeners of the port.
*/
for (i=0, ci=0; i<conf->max_ports && ci<conf->port_cnt; ++i) {
struct conf_port *conf_port = conf->ports[i];
pj_int32_t level;
/* Skip empty port. */
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) {
conf_port->rx_level = 0;
continue;
}
/* Get frame from this port.
* For port zero (sound port) and passive ports, get the frame from
* the rx_buffer instead.
*/
if (conf_port->port == NULL) {
pj_int16_t *snd_buf;
if (conf_port->snd_read_pos == conf_port->snd_write_pos) {
conf_port->snd_read_pos =
(conf_port->snd_write_pos+RX_BUF_COUNT-RX_BUF_COUNT/2) %
RX_BUF_COUNT;
}
/* Skip if this port is muted/disabled. */
if (conf_port->rx_setting != PJMEDIA_PORT_ENABLE) {
conf_port->rx_level = 0;
continue;
}
snd_buf = conf_port->snd_buf[conf_port->snd_read_pos];
pjmedia_copy_samples((pj_int16_t*)frame->buf, snd_buf,
conf->samples_per_frame);
conf_port->snd_read_pos = (conf_port->snd_read_pos+1) % RX_BUF_COUNT;
} else {
pj_status_t status;
pjmedia_frame_type frame_type;
status = read_port(conf, conf_port, (pj_int16_t*)frame->buf,
conf->samples_per_frame, &frame_type);
if (status != PJ_SUCCESS) {
/* bennylp: why do we need this????
* Also see comments on similar issue with write_port().
PJ_LOG(4,(THIS_FILE, "Port %.*s get_frame() returned %d. "
"Port is now disabled",
(int)conf_port->name.slen,
conf_port->name.ptr,
status));
conf_port->rx_setting = PJMEDIA_PORT_DISABLE;
*/
continue;
}
/* Check that the port is not removed when we call get_frame() */
if (conf->ports[i] == NULL)
continue;
}
/* 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 != NORMAL_LEVEL) {
pj_int16_t *input = (pj_int16_t*) 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 / NORMAL_LEVEL;*/
itemp = (itemp * adj) >> 7;
/* 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;
if (itemp >=0 ) level += itemp;
else level -= itemp;
}
level /= conf->samples_per_frame;
} else {
level = pjmedia_calc_avg_signal((pj_int16_t*) frame->buf,
conf->samples_per_frame);
}
/* Apply simple AGC to the level, to avoid dramatic change in the
* level thus causing noise because the signal now is not aligned
* with the signal from the previous frame.
*/
if (level >= conf_port->last_level) {
level = (conf_port->last_level * ATTACK_A + level * ATTACK_B) /
(ATTACK_A + ATTACK_B);
} else {
level = (conf_port->last_level * DECAY_A + level * DECAY_B) /
(DECAY_A + DECAY_B);
}
conf_port->last_level = level;
/* Convert level to 8bit complement ulaw */
level = pjmedia_linear2ulaw(level) ^ 0xff;
/* Put this level to port's last RX level. */
conf_port->rx_level = level;
/* Skip processing frame if level is zero */
if (level == 0)
continue;
/* Convert the buffer to unsigned 16bit value */
for (j=0; j<conf->samples_per_frame; ++j)
conf->uns_buf[j] = pcm2unsigned(((pj_int16_t*)frame->buf)[j]);
/* Add the signal to all listeners. */
for (cj=0; cj < conf_port->listener_cnt; ++cj)
{
struct conf_port *listener;
pj_uint32_t *mix_buf;
unsigned k;
listener = conf->ports[conf_port->listener_slots[cj]];
/* Skip if this listener doesn't want to receive audio */
if (listener->tx_setting != PJMEDIA_PORT_ENABLE)
continue;
/* Mix the buffer. If this is the first source for target port,
* zero the mix buffer of target port first.
*/
mix_buf = listener->mix_buf;
if (listener->src_level == 0) {
pj_bzero( mix_buf, conf->samples_per_frame*sizeof(mix_buf[0]));
}
/* A little bit of optimization:
* When "conf_port" is the only transmitter to "listener",
* just add copy the frame directly from the original
* 16bit frame (avoiding unsigned2pcm() conversion).
* But write_port() needs to be aware of this trick!
*/
if (listener->transmitter_cnt == 1) {
pjmedia_copy_samples((pj_int16_t*)mix_buf,
(const pj_int16_t*)frame->buf,
conf->samples_per_frame);
listener->src_level = level;
} else {
for (k=0; k<conf->samples_per_frame; ++k)
mix_buf[k] += (conf->uns_buf[k] * level);
listener->src_level += level;
}
listener->src_cnt++;
}
}
/* 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];
pjmedia_frame_type frm_type;
pj_status_t status;
if (!conf_port)
continue;
/* Var "ci" is to count how many ports have been visited. */
++ci;
status = write_port( conf, conf_port, &frame->timestamp,
&frm_type);
if (status != PJ_SUCCESS) {
/* bennylp: why do we need this????
One thing for sure, put_frame()/write_port() may return
non-successfull status on Win32 if there's temporary glitch
on network interface, so disabling the port here does not
sound like a good idea.
PJ_LOG(4,(THIS_FILE, "Port %.*s put_frame() returned %d. "
"Port is now disabled",
(int)conf_port->name.slen,
conf_port->name.ptr,
status));
conf_port->tx_setting = PJMEDIA_PORT_DISABLE;
*/
continue;
}
/* Set the type of frame to be returned to sound playback
* device.
*/
if (i == 0)
speaker_frame_type = frm_type;
}
/* Return sound playback frame. */
if (conf->ports[0]->src_level) {
TRACE_((THIS_FILE, "write to audio, count=%d",
conf->samples_per_frame));
pjmedia_copy_samples( (pj_int16_t*)frame->buf,
(const pj_int16_t*)conf->ports[0]->mix_buf,
conf->samples_per_frame);
} else {
pjmedia_zero_samples((pj_int16_t*)frame->buf, conf->samples_per_frame);
}
/* MUST set frame type */
frame->type = speaker_frame_type;
pj_mutex_unlock(conf->mutex);
#ifdef REC_FILE
if (fhnd_rec == NULL)
fhnd_rec = fopen(REC_FILE, "wb");
if (fhnd_rec)
fwrite(frame->buf, frame->size, 1, fhnd_rec);
#endif
return PJ_SUCCESS;
}
/*
* get_frame() for passive port
*/
static pj_status_t get_frame_pasv(pjmedia_port *this_port,
pjmedia_frame *frame)
{
pj_assert(0);
PJ_UNUSED_ARG(this_port);
PJ_UNUSED_ARG(frame);
return -1;
}
/*
* Recorder callback.
*/
static pj_status_t put_frame(pjmedia_port *this_port,
const pjmedia_frame *frame)
{
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
struct conf_port *port = conf->ports[this_port->port_data.ldata];
const pj_int16_t *input = (const pj_int16_t*) frame->buf;
pj_int16_t *target_snd_buf;
/* 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 (port->rx_setting != PJMEDIA_PORT_ENABLE) {
return PJ_SUCCESS;
}
/* Skip if no port is listening to the microphone */
if (port->listener_cnt == 0) {
return PJ_SUCCESS;
}
/* Determine which rx_buffer to fill in */
target_snd_buf = port->snd_buf[port->snd_write_pos];
/* Copy samples from audio device to target rx_buffer */
pjmedia_copy_samples(target_snd_buf, input, conf->samples_per_frame);
/* Switch buffer */
port->snd_write_pos = (port->snd_write_pos+1)%RX_BUF_COUNT;
return PJ_SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -