/*
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 <assert.h>
#include <iostream>
#include <cstdio>
#include <ctime>
#include <sys/types.h>
#include <sys/time.h>
#include "audio_tx.h"
#include "log.h"
#include "phone.h"
#include "userintf.h"
#include "util.h"
#include "line.h"
#include "sequence_number.h"
#include "audits/memman.h"
extern t_phone *phone;
#define SAMPLE_BUF_SIZE (MAX_PTIME * sc_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 << ":" << debug_timer.tv_sec * 1000 + debug_timer.tv_usec / 1000 - (debug_timer_prev.tv_sec * 1000 + debug_timer_prev.tv_usec / 1000);\
cout << " " << (s) << endl;\
debug_timer_prev = debug_timer;\
}
//////////
// PUBLIC
//////////
t_audio_tx::t_audio_tx(t_audio_session *_audio_session,
t_audio_io *_playback_device, t_twinkle_rtp_session *_rtp_session,
t_audio_codec _codec,
const map<unsigned short, t_audio_codec> &_payload2codec,
unsigned short _ptime)
{
audio_session = _audio_session;
user_config = audio_session->get_line()->get_user();
assert(user_config);
playback_device = _playback_device;
rtp_session = _rtp_session;
codec = _codec;
sc_sample_rate = audio_sample_rate(_codec);
payload2codec = _payload2codec;
is_running = false;
stop_running = false;
// Create audio decoders
map_audio_decoder[CODEC_G711_ALAW] = new t_g711a_audio_decoder(_ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_G711_ALAW]);
map_audio_decoder[CODEC_G711_ULAW] = new t_g711u_audio_decoder(_ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_G711_ULAW]);
map_audio_decoder[CODEC_GSM] = new t_gsm_audio_decoder(user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_GSM]);
#ifdef HAVE_SPEEX
map_audio_decoder[CODEC_SPEEX_NB] = new t_speex_audio_decoder(
t_speex_audio_decoder::MODE_NB, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_NB]);
map_audio_decoder[CODEC_SPEEX_WB] = new t_speex_audio_decoder(
t_speex_audio_decoder::MODE_WB, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_WB]);
map_audio_decoder[CODEC_SPEEX_UWB] = new t_speex_audio_decoder(
t_speex_audio_decoder::MODE_UWB, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_SPEEX_UWB]);
#endif
#ifdef HAVE_ILBC
map_audio_decoder[CODEC_ILBC] = new t_ilbc_audio_decoder(_ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_ILBC]);
#endif
map_audio_decoder[CODEC_G726_16] = new t_g726_audio_decoder(
t_g726_audio_decoder::BIT_RATE_16, _ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_G726_16]);
map_audio_decoder[CODEC_G726_24] = new t_g726_audio_decoder(
t_g726_audio_decoder::BIT_RATE_24, _ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_G726_24]);
map_audio_decoder[CODEC_G726_32] = new t_g726_audio_decoder(
t_g726_audio_decoder::BIT_RATE_32, _ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_G726_32]);
map_audio_decoder[CODEC_G726_40] = new t_g726_audio_decoder(
t_g726_audio_decoder::BIT_RATE_40, _ptime, user_config);
MEMMAN_NEW(map_audio_decoder[CODEC_G726_40]);
ptime = map_audio_decoder[codec]->get_default_ptime();
sample_buf = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(sample_buf);
// Create concealment buffers
for (int i = 0; i < MAX_CONCEALMENT; i++) {
conceal_buf[i] = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(conceal_buf[i]);
conceal_buflen[i] = 0;
}
conceal_num = 0;
conceal_pos = 0;
// Initialize jitter buffer
jitter_buf = new unsigned char[JITTER_BUF_SIZE(sc_sample_rate)];
MEMMAN_NEW_ARRAY(jitter_buf);
jitter_buf_len = 0;
load_jitter_buf = true;
soundcard_buf_size = playback_device->get_buffer_size(false);
// Initialize 3-way settings
is_3way = false;
is_3way_mixer = false;
media_3way_peer_tx = NULL;
peer_tx_3way = NULL;
peer_rx_3way = NULL;
mix_buf_3way = NULL;
// Initialize telephone event settings
pt_telephone_event = -1;
pt_telephone_event_alt = 1;
}
t_audio_tx::~t_audio_tx() {
struct timespec sleeptimer;
if (is_running) {
stop_running = true;
do {
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 10000000;
nanosleep(&sleeptimer, NULL);
continue;
} while (is_running);
}
MEMMAN_DELETE_ARRAY(sample_buf);
delete [] sample_buf;
MEMMAN_DELETE_ARRAY(jitter_buf);
delete [] jitter_buf;
for (int i = 0; i < MAX_CONCEALMENT; i++) {
MEMMAN_DELETE_ARRAY(conceal_buf[i]);
delete [] conceal_buf[i];
}
// Destroy audio decoders
for (map<t_audio_codec, t_audio_decoder *>::iterator i = map_audio_decoder.begin();
i != map_audio_decoder.end(); i++)
{
MEMMAN_DELETE(i->second);
delete i->second;
}
// Cleanup 3-way resources
if (media_3way_peer_tx) {
MEMMAN_DELETE(media_3way_peer_tx);
delete media_3way_peer_tx;
}
if (mix_buf_3way) {
MEMMAN_DELETE_ARRAY(mix_buf_3way);
delete [] mix_buf_3way;
}
}
void t_audio_tx::retain_for_concealment(unsigned char *buf, unsigned short len) {
if (conceal_num == 0) {
memcpy(conceal_buf[0], buf, len);
conceal_buflen[0] = len;
conceal_num = 1;
conceal_pos = 0;
return;
}
if (conceal_num < MAX_CONCEALMENT) {
memcpy(conceal_buf[conceal_num], buf, len);
conceal_buflen[conceal_num] = len;
conceal_num++;
return;
}
memcpy(conceal_buf[conceal_pos], buf, len);
conceal_buflen[conceal_pos] = len;
conceal_pos = (conceal_pos + 1) % MAX_CONCEALMENT;
}
void t_audio_tx::conceal(short num) {
// Some codecs have a PLC.
// Only use this PLC is the sound card sample rate equals the codec
// sample rate. If they differ, then we should resample the codec
// samples. As this should be a rare case, we are lazy here. In
// this rare case, use Twinkle's low-tech PLC.
if (map_audio_decoder[codec]->has_plc() && audio_sample_rate(codec) == sc_sample_rate) {
short *sb = (short *)sample_buf;
for (int i = 0; i < num; i++) {
int nsamples;
nsamples = map_audio_decoder[codec]->conceal(sb, SAMPLE_BUF_SIZE);
if (nsamples > 0) {
play_pcm(sample_buf, nsamples * 2);
}
}
return;
}
// Replay previous packets for other codecs
short i = (conceal_pos + (MAX_CONCEALMENT - num)) % MAX_CONCEALMENT;
if (i >= conceal_pos) {
for (int j = i; j < MAX_CONCEALMENT; j++) {
play_pcm(conceal_buf[j], conceal_buflen[j]);
}
for (int j = 0; j < conceal_pos; j++) {
play_pcm(conceal_buf[j], conceal_buflen[j]);
}
} else {
for (int j = i; j < conceal_pos; j++) {
play_pcm(conceal_buf[j], conceal_buflen[j]);
}
}
}
void t_audio_tx::clear_conceal_buf(void) {
conceal_pos = 0;
conceal_num = 0;
}
void t_audio_tx::play_pcm(unsigned char *buf, unsigned short len, bool only_3rd_party) {
int status;
struct timeval debug_timer, debug_timer_prev;
unsigned char *playbuf = buf;
// If there is only sound from the 3rd party in a 3-way, then check
// if there is still enough sound in the buffer of the DSP to be
// played. If not, then play out the sound from the 3rd party only.
if (only_3rd_party) {
if (playback_device->get_buffer_space(false) < soundcard_buf_size - len) {
// There is still sound in the DSP buffers to be
// played, so let's wait. Maybe in the next cycle
// an RTP packet from the far-end will be received.
return;
}
}
// If we are in a 3-way then send the samples to the peer audio
// receiver for mixing
if (!only_3rd_party && is_3way && peer_rx_3way) {
peer_rx_3way->post_media_peer_tx_3way(buf, len, sc_sample_rate);
}
// If we are in a 3-way conference and we are not the mixer then
// send the sound samples to the mixer
if (is_3way && !is_3way_mixer) {
if (peer_tx_3way) {
peer_tx_3way->post_media_peer_tx_3way(buf, len, sc_sample_rate);
return;
} else {
// There is no peer.
return;
}
}
// Mix audio for 3-way conference
if (is_3way && is_3way_mixer) {
if (media_3way_peer_tx->get(mix_buf_3way, len)) {
short *mix_sb = (short *)mix_buf_3way;
short *sb = (short *)buf;
for (int i = 0; i < len / 2; i++) {
mix_sb[i] = mix_linear_pcm(sb[i], mix_sb[i]);
}
playbuf = mix_buf_3way;
}
}
// Fill jitter buffer before playing
if (load_jitter_buf) {
if (jitter_buf_len + len < JITTER_BUF_SIZE(sc_sample_rate)) {
memcpy(jitter_buf + jitter_buf_len, playbuf, len);
jitter_buf_len += len;
} else {
// Write the contents of the jitter buffer to the DSP.
// The buffers in the DSP will now function as jitter
// buffer.
status = playback_device->write(jitter_buf, jitter_buf_len);
if (status != jitter_buf_len) {
string msg("Writing to dsp failed: ");
msg += get_error_str(errno);
log_file->write_report(msg, "t_audio_tx::play_pcm",
LOG_NORMAL, LOG_CRITICAL);
}
// Write passed sound samples to DSP.
status = playback_device->write(playbuf, len);
if (status != len) {
string msg("Writing to dsp failed: ");
msg += get_error_str(errno);
log_file->write_report(msg, "t_audio_tx::play_pcm",
LOG_NORMAL, LOG_CRITICAL);
}
load_jitter_buf = false;
}
return;
}
// If buffer on soundcard is empty, then the jitter buffer needs
// to be refilled. This should only occur when no RTP packets
// have been received for a while (silence suppression or packet loss)
int bufferspace = playback_device->get_buffer_space(false);
if (bufferspace == soundcard_buf_size && len <= JITTER_BUF_SIZE(sc_sample_rate)) {
memcpy(jitter_buf, playbuf, len);
jitter_buf_len = len;
load_jitter_buf = true;
log_file->write_header("t_audio_tx::play_pcm", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": jitter buffer empty.\n");
log_file->write_footer();
return;
}
// If the play-out buffer contains the maximum number of
// packets then start skipping packets to prevent
// unacceptable delay.
// This can only happen if the thread did not get
// processing time for a while and RTP packets start to
// pile up.
// Or if a soundcard plays out the samples at just less then
// the requested sample rate.
/* Not needed anymore, the ::run loop already discards incoming RTP packets
with a late timestamp. This seems to solve the slow soundcard problem
better. The solution below caused annoying ticks in the playout.
if (soundcard_buf_size - bufferspace > JITTER_BUF_SIZE + len) {
log_file->write_header("t_audio_tx::play_pcm", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": jitter buffer overflow: ");
log_file->write_raw(bufferspace);
log_file->write_raw(" bytes.\n");
log_file->write_footer();
return;
}
*/
// Write passed sound samples to DSP.
status = playback_device->write(playbuf, len);
if (status != len) {
string msg("Writing to dsp failed: ");
msg += get_error_str(errno);
log_file->write_report(msg, "t_audio_tx::play_pcm",
LOG_NORMAL, LOG_CRITICAL);
}
}
void t_audio_tx::set_running(bool running) {
is_running = running;
}
void t_audio_tx::run(void) {
const AppDataUnit* adu;
struct timespec sleeptimer;
struct timeval debug_timer, debug_timer_prev;
int last_seqnum = -1; // seqnum of last received RTP packet
// RTP packets with multiple SSRCs may be received. Each SSRC
// represents an audio stream. Twinkle will only play 1 audio stream.
// On a reception of a new SSRC, Twinkle will switch over to play the
// new stream. This supports devices that change SSRC during a call.
uint32 ssrc_current = 0;
bool recvd_dtmf = false; // indicates if last RTP packets is a DTMF event
// 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;
uint32 rtp_timestamp;
// This thread may not take the lock on the transaction layer to
// prevent dead locks
phone->add_prohibited_thread();
ui->add_prohibited_thread();
while (true) {
do {
adu = NULL;
if (stop_running) break;
rtp_timestamp = rtp_session->getFirstTimestamp();
adu = rtp_session->getData(
rtp_session->getFirstTimestamp());
if (adu == NULL || adu->getSize() <= 0) {
// There is no packet available. This may have
// several reasons:
// - the thread scheduling granularity does
// not match ptime
// - packet lost
// - packet delayed
// Wait another cycle for a packet. The
// jitter buffer will cope with this variation.
if (adu) {
delete adu;
adu = NULL;
}
// If we are the mixer in a 3-way call and there
// is enough media from the other far-end then
// this must be sent to the dsp.
if (is_3way && is_3way_mixer &&
media_3way_peer_tx->size_content() >=
ptime * (audio_sample_rate(codec) / 1000) * 2)
{
// Fill the sample buffer with silence
int len = ptime * (audio_sample_rate(codec) / 1000) * 2;
memset(sample_buf, 0, len);
play_pcm(sample_buf, len, true);
}
// Sleep ptime ms
sleeptimer.tv_sec = 0;
if (ptime >= 20) {
sleeptimer.tv_nsec =
ptime * 1000000 - 10000000;
} else {
// With a thread schedule of 10ms
// granularity, this will schedule the
// thread every 10ms.
sleeptimer.tv_nsec = 5000000;
}
nanosleep(&sleeptimer, NULL);
}
} while (adu == NULL || (adu->getSize() <= 0));
if (stop_running) {
if (adu) delete adu;
break;
}
if (adu) {
// adu is created by ccRTP, but we have to delete it,
// so report it to MEMMAN
MEMMAN_NEW(const_cast<ost::AppDataUnit*>(adu));
}
// Check for a codec change
map<unsigned short, t_audio_codec>::const_iterator it_codec;
it_codec = payload2codec.find(adu->getType());
t_audio_codec recvd_codec = CODEC_NULL;
if (it_codec != payload2codec.end()) {
recvd_codec = it_codec->second;
}
// Switch over to new SSRC
if (last_seqnum == -1 || ssrc_current != adu->getSource().getID()) {
if (recvd_codec != CODEC_NULL) {
ssrc_current = adu->getSource().getID();
// An SSRC defines a sequence number space. So a new
// SSRC starts with a new random sequence number
last_seqnum = -1;
log_file->write_header("t_audio_tx::run",
LOG_NORMAL);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": play SSRC ");
log_file->write_raw(ssrc_current);
log_file->write_endl();
log_file->write_footer();
} else {
// SSRC received had an unsupported codec
// Discard.
// KLUDGE: for now this supports a scenario where a
// far-end starts ZRTP negotiation by sending CN
// packets with a separate SSRC while ZRTP is disabled
// in Twinkle. Twinkle will then receive the CN packets
// and discard them here as CN is an unsupported codec.
log_file->write_header("t_audio_tx::run",
LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": SSRC received (");
log_file->write_raw(adu->getSource().getID());
log_file->write_raw(") has unsupported codec ");
log_file->write_raw(adu->getType());
log_file->write_endl();
log_file->write_footer();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
continue;
}
}
map<t_audio_codec, t_audio_decoder *>::const_iterator it_decoder;
it_decoder = map_audio_decoder.find(recvd_codec);
if (it_decoder != map_audio_decoder.end()) {
if (codec != recvd_codec) {
codec = recvd_codec;
get_line()->ci_set_recv_codec(codec);
ui->cb_async_recv_codec_changed(get_line()->get_line_number(),
codec);
log_file->write_header("t_audio_tx::run",
LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": codec change to ");
log_file->write_raw(ui->format_codec(codec));
log_file->write_endl();
log_file->write_footer();
}
} else {
if (adu->getType() == pt_telephone_event ||
adu->getType() == pt_telephone_event_alt)
{
recvd_dtmf = true;
} else {
if (codec != CODEC_UNSUPPORTED) {
codec = CODEC_UNSUPPORTED;
get_line()->ci_set_recv_codec(codec);
ui->cb_async_recv_codec_changed(
get_line()->get_line_number(), codec);
log_file->write_header("t_audio_tx::run",
LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": payload type ");
log_file->write_raw(adu->getType());
log_file->write_raw(" not supported\n");
log_file->write_footer();
}
last_seqnum = adu->getSeqNum();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
continue;
}
}
// DTMF event
if (recvd_dtmf) {
// NOTE: the DTMF tone will be detected here
// while there might still be data in the jitter
// buffer. If the jitter buffer was already sent
// to the DSP, then the DSP will continue to play
// out the buffer sound samples.
if (dtmf_previous_timestamp != rtp_timestamp) {
// A new DTMF tone has been received.
dtmf_previous_timestamp = rtp_timestamp;
t_rtp_telephone_event *e =
(t_rtp_telephone_event *)adu->getData();
ui->cb_async_dtmf_detected(get_line()->get_line_number(),
e->get_event());
// Log DTMF event
log_file->write_header("t_audio_tx::run");
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": detected DTMF event - ");
log_file->write_raw(e->get_event());
log_file->write_endl();
log_file->write_footer();
}
recvd_dtmf = false;
last_seqnum = adu->getSeqNum();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
continue;
}
// Discard invalide payload sizes
if (!map_audio_decoder[codec]->valid_payload_size(
adu->getSize(), SAMPLE_BUF_SIZE / 2))
{
log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": RTP payload size (");
log_file->write_raw((unsigned long)(adu->getSize()));
log_file->write_raw(" bytes) invalid for \n");
log_file->write_raw(ui->format_codec(codec));
log_file->write_footer();
last_seqnum = adu->getSeqNum();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
continue;
}
unsigned short recvd_ptime;
recvd_ptime = map_audio_decoder[codec]->get_ptime(adu->getSize());
// Log a change of ptime
if (ptime != recvd_ptime) {
log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": ptime changed from ");
log_file->write_raw(ptime);
log_file->write_raw(" ms to ");
log_file->write_raw(recvd_ptime);
log_file->write_raw(" ms\n");
log_file->write_footer();
ptime = recvd_ptime;
}
// Check for lost packets
// This must be done before decoding the received samples as the
// speex decoder has its own PLC algorithm for which it needs the decoding
// state before decoding the new samples.
seq16_t seq_recvd(adu->getSeqNum());
seq16_t seq_last(static_cast<uint16>(last_seqnum));
if (last_seqnum != -1 && seq_recvd - seq_last > 1) {
// Packets have been lost
uint16 num_lost = (seq_recvd - seq_last) - 1;
log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": ");
log_file->write_raw(num_lost);
log_file->write_raw(" RTP packets lost.\n");
log_file->write_footer();
if (num_lost <= conceal_num) {
// Conceal packet loss
conceal(num_lost);
}
clear_conceal_buf();
}
// Determine if resampling is needed due to dynamic change to
// codec with other sample rate.
short downsample_factor = 1;
short upsample_factor = 1;
if (audio_sample_rate(codec) > sc_sample_rate) {
downsample_factor = audio_sample_rate(codec) / sc_sample_rate;
} else if (audio_sample_rate(codec) < sc_sample_rate) {
upsample_factor = sc_sample_rate / audio_sample_rate(codec);
}
// Create sample buffer. If no resampling is needed, the sample
// buffer from the audio_tx object can be used directly.
// Otherwise a temporary sample buffers is created that will
// be resampled to the object's sample buffer later.
short *sb;
int sb_size;
if (downsample_factor > 1) {
sb_size = SAMPLE_BUF_SIZE / 2 * downsample_factor;
sb = new short[sb_size];
MEMMAN_NEW_ARRAY(sb);
} else if (upsample_factor > 1) {
sb_size = SAMPLE_BUF_SIZE / 2;
sb = new short[SAMPLE_BUF_SIZE / 2];
MEMMAN_NEW_ARRAY(sb);
} else {
sb_size = SAMPLE_BUF_SIZE / 2;
sb = (short *)sample_buf;
}
// Decode the audio
unsigned char *payload = const_cast<uint8 *>(adu->getData());
short sample_size; // size in bytes
sample_size = 2 * map_audio_decoder[codec]->decode(payload, adu->getSize(),
sb, sb_size);
// Resample if needed
if (downsample_factor > 1) {
short *p = sb;
sb = (short *)sample_buf;
for (int i = 0; i < sample_size / 2; i += downsample_factor) {
sb[i / downsample_factor] = p[i];
}
MEMMAN_DELETE_ARRAY(p);
delete [] p;
sample_size /= downsample_factor;
} else if (upsample_factor > 1) {
short *p = sb;
sb = (short *)sample_buf;
for (int i = 0; i < sample_size / 2; i++) {
for (int j = 0; j < upsample_factor; j++) {
sb[i * upsample_factor + j] = p[i];
}
}
MEMMAN_DELETE_ARRAY(p);
delete [] p;
sample_size *= upsample_factor;
}
// If the decoder deliverd 0 bytes, then it failed
if (sample_size == 0) {
last_seqnum = adu->getSeqNum();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
continue;
}
// Discard packet if we are lacking behind. This happens if the
// soundcard plays at a rate less than the requested sample rate.
if (rtp_session->isWaiting(&(adu->getSource()))) {
uint32 last_ts = rtp_session->getLastTimestamp(&(adu->getSource()));
uint32 diff;
diff = last_ts - rtp_timestamp;
if (diff > (JITTER_BUF_SIZE(sc_sample_rate) / AUDIO_SAMPLE_SIZE) * 8)
{
log_file->write_header("t_audio_tx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": discard delayed packet.\n");
log_file->write_raw("Timestamp: ");
log_file->write_raw(rtp_timestamp);
log_file->write_raw(", Last timestamp: ");
log_file->write_raw((long unsigned int)last_ts);
log_file->write_endl();
log_file->write_footer();
last_seqnum = adu->getSeqNum();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
continue;
}
}
play_pcm(sample_buf, sample_size);
retain_for_concealment(sample_buf, sample_size);
last_seqnum = adu->getSeqNum();
MEMMAN_DELETE(const_cast<ost::AppDataUnit*>(adu));
delete adu;
// No sleep is done here but in the loop waiting
// for a new packet. If a packet is already available
// it can be send to the sound card immediately so
// the play-out buffer keeps filled.
// If the play-out buffer gets empty you hear a
// crack in the sound.
}
phone->remove_prohibited_thread();
ui->remove_prohibited_thread();
is_running = false;
}
void t_audio_tx::set_pt_telephone_event(int pt, int pt_alt) {
pt_telephone_event = pt;
pt_telephone_event_alt = pt_alt;
}
t_line *t_audio_tx::get_line(void) const {
return audio_session->get_line();
}
void t_audio_tx::join_3way(bool mixer, t_audio_tx *peer_tx, t_audio_rx *peer_rx) {
mtx_3way.lock();
if (is_3way) {
log_file->write_header("t_audio_tx::join_3way");
log_file->write_raw("ERROR: audio tx 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_tx::join_3way");
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": join 3-way.\n");
if (mixer) {
log_file->write_raw("Role is: mixer.\n");
} else {
log_file->write_raw("Role is: non-mixing.\n");
}
if (peer_tx) {
log_file->write_raw("A peer transmitter already exists.\n");
} else {
log_file->write_raw("A peer transmitter does not exist.\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();
peer_tx_3way = peer_tx;
peer_rx_3way = peer_rx;
is_3way_mixer = mixer;
is_3way = true;
// Create buffers for mixing
mix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(mix_buf_3way);
// See comments in audio_rx.cpp for the size of this buffer.
media_3way_peer_tx = new t_media_buffer(JITTER_BUF_SIZE(sc_sample_rate));
MEMMAN_NEW(media_3way_peer_tx);
mtx_3way.unlock();
}
void t_audio_tx::set_peer_tx_3way(t_audio_tx *peer_tx) {
mtx_3way.lock();
if (!is_3way) {
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_tx::set_peer_tx_3way");
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
if (peer_tx) {
log_file->write_raw(": set peer transmitter.\n");
} else {
log_file->write_raw(": erase peer transmitter.\n");
}
if (is_3way_mixer) {
log_file->write_raw("Role is: mixer.\n");
} else {
log_file->write_raw("Role is: non-mixing.\n");
}
log_file->write_footer();
peer_tx_3way = peer_tx;
mtx_3way.unlock();
}
void t_audio_tx::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_tx::set_peer_rx_3way");
log_file->write_raw("Audio tx 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_3way_mixer) {
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_tx::set_mixer_3way(bool mixer) {
mtx_3way.lock();
if (!is_3way) {
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_tx::set_mixer_3way");
log_file->write_raw("Audio tx line ");
log_file->write_raw(get_line()->get_line_number()+1);
if (mixer) {
log_file->write_raw(": change role to: mixer.\n");
} else {
log_file->write_raw(": change role to: non-mixing.\n");
}
log_file->write_footer();
is_3way_mixer = mixer;
mtx_3way.unlock();
}
void t_audio_tx::stop_3way(void) {
mtx_3way.lock();
if (!is_3way) {
log_file->write_header("t_audio_tx::stop_3way");
log_file->write_raw("ERROR: audio tx 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_tx::stop_3way");
log_file->write_raw("Audio tx 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_3way_mixer = false;
if (media_3way_peer_tx) {
MEMMAN_DELETE(media_3way_peer_tx);
delete media_3way_peer_tx;
media_3way_peer_tx = NULL;
}
if (mix_buf_3way) {
MEMMAN_DELETE_ARRAY(mix_buf_3way);
delete [] mix_buf_3way;
mix_buf_3way = NULL;
}
mtx_3way.unlock();
}
void t_audio_tx::post_media_peer_tx_3way(unsigned char *media, int len,
unsigned short peer_sample_rate)
{
mtx_3way.lock();
if (!is_3way || !is_3way_mixer) {
mtx_3way.unlock();
return;
}
if (peer_sample_rate != sc_sample_rate) {
// Resample media from peer to sample rate of this transmitter
int output_len = (len / 2) * sc_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, sc_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();
}
bool t_audio_tx::get_is_3way_mixer(void) const {
return is_3way_mixer;
}
syntax highlighted by Code2HTML, v. 0.9.1