/*
Copyright (C) 2005-2007 Michel de Boer <michel@twinklephone.com>
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 <iostream>
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include <sys/time.h>
#include <cc++/config.h>
#include "audio_rx.h"
#include "log.h"
#include "phone.h"
#include "rtp_telephone_event.h"
#include "userintf.h"
#include "line.h"
#include "sys_settings.h"
#include "sequence_number.h"
#include "audits/memman.h"
extern t_phone *phone;
#define SAMPLE_BUF_SIZE (audio_encoder->get_ptime() * audio_encoder->get_sample_rate()/1000 *\
AUDIO_SAMPLE_SIZE/8)
// Debug macro to print timestamp
#define DEBUG_TS(s) { gettimeofday(&debug_timer, NULL);\
cout << "DEBUG: ";\
cout << debug_timer.tv_sec * 1000 +\
debug_timer.tv_usec / 1000;\
cout << " " << (s) << endl;\
}
//////////
// PRIVATE
//////////
bool t_audio_rx::get_sound_samples(unsigned short &sound_payload_size, bool &silence) {
int status;
struct timespec sleeptimer;
struct timeval debug_timer;
silence = false;
mtx_3way.lock();
if (is_3way && !is_main_rx_3way) {
// We are not the main receiver in a 3-way call, so
// get the sound samples from the local media buffer.
// This buffer will be filled by the main receiver.
if (!media_3way_peer_rx->get(sample_buf, SAMPLE_BUF_SIZE)) {
// The mutex is unlocked before going to sleep.
// First I had the mutex unlock after the sleep.
// That worked fine with LinuxThreading, but it does
// not work with NPTL. It causes a deadlock when
// the main receiver calls post_media_peer_rx_3way
// as NPTL does not fair scheduling. This thread
// simly gets the lock again and the main receiver
// dies from starvation.
mtx_3way.unlock();
// There is not enough data yet. Sleep for 1 ms.
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 1000000;
nanosleep(&sleeptimer, NULL);
return false;
}
mtx_3way.unlock();
} else {
// Don't keep the 3way mutex locked while waiting for the DSP.
mtx_3way.unlock();
// Get the sound samples from the DSP
status = input_device->read(sample_buf, SAMPLE_BUF_SIZE);
if (status != SAMPLE_BUF_SIZE) {
if (!logged_capture_failure) {
// Log this failure only once
log_file->write_header("t_audio_rx::get_sound_samples",
LOG_NORMAL, LOG_WARNING);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": sound capture failed.\n");
log_file->write_raw("Status: ");
log_file->write_raw(status);
log_file->write_endl();
log_file->write_footer();
logged_capture_failure = true;
}
stop_running = true;
return false;
}
// If line is muted, then fill sample buffer with silence.
// Note that we keep reading the dsp, to prevent the DSP buffers
// from filling up.
if (get_line()->get_is_muted()) {
memset(sample_buf, 0, SAMPLE_BUF_SIZE);
}
}
// Convert buffer to a buffer of shorts as the samples are 16 bits
short *sb = (short *)sample_buf;
// Reduce noise
if (sys_config->get_au_reduce_noise_mic()) {
pcm_reduce_noise(sb, SAMPLE_BUF_SIZE / 2);
}
mtx_3way.lock();
if (is_3way) {
// Send the sound samples to the other receiver if we
// are the main receiver.
// There may be no other receiver when one of the far-ends
// has put the call on-hold.
if (is_main_rx_3way && peer_rx_3way) {
peer_rx_3way->post_media_peer_rx_3way(sample_buf, SAMPLE_BUF_SIZE,
audio_encoder->get_sample_rate());
}
// Mix the sound samples with the 3rd party
if (media_3way_peer_tx->get(mix_buf_3way, SAMPLE_BUF_SIZE)) {
short *mix_sb = (short *)mix_buf_3way;
for (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {
sb[i] = mix_linear_pcm(sb[i], mix_sb[i]);
}
}
}
mtx_3way.unlock();
// Encode sound samples
sound_payload_size = audio_encoder->encode(
sb, nsamples, payload, payload_size, silence);
return true;
}
bool t_audio_rx::get_dtmf_event(void) {
// DTMF events are not supported in a 3-way conference
if (is_3way) return false;
if (!sema_dtmf_q.try_down()) {
// No DTMF event available
return false;
}
// Get next DTMF event
mtx_dtmf_q.lock();
t_dtmf_event dtmf_event = dtmf_queue.front();
dtmf_queue.pop();
mtx_dtmf_q.unlock();
ui->cb_async_send_dtmf(get_line()->get_line_number(), dtmf_event.dtmf_tone);
// Create DTMF player
if (dtmf_event.inband) {
dtmf_player = new t_inband_dtmf_player(this, audio_encoder, user_config,
dtmf_event.dtmf_tone, timestamp, nsamples);
MEMMAN_NEW(dtmf_player);
// Log DTMF event
log_file->write_header("t_audio_rx::get_dtmf_event", LOG_NORMAL);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": start inband DTMF tone - ");
log_file->write_raw(dtmf_event.dtmf_tone);
log_file->write_endl();
log_file->write_footer();
} else {
// The telephone events may have a different sampling rate than
// the audio codec. Change nsamples accordingly.
nsamples = audio_sample_rate(CODEC_TELEPHONE_EVENT)/1000 *
audio_encoder->get_ptime();
dtmf_player = new t_rtp_event_dtmf_player(this, audio_encoder, user_config,
dtmf_event.dtmf_tone, timestamp, nsamples);
MEMMAN_NEW(dtmf_player);
// Log DTMF event
log_file->write_header("t_audio_rx::get_dtmf_event", LOG_NORMAL);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": start DTMF event - ");
log_file->write_raw(dtmf_event.dtmf_tone);
log_file->write_endl();
log_file->write_raw("Payload type: ");
log_file->write_raw(pt_telephone_event);
log_file->write_endl();
log_file->write_footer();
// Set RTP payload format
// HACK: the sample rate for telephone events is 8000, but the
// ccRTP stack does not handle it well when the sample rate
// changes. When the sample rate of the audio codec is kept
// on the ccRTP session settings, then all works fine.
rtp_session->setPayloadFormat(DynamicPayloadFormat(pt_telephone_event,
audio_encoder->get_sample_rate()));
// should be this: audio_sample_rate(CODEC_TELEPHONE_EVENT)
// As all RTP event contain the same timestamp, the ccRTP stack will
// discard packets when the timestamp gets to old.
// Increase the expire timeout value to prevent this.
rtp_session->setExpireTimeout((JITTER_BUF_MS +
user_config->get_dtmf_duration() + user_config->get_dtmf_pause()) * 1000);
}
return true;
}
void t_audio_rx::set_sound_payload_format(void) {
nsamples = audio_encoder->get_sample_rate()/1000 * audio_encoder->get_ptime();
rtp_session->setPayloadFormat(DynamicPayloadFormat(audio_encoder->get_payload_id(),
audio_encoder->get_sample_rate()));
}
//////////
// PUBLIC
//////////
t_audio_rx::t_audio_rx(t_audio_session *_audio_session,
t_audio_io *_input_device, t_twinkle_rtp_session *_rtp_session,
t_audio_codec _codec, unsigned short _payload_id,
unsigned short _ptime) : sema_dtmf_q(0)
{
audio_session = _audio_session;
user_config = audio_session->get_line()->get_user();
assert(user_config);
input_device = _input_device;
rtp_session = _rtp_session;
dtmf_player = NULL;
is_running = false;
stop_running = false;
logged_capture_failure = false;
use_nat_keepalive = phone->use_nat_keepalive(user_config);
pt_telephone_event = -1;
// Create audio encoder
switch (_codec) {
case CODEC_G711_ALAW:
audio_encoder = new t_g711a_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G711_ULAW:
audio_encoder = new t_g711u_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_GSM:
audio_encoder = new t_gsm_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
#ifdef HAVE_SPEEX
case CODEC_SPEEX_NB:
audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
t_speex_audio_encoder::MODE_NB, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_SPEEX_WB:
audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
t_speex_audio_encoder::MODE_WB, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_SPEEX_UWB:
audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
t_speex_audio_encoder::MODE_UWB, user_config);
MEMMAN_NEW(audio_encoder);
break;
#endif
#ifdef HAVE_ILBC
case CODEC_ILBC:
audio_encoder = new t_ilbc_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
#endif
case CODEC_G726_16:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_16, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G726_24:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_24, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G726_32:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_32, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G726_40:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_40, user_config);
MEMMAN_NEW(audio_encoder);
break;
default:
assert(false);
}
payload_size = audio_encoder->get_max_payload_size();
sample_buf = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(sample_buf);
payload = new unsigned char[payload_size];
MEMMAN_NEW_ARRAY(payload);
nsamples = audio_encoder->get_sample_rate()/1000 * audio_encoder->get_ptime();
// Initialize 3-way settings to 'null'
media_3way_peer_tx = NULL;
media_3way_peer_rx = NULL;
peer_rx_3way = NULL;
mix_buf_3way = NULL;
is_3way = false;
is_main_rx_3way = false;
}
t_audio_rx::~t_audio_rx() {
struct timespec sleeptimer;
if (is_running) {
stop_running = true;
do {
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 10000000;
nanosleep(&sleeptimer, NULL);
} while (is_running);
}
MEMMAN_DELETE_ARRAY(sample_buf);
delete [] sample_buf;
MEMMAN_DELETE_ARRAY(payload);
delete [] payload;
MEMMAN_DELETE(audio_encoder);
delete audio_encoder;
// Clean up resources for 3-way conference calls
if (media_3way_peer_tx) {
MEMMAN_DELETE(media_3way_peer_tx);
delete media_3way_peer_tx;
}
if (media_3way_peer_rx) {
MEMMAN_DELETE(media_3way_peer_rx);
delete media_3way_peer_rx;
}
if (mix_buf_3way) {
MEMMAN_DELETE_ARRAY(mix_buf_3way);
delete [] mix_buf_3way;
}
if (dtmf_player) {
MEMMAN_DELETE(dtmf_player);
delete dtmf_player;
}
}
void t_audio_rx::set_running(bool running) {
is_running = running;
}
// NOTE: no operations on the phone object are allowed inside the run() method.
// Such an operation needs a lock on the transaction layer. The destructor
// on audio_rx is called while this lock is locked. The destructor waits
// in a busy loop for the run() method to finish. If the run() method would
// need the phone lock, this would lead to a dead lock (and a long trip
// in debug hell!)
void t_audio_rx::run(void) {
int status;
struct timespec sleeptimer;
struct timeval debug_timer;
unsigned short sound_payload_size;
uint32 dtmf_rtp_timestamp;
phone->add_prohibited_thread();
ui->add_prohibited_thread();
// This flag indicates if we are currently in a silence period.
// The start of a new stream is assumed to start in silence, such
// that the very first RTP packet will be marked.
bool silence_period = true;
uint64 silence_nsamples = 0; // duration in samples
// This flag indicates if a sound frame can be suppressed
bool suppress_samples = false;
// The running flag is set already in t_audio_session::run to prevent
// a crash when the thread gets destroyed before it starts running.
// is_running = true;
// For a 3-way conference only the main receiver has access
// to the dsp.
if (!is_3way || is_main_rx_3way) {
// Enable recording
if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
sys_config->get_dev_mic()))
{
input_device->enable(true, true);
} else {
input_device->enable(false, true);
}
// If the stream is stopped for call-hold, then the buffer might
// be filled with old sound samples.
input_device->flush(false, true);
}
// Synchronize the timestamp driven by the sampling rate
// of the recording with the timestamp of the RTP session.
// As the RTP session is already created in advance, the
// RTP clock is a bit ahead already.
timestamp = rtp_session->getCurrentTimestamp() + nsamples;
// This loop keeps running until the stop_running flag is set to true.
// When a call is being released the stop_running flag is set to true.
// At that moment the lock on the transaction layer (phone) is taken.
// So do not use operations that take the phone lock, otherwise a
// dead lock may occur during call release.
while (true) {
if (stop_running) break;
if (dtmf_player) {
rtp_session->setMark(false);
// Skip samples from sound card
input_device->read(sample_buf, SAMPLE_BUF_SIZE);
sound_payload_size = dtmf_player->get_payload(
payload, payload_size, timestamp, dtmf_rtp_timestamp);
silence_period = false;
} else if (get_dtmf_event()) {
// RFC 2833
// Set marker in first RTP packet of a DTMF event
rtp_session->setMark(true);
// Skip samples from sound card
input_device->read(sample_buf, SAMPLE_BUF_SIZE);
assert(dtmf_player);
sound_payload_size = dtmf_player->get_payload(
payload, payload_size, timestamp, dtmf_rtp_timestamp);
silence_period = false;
} else if (get_sound_samples(sound_payload_size, suppress_samples)) {
if (suppress_samples && use_nat_keepalive) {
if (!silence_period) silence_nsamples = 0;
// Send a silence packet at the NAT keep alive interval
// to keep the NAT bindings for RTP fresh.
silence_nsamples += SAMPLE_BUF_SIZE / 2;
if (silence_nsamples >
user_config->get_timer_nat_keepalive() * 1000 *
audio_encoder->get_sample_rate())
{
suppress_samples = false;
}
}
if (silence_period && !suppress_samples) {
// RFC 3551 4.1
// Set marker bit in first RTP packet after silence
rtp_session->setMark(true);
} else {
rtp_session->setMark(false);
}
silence_period = suppress_samples;
} else {
continue;
}
// If timestamp is more than 1 payload size ahead of the clock of
// the ccRTP stack, then drop the current payload and do not advance
// the timestamp. This will happen if the DSP delivers more
// sound samples than the set sample rate. To compensate for this
// samples must be dropped.
uint32 current_timestamp = rtp_session->getCurrentTimestamp();
if (seq32_t(timestamp) <= seq32_t(current_timestamp + nsamples)) {
if (dtmf_player) {
// Send DTMF payload
rtp_session->putData(dtmf_rtp_timestamp, payload,
sound_payload_size);
// If DTMF has ended then set payload back to sound
if (dtmf_player->finished()) {
set_sound_payload_format();
MEMMAN_DELETE(dtmf_player);
delete dtmf_player;
dtmf_player = NULL;
}
} else if (!suppress_samples) {
// Send sound samples
// Set the expire timeout to the jitter buffer size.
// This allows for old packets still to be sent out.
rtp_session->setExpireTimeout(MAX_OUT_AUDIO_DELAY_MS * 1000);
rtp_session->putData(timestamp, payload, sound_payload_size);
}
timestamp += nsamples;
} else {
log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": discarded surplus of sound samples.\n");
log_file->write_raw("Timestamp: ");
log_file->write_raw(timestamp);
log_file->write_endl();
log_file->write_raw("Current timestamp: ");
log_file->write_raw(current_timestamp);
log_file->write_endl();
log_file->write_raw("nsamples: ");
log_file->write_raw(nsamples);
log_file->write_endl();
log_file->write_footer();
}
// If there is enough data in the DSP buffers to fill another
// RTP packet then do not sleep, but immediately go to the
// next cycle to play out the data. Probably this thread did
// not get enough time, so the buffer filled up. The far end
// jitter buffer has to cope with the jitter caused by this.
if (is_3way && !is_main_rx_3way) {
if (media_3way_peer_rx->size_content() >= SAMPLE_BUF_SIZE) {
continue;
}
} else {
if (input_device->get_buffer_space(true) >= SAMPLE_BUF_SIZE) continue;
}
// There is no data left in the DSP buffers to play out anymore.
// So the timestamp must be in sync with the clock of the ccRTP
// stack. It might get behind if the sound cards samples a bit
// slower than the set sample rate. Advance the timestamp to get
// in sync again.
current_timestamp = rtp_session->getCurrentTimestamp();
if (seq32_t(timestamp) <= seq32_t(current_timestamp -
(JITTER_BUF_MS / audio_encoder->get_ptime()) * nsamples))
{
timestamp += nsamples * (JITTER_BUF_MS / audio_encoder->get_ptime());
log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": timestamp forwarded by ");
log_file->write_raw(nsamples * (JITTER_BUF_MS /
audio_encoder->get_ptime()));
log_file->write_endl();
log_file->write_raw("Timestamp: ");
log_file->write_raw(timestamp);
log_file->write_endl();
log_file->write_raw("Current timestamp: ");
log_file->write_raw(current_timestamp);
log_file->write_endl();
log_file->write_raw("nsamples: ");
log_file->write_raw(nsamples);
log_file->write_endl();
log_file->write_footer();
}
}
phone->remove_prohibited_thread();
ui->remove_prohibited_thread();
is_running = false;
}
void t_audio_rx::set_pt_telephone_event(int pt) {
pt_telephone_event = pt;
}
void t_audio_rx::push_dtmf(char digit, bool inband) {
// Ignore invalid DTMF digits
if (!VALID_DTMF_SYM(digit)) return;
// Ignore DTMF tones in a 3-way conference
if (is_3way) return;
t_dtmf_event dtmf_event;
dtmf_event.dtmf_tone = char2dtmf_ev(digit);
dtmf_event.inband = inband;
mtx_dtmf_q.lock();
dtmf_queue.push(dtmf_event);
mtx_dtmf_q.unlock();
sema_dtmf_q.up();
}
t_line *t_audio_rx::get_line(void) const {
return audio_session->get_line();
}
void t_audio_rx::join_3way(bool main_rx, t_audio_rx *peer_rx) {
mtx_3way.lock();
if (is_3way) {
log_file->write_header("t_audio_rx::join_3way", LOG_NORMAL);
log_file->write_raw("ERROR: audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(" - 3way is already active.\n");
log_file->write_footer();
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::join_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": join 3-way.\n");
if (main_rx) {
log_file->write_raw("Role is: mixer.\n");
} else {
log_file->write_raw("Role is: non-mixing.\n");
}
if (peer_rx) {
log_file->write_raw("A peer receiver already exists.\n");
} else {
log_file->write_raw("A peer receiver does not exist.\n");
}
log_file->write_footer();
// Create media buffers for the 2 far-ends of a 3-way call.
// The size of the media buffer is the size of the jitter buffer.
// This allows for jitter in the RTP streams and also for
// incompatible payload sizes. Eg. 1 far-end may send 20ms paylaods,
// while the other sends 30ms payloads. The outgoing RTP stream might
// even have another payload size.
// When the data has been captured from the soundcard, it will be
// checked if there is enough data available in the media buffers, i.e.
// the same amount of data as captured from the soundcard for mixing.
// If there is it will be retrieved and mixed.
// If there isn't the captured sound will simply be sent on its own
// to the far-end. Meanwhile the buffer will fill up with data such
// that from the next captured sample there will be sufficient data
// for mixing.
media_3way_peer_tx = new t_media_buffer(
JITTER_BUF_SIZE(audio_encoder->get_sample_rate()));
MEMMAN_NEW(media_3way_peer_tx);
media_3way_peer_rx = new t_media_buffer(
JITTER_BUF_SIZE(audio_encoder->get_sample_rate()));
MEMMAN_NEW(media_3way_peer_rx);
// Create a mix buffer for one sample frame.
mix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(mix_buf_3way);
peer_rx_3way = peer_rx;
is_3way = true;
is_main_rx_3way = main_rx;
// Stop DTMF tones as these are not supported in a 3way
if (dtmf_player) {
MEMMAN_DELETE(dtmf_player);
delete dtmf_player;
dtmf_player = NULL;
}
mtx_3way.unlock();
}
void t_audio_rx::set_peer_rx_3way(t_audio_rx *peer_rx) {
mtx_3way.lock();
if (!is_3way) {
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::set_peer_rx_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
if (peer_rx) {
log_file->write_raw(": set peer receiver.\n");
} else {
log_file->write_raw(": erase peer receiver.\n");
}
if (is_main_rx_3way) {
log_file->write_raw("Role is: mixer.\n");
} else {
log_file->write_raw("Role is: non-mixing.\n");
}
log_file->write_footer();
peer_rx_3way = peer_rx;
mtx_3way.unlock();
}
void t_audio_rx::set_main_rx_3way(bool main_rx) {
mtx_3way.lock();
if (!is_3way) {
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::set_main_rx_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
if (main_rx) {
log_file->write_raw(": change role to: mixer.\n");
} else {
log_file->write_raw(": change role to: non-mixing.\n");
}
log_file->write_footer();
// Initialize the DSP if we become the mixer and we were not before
if (main_rx && !is_main_rx_3way) {
// Enable recording
int arg;
if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
sys_config->get_dev_mic()))
{
input_device->enable(true, true);
} else {
input_device->enable(false, true);
}
// If the stream is stopped for call-hold, then the buffer might
// be filled with old sound samples.
input_device->flush(false, true);
}
is_main_rx_3way = main_rx;
mtx_3way.unlock();
}
void t_audio_rx::stop_3way(void) {
mtx_3way.lock();
if (!is_3way) {
log_file->write_header("t_audio_rx::stop_3way");
log_file->write_raw("ERROR: audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(" - 3way is not active.\n");
log_file->write_footer();
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::stop_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": stop 3-way.\n");
log_file->write_footer();
is_3way = false;
is_main_rx_3way = false;
peer_rx_3way = NULL;
MEMMAN_DELETE(media_3way_peer_tx);
delete media_3way_peer_tx;
media_3way_peer_tx = NULL;
MEMMAN_DELETE(media_3way_peer_rx);
delete media_3way_peer_rx;
media_3way_peer_rx = NULL;
MEMMAN_DELETE_ARRAY(mix_buf_3way);
delete [] mix_buf_3way;
mix_buf_3way = NULL;
mtx_3way.unlock();
}
void t_audio_rx::post_media_peer_tx_3way(unsigned char *media, int len,
unsigned short peer_sample_rate)
{
mtx_3way.lock();
if (!is_3way) {
// This is not a 3-way call. This is not necessarily an
// error condition. The 3rd party may be in the process of
// leaving the conference.
// Simply discard the posted media
mtx_3way.unlock();
return;
}
if (peer_sample_rate != audio_encoder->get_sample_rate()) {
// Resample media from peer to sample rate of this receiver
int output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;
short *output_buf = new short[output_len];
MEMMAN_NEW_ARRAY(output_buf);
int resample_len = resample((short *)media, len / 2, peer_sample_rate,
output_buf, output_len, audio_encoder->get_sample_rate());
media_3way_peer_tx->add((unsigned char *)output_buf, resample_len * 2);
MEMMAN_DELETE_ARRAY(output_buf);
delete [] output_buf;
} else {
media_3way_peer_tx->add(media, len);
}
mtx_3way.unlock();
}
void t_audio_rx::post_media_peer_rx_3way(unsigned char *media, int len,
unsigned short peer_sample_rate)
{
mtx_3way.lock();
if (!is_3way) {
// This is not a 3-way call. This is not necessarily an
// error condition. The 3rd party may be in the process of
// leaving the conference.
// Simply discard the posted media
mtx_3way.unlock();
return;
}
if (peer_sample_rate != audio_encoder->get_sample_rate()) {
// Resample media from peer to sample rate of this receiver
int output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;
short *output_buf = new short[output_len];
MEMMAN_NEW_ARRAY(output_buf);
int resample_len = resample((short *)media, len / 2, peer_sample_rate,
output_buf, output_len, audio_encoder->get_sample_rate());
media_3way_peer_rx->add((unsigned char *)output_buf, resample_len * 2);
MEMMAN_DELETE_ARRAY(output_buf);
delete [] output_buf;
} else {
media_3way_peer_rx->add(media, len);
}
mtx_3way.unlock();
}
bool t_audio_rx::get_is_main_rx_3way(void) const {
return is_main_rx_3way;
}
syntax highlighted by Code2HTML, v. 0.9.1