diff options
Diffstat (limited to 'src/audio/audio_tx.cpp')
-rw-r--r-- | src/audio/audio_tx.cpp | 1009 |
1 files changed, 1009 insertions, 0 deletions
diff --git a/src/audio/audio_tx.cpp b/src/audio/audio_tx.cpp new file mode 100644 index 0000000..4901ede --- /dev/null +++ b/src/audio/audio_tx.cpp @@ -0,0 +1,1009 @@ +/* + Copyright (C) 2005-2009 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) { + /* Does not work on all ALSA implementations. + if (playback_device->get_buffer_space(false) < soundcard_buf_size - len) { + */ + if (!playback_device->play_buffer_underrun()) { + // 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) + /* + * This code does not work on all ALSA implementations, e.g. ALSA via pulse audio + int bufferspace = playback_device->get_buffer_space(false); + if (bufferspace == soundcard_buf_size && len <= JITTER_BUF_SIZE(sc_sample_rate)) { + */ + if (playback_device->play_buffer_underrun()) { + 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); + return; + } +} + +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 = 0; + + // 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 > (uint32_t)(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. + + +#ifdef HAVE_SPEEX + // store decoded output for (optional) echo cancellation + if (audio_session->get_do_echo_cancellation()) { + if (audio_session->get_echo_captured_last()) { + speex_echo_playback(audio_session->get_speex_echo_state(), (spx_int16_t *) sb); + audio_session->set_echo_captured_last(false);; + } + } +#endif + + } + + 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; +} |