diff options
Diffstat (limited to 'src/session.cpp')
-rw-r--r-- | src/session.cpp | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/src/session.cpp b/src/session.cpp new file mode 100644 index 0000000..83771f6 --- /dev/null +++ b/src/session.cpp @@ -0,0 +1,822 @@ +/* + 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 "line.h" +#include "log.h" +#include "phone.h" +#include "phone_user.h" +#include "session.h" +#include "util.h" +#include "userintf.h" +#include "audits/memman.h" + +extern string user_host; +extern string local_hostname; +extern t_phone *phone; + +/////////// +// PRIVATE +/////////// + +void t_session::set_recvd_codecs(t_sdp *sdp) { + recvd_codecs.clear(); + send_ac2payload.clear(); + send_payload2ac.clear(); + list<unsigned short> payloads = sdp->get_codecs(SDP_AUDIO); + for (list<unsigned short>::iterator i = payloads.begin(); + i != payloads.end(); i++) + { + t_audio_codec ac = sdp->get_codec(SDP_AUDIO, *i); + if (ac > CODEC_UNSUPPORTED) { + recvd_codecs.push_back(ac); + send_ac2payload[ac] = *i; + send_payload2ac[*i] = ac; + } + } +} + +bool t_session::is_3way(void) const { + t_line *l = get_line(); + t_phone *p = l->get_phone(); + + return p->part_of_3way(l->get_line_number()); +} + +t_session *t_session::get_peer_3way(void) const { + t_line *l = get_line(); + t_phone *p = l->get_phone(); + + t_line *peer_line = p->get_3way_peer_line(l->get_line_number()); + + return peer_line->get_session(); +} + +/////////// +// PUBLIC +/////////// + +t_session::t_session(t_dialog *_dialog, string _receive_host, + unsigned short _receive_port) +{ + dialog = _dialog; + + user_config = dialog->get_line()->get_user(); + assert(user_config); + + receive_host = _receive_host; + retrieve_host = _receive_host; + receive_port = _receive_port; + src_sdp_version = int2str(rand()); + src_sdp_id = int2str(rand()); + use_codec = CODEC_NULL; + + switch (user_config->get_dtmf_transport()) { + case DTMF_RFC2833: + case DTMF_AUTO: + recv_dtmf_pt = user_config->get_dtmf_payload_type(); + break; + default: + recv_dtmf_pt = 0; + } + + send_dtmf_pt = 0; + + offer_codecs = user_config->get_codecs(); + ptime = user_config->get_ptime(); + ilbc_mode = user_config->get_ilbc_mode(); + + recvd_offer = false; + recvd_answer = false; + sent_offer = false; + direction = SDP_SENDRECV; + + audio_rtp_session = NULL; + is_on_hold = false; + is_killed = false; + + // Initialize audio codec to payload mappings + recv_ac2payload[CODEC_G711_ULAW] = SDP_FORMAT_G711_ULAW; + recv_ac2payload[CODEC_G711_ALAW] = SDP_FORMAT_G711_ALAW; + recv_ac2payload[CODEC_GSM] = SDP_FORMAT_GSM; + recv_ac2payload[CODEC_SPEEX_NB] = user_config->get_speex_nb_payload_type(); + recv_ac2payload[CODEC_SPEEX_WB] = user_config->get_speex_wb_payload_type(); + recv_ac2payload[CODEC_SPEEX_UWB] = user_config->get_speex_uwb_payload_type(); + recv_ac2payload[CODEC_ILBC] = user_config->get_ilbc_payload_type(); + recv_ac2payload[CODEC_G726_16] = user_config->get_g726_16_payload_type(); + recv_ac2payload[CODEC_G726_24] = user_config->get_g726_24_payload_type(); + recv_ac2payload[CODEC_G726_32] = user_config->get_g726_32_payload_type(); + recv_ac2payload[CODEC_G726_40] = user_config->get_g726_40_payload_type(); + recv_ac2payload[CODEC_TELEPHONE_EVENT] = user_config->get_dtmf_payload_type(); + send_ac2payload.clear(); + + // Initialize pauload to audio codec mappings + recv_payload2ac[SDP_FORMAT_G711_ULAW] = CODEC_G711_ULAW; + recv_payload2ac[SDP_FORMAT_G711_ALAW] = CODEC_G711_ALAW; + recv_payload2ac[SDP_FORMAT_GSM] = CODEC_GSM; + recv_payload2ac[user_config->get_speex_nb_payload_type()] = CODEC_SPEEX_NB; + recv_payload2ac[user_config->get_speex_wb_payload_type()] = CODEC_SPEEX_WB; + recv_payload2ac[user_config->get_speex_uwb_payload_type()] = CODEC_SPEEX_UWB; + recv_payload2ac[user_config->get_ilbc_payload_type()] = CODEC_ILBC; + recv_payload2ac[user_config->get_g726_16_payload_type()] = CODEC_G726_16; + recv_payload2ac[user_config->get_g726_24_payload_type()] = CODEC_G726_24; + recv_payload2ac[user_config->get_g726_32_payload_type()] = CODEC_G726_32; + recv_payload2ac[user_config->get_g726_40_payload_type()] = CODEC_G726_40; + recv_payload2ac[user_config->get_dtmf_payload_type()] = CODEC_TELEPHONE_EVENT; + send_payload2ac.clear(); +} + +t_session::~t_session() { + stop_rtp(); +} + +t_session *t_session::create_new_version(void) const { + t_session *s = new t_session(*this); + MEMMAN_NEW(s); + s->src_sdp_version = int2str(atoi(src_sdp_version.c_str()) + 1); + s->recvd_codecs.clear(); + s->recvd_offer = false; + s->recvd_answer = false; + s->sent_offer = false; + + // Do not copy the RTP session + s->set_audio_session(NULL); + + // Clear the codec to payload mappings as a new response must + // be received from the far end + s->send_ac2payload.clear(); + s->send_payload2ac.clear(); + + return s; +} + +t_session *t_session::create_call_hold(void) const { + t_session *s = create_new_version(); + + if (user_config->get_hold_variant() == HOLD_RFC2543) { + s->receive_host = "0.0.0.0"; + } else if (user_config->get_hold_variant() == HOLD_RFC3264) { + // RFC 3264 8.4 + if (direction == SDP_SENDRECV) { + s->direction = SDP_SENDONLY; + } + else if (direction == SDP_RECVONLY) { + s->direction = SDP_INACTIVE; + } + } else { + assert(false); + } + + // Prevent RTP from being started for this session as long + // as the call is put on hold. Without this, the RTP sessions + // will get started when a re-INVITE is received from the far-end + // while the call is still locally on-hold. + s->hold(); + + return s; +} + +t_session *t_session::create_call_retrieve(void) const { + t_session *s = create_new_version(); + + if (user_config->get_hold_variant() == HOLD_RFC2543) { + s->receive_host = retrieve_host; + } else if (user_config->get_hold_variant() == HOLD_RFC3264) { + // RFC 3264 8.4 + if (direction == SDP_SENDONLY) { + s->direction = SDP_SENDRECV; + } + else if (direction == SDP_INACTIVE) { + s->direction = SDP_RECVONLY; + } + } else { + assert(false); + } + + return s; +} + +t_session *t_session::create_clean_copy(void) const { + t_session *s = new t_session(*this); + MEMMAN_NEW(s); + s->src_sdp_version = int2str(atoi(src_sdp_version.c_str()) + 1); + s->dst_sdp_version = ""; + s->dst_sdp_id = ""; + s->dst_rtp_host = ""; + s->dst_rtp_port = 0; + s->recvd_codecs.clear(); + s->recvd_offer = false; + s->recvd_answer = false; + s->sent_offer = false; + s->direction = SDP_SENDRECV; + + // Do not copy the RTP session + s->set_audio_session(NULL); + + // Clear the codec to payload mappings as a new response must + // be received from the far end + s->send_ac2payload.clear(); + s->send_payload2ac.clear(); + + return s; +} + +bool t_session::process_sdp_offer(t_sdp *sdp, int &warn_code, + string &warn_text) +{ + if (!sdp->is_supported(warn_code, warn_text)) return false; + + dst_sdp_version = sdp->origin.session_version; + dst_sdp_id = sdp->origin.session_id; + recvd_sdp_offer = *sdp; + + // RFC 3264 5 + // SDP may contain 0 m= lines + if (sdp->media.empty()) return true; + + dst_rtp_host = sdp->get_rtp_host(SDP_AUDIO); + dst_rtp_port = sdp->get_rtp_port(SDP_AUDIO); + set_recvd_codecs(sdp); + dst_zrtp_support = sdp->get_zrtp_support(SDP_AUDIO); + + // The direction in the SDP is from the point of view of the + // far end. Swap the direction to store it as the point of view + // from the near end. + switch(sdp->get_direction(SDP_AUDIO)) { + case SDP_INACTIVE: + direction = SDP_INACTIVE; + break; + case SDP_SENDONLY: + if (is_on_hold && user_config->get_hold_variant() == HOLD_RFC3264) { + // The phone is put on-hold. We don't want to + // receive media. + direction = SDP_INACTIVE; + } else { + direction = SDP_RECVONLY; + } + break; + case SDP_RECVONLY: + direction = SDP_SENDONLY; + break; + case SDP_SENDRECV: + if (is_on_hold && user_config->get_hold_variant() == HOLD_RFC3264) { + // The phone is put on-hold. We don't want to + // receive media. + direction = SDP_SENDONLY; + } else { + direction = SDP_SENDRECV; + } + break; + default: + assert(false); + } + + // Check if the list of received codecs has at least 1 codec + // in common with the list of codecs we can offer. If there + // is no common codec, then no call can be established. + list<t_audio_codec>::iterator supported_codec_it = offer_codecs.end(); + for (list<t_audio_codec>::const_iterator i = recvd_codecs.begin(); + i != recvd_codecs.end(); i++) + { + list<t_audio_codec>::iterator tmp_it; + if ((supported_codec_it == offer_codecs.end() || + !user_config->get_in_obey_far_end_codec_pref()) && + (tmp_it = std::find(offer_codecs.begin(), supported_codec_it, *i)) != + supported_codec_it) + { + // Codec supported + supported_codec_it = tmp_it; + use_codec = *i; // this codec goes into answer + + // Use the payload to codec bindings as signalled in the + // offer by the far end. + recv_payload2ac[send_ac2payload[use_codec]] = use_codec; + recv_ac2payload[use_codec] = send_ac2payload[use_codec]; + } else if (*i == CODEC_TELEPHONE_EVENT) { + // telephone-event payload is supported + send_dtmf_pt = send_ac2payload[*i]; + + // When we support RFC 2833 events, then take the payload + // type from the far end. + if (recv_dtmf_pt > 0) { + recv_dtmf_pt = send_dtmf_pt; // this goes into answer as well + } + } + } + + if (supported_codec_it == offer_codecs.end()) { + warn_code = W_305_INCOMPATIBLE_MEDIA_FORMAT; + warn_text = "None of the audio codecs is supported"; + return false; + } + + // Overwrite ptime value with ptime from SDP + unsigned short p = sdp->get_ptime(SDP_AUDIO); + if (p > 0) ptime = p; + + // RFC 3952 5 + // Select the iLBC mode that needs the lowest bandwidth + if (use_codec == CODEC_ILBC) { + int recvd_mode = sdp->get_fmtp_int_param(SDP_AUDIO, + send_ac2payload[use_codec], "mode"); + if (recvd_mode == -1) recvd_mode = 30; + if (VALID_ILBC_MODE(recvd_mode) && recvd_mode > ilbc_mode) { + ilbc_mode = static_cast<unsigned short>(recvd_mode); + } + } + + return true; +} + +bool t_session::process_sdp_answer(t_sdp *sdp, int &warn_code, + string &warn_text) +{ + if (!sdp->is_supported(warn_code, warn_text)) return false; + + // As our offer always contains an audio m= line, the answer + // should contain one as well. If there are media lines, then + // the sdp->is_supported already verified there is audio. + if (sdp->media.empty()) { + warn_code = W_304_MEDIA_TYPE_NOT_AVAILABLE; + warn_text = "Valid media stream for audio is missing"; + return false; + } + + dst_sdp_version = sdp->origin.session_version; + dst_sdp_id = sdp->origin.session_id; + dst_rtp_host = sdp->get_rtp_host(SDP_AUDIO); + dst_rtp_port = sdp->get_rtp_port(SDP_AUDIO); + dst_zrtp_support = sdp->get_zrtp_support(SDP_AUDIO); + set_recvd_codecs(sdp); + + // Find the first codec in the received codecs list that + // is supported. + // Per the offer/answer model all received codecs should be + // supported! It seems that some applications put more codecs + // in the answer though. + list<t_audio_codec>::iterator codec_found_it = offer_codecs.end(); + + for (list<t_audio_codec>::const_iterator i = recvd_codecs.begin(); + i != recvd_codecs.end(); i++) + { + list<t_audio_codec>::iterator tmp_it; + if ((codec_found_it == offer_codecs.end() || + !user_config->get_out_obey_far_end_codec_pref()) && + (tmp_it = std::find(offer_codecs.begin(), codec_found_it, *i)) != + codec_found_it) + { + codec_found_it = tmp_it; + use_codec = *i; + } else if (*i == CODEC_TELEPHONE_EVENT) { + // telephone-event payload is supported + send_dtmf_pt = send_ac2payload[*i]; + } + } + + if (codec_found_it == offer_codecs.end()) { + // None of the answered codecs is supported + warn_code = W_305_INCOMPATIBLE_MEDIA_FORMAT; + warn_text = "None of the codecs is supported"; + return false; + } + + // Overwrite ptime value with ptime from SDP + unsigned short p = sdp->get_ptime(SDP_AUDIO); + if (p > 0) ptime = p; + + // RFC 3952 5 + // Select the iLBC mode that needs the lowest bandwidth + if (use_codec == CODEC_ILBC) { + int recvd_mode = sdp->get_fmtp_int_param(SDP_AUDIO, + send_ac2payload[use_codec], "mode"); + if (recvd_mode == -1) recvd_mode = 30; + if (VALID_ILBC_MODE(recvd_mode) && recvd_mode > ilbc_mode) { + ilbc_mode = static_cast<unsigned short>(recvd_mode); + } + } + + return true; +} + +void t_session::create_sdp_offer(t_sip_message *m, const string &user) { + // Delete old body if present + if (m->body) { + MEMMAN_DELETE(m->body); + delete m->body; + } + + // Determine the IP address to receive the media streams + if (receive_host == AUTO_IP4_ADDRESS) { + unsigned local_ip = m->get_local_ip(); + if (local_ip == 0) { + log_file->write_report("Cannot determine local IP address.", + "t_session::create_sdp_offer", LOG_NORMAL, LOG_CRITICAL); + } else { + receive_host = USER_HOST(user_config, h_ip2str(local_ip)); + retrieve_host = receive_host; + } + } + + m->body = new t_sdp(user, src_sdp_id, src_sdp_version, receive_host, + receive_host, receive_port, offer_codecs, recv_dtmf_pt, + recv_ac2payload); + MEMMAN_NEW(m->body); + + + // Set ptime for G711/G726 codecs + list<t_audio_codec>::iterator it_g7xx; + it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G711_ALAW); + if (it_g7xx == offer_codecs.end()) { + it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G711_ULAW); + } + if (it_g7xx == offer_codecs.end()) { + it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_16); + } + if (it_g7xx == offer_codecs.end()) { + it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_24); + } + if (it_g7xx == offer_codecs.end()) { + it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_32); + } + if (it_g7xx == offer_codecs.end()) { + it_g7xx = find(offer_codecs.begin(), offer_codecs.end(), CODEC_G726_40); + } + if (it_g7xx != offer_codecs.end()) { + ((t_sdp *)m->body)->set_ptime(SDP_AUDIO, ptime); + } + + // Set mode for iLBC codecs + list<t_audio_codec>::iterator it_ilbc; + it_ilbc = find(offer_codecs.begin(), offer_codecs.end(), CODEC_ILBC); + if (it_ilbc != offer_codecs.end() && ilbc_mode != 30) { + ((t_sdp *)m->body)->set_fmtp_int_param(SDP_AUDIO, recv_ac2payload[CODEC_ILBC], + "mode", ilbc_mode); + } + + // Set direction + if (direction != SDP_SENDRECV) { + ((t_sdp *)m->body)->set_direction(SDP_AUDIO, direction); + } + + // Set zrtp support + if (user_config->get_zrtp_enabled() && user_config->get_zrtp_sdp()) { + ((t_sdp *)m->body)->set_zrtp_support(SDP_AUDIO); + } + + m->hdr_content_type.set_media(t_media("application", "sdp")); + + sent_offer = true; +} + +void t_session::create_sdp_answer(t_sip_message *m, const string &user) { + // Delete old body if present + if (m->body) { + MEMMAN_DELETE(m->body); + delete m->body; + } + + // Determine the IP address to receive the media streams + if (receive_host == AUTO_IP4_ADDRESS) { + unsigned long local_ip = 0; + unsigned long dst_ip = gethostbyname(dst_rtp_host); + + if (dst_ip != 0) + { + // Determine source IP address for RTP from the + // destination RTP IP address. + log_file->write_report("Cannot determine local IP address from RTP destination.", + "t_session::create_sdp_answer", LOG_NORMAL, LOG_WARNING); + + local_ip = get_src_ip4_address_for_dst(dst_ip); + } + else + { + string log_msg = "Cannot determine IP address for: "; + log_msg += dst_rtp_host; + log_file->write_report(log_msg, + "t_session::create_sdp_answer", LOG_NORMAL, LOG_WARNING); + } + + if (local_ip == 0) + { + // Somehow the source IP address could not be determined + // from the destination RTP address. Try to determine it + // from the destination of the SIP message. + local_ip = m->get_local_ip(); + } + + if (local_ip == 0) { + log_file->write_report("Cannot determine local IP address.", + "t_session::create_sdp_answer", LOG_NORMAL, LOG_CRITICAL); + } else { + receive_host = USER_HOST(user_config, h_ip2str(local_ip)); + retrieve_host = receive_host; + } + } + + list<t_audio_codec> answer_codecs; + answer_codecs.push_back(use_codec); + + // RFC 3264 6 + // The answer must contain an m-line for each m-line in the offer in + // the same order. Media can be rejected by setting the port to 0. + // Only the first audio stream is accepted, all other media streams + // will be rejected. + m->body = new t_sdp(user, src_sdp_id, src_sdp_version, receive_host, + receive_host); + MEMMAN_NEW(m->body); + bool audio_answered = false; + for (list<t_sdp_media>::const_iterator i = recvd_sdp_offer.media.begin(); + i != recvd_sdp_offer.media.end(); i++) + { + if (!audio_answered && i->get_media_type() == SDP_AUDIO && + i->port != 0) + { + // Accept the first audio stream + ((t_sdp *)m->body)->add_media(t_sdp_media( + SDP_AUDIO, receive_port, answer_codecs, recv_dtmf_pt, + send_ac2payload)); + audio_answered = true; + } + else + { + // Reject media stream by setting port to zero + t_sdp_media reject_media(*i); + reject_media.port = 0; + ((t_sdp *)m->body)->add_media(reject_media); + } + } + + m->hdr_content_type.set_media(t_media("application", "sdp")); + + // If there were no media lines in the offer, we sent no media + // lines in the answer + if (recvd_sdp_offer.media.empty()) return; + + // Set audio attributes + + // Set ptime for G711 codecs + if (use_codec == CODEC_G711_ALAW || + use_codec == CODEC_G711_ULAW) + { + ((t_sdp *)m->body)->set_ptime(SDP_AUDIO, ptime); + } + + // Set mode for iLBC codecs + if (use_codec == CODEC_ILBC && ilbc_mode != 30) { + unsigned short ilbc_payload = const_cast<t_session *>(this)-> + recv_ac2payload[CODEC_ILBC]; + ((t_sdp *)m->body)->set_fmtp_int_param(SDP_AUDIO, ilbc_payload, + "mode", ilbc_mode); + } + + // Set direction + if (direction != SDP_SENDRECV) { + ((t_sdp *)m->body)->set_direction(SDP_AUDIO, direction); + } + + // Set zrtp support + if (user_config->get_zrtp_enabled() && user_config->get_zrtp_sdp()) { + ((t_sdp *)m->body)->set_zrtp_support(SDP_AUDIO); + } +} + +void t_session::start_rtp(void) { + // If a session is killed, it may not be started again. + if (is_killed) { + log_file->write_report("Cannot start. The session is killed already.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + return; + } + + // If a session is on-hold then do not start RTP. + if (is_on_hold) { + log_file->write_report("Cannot start. The session is on hold.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + return; + } + + if (receive_host.empty()) { + log_file->write_report("Cannot start. receive_host is empty.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + return; + } + + if (dst_rtp_host.empty()) { + log_file->write_report("Cannot start. dst_rtp_host is empty.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + return; + } + + // Local and remote hold + if (((receive_host == "0.0.0.0" || receive_port == 0) && + (dst_rtp_host == "0.0.0.0" || dst_rtp_port == 0)) || + direction == SDP_INACTIVE) + { + log_file->write_report("Cannot start. Local and remote on hold.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + return; + } + + // Inform user about the codecs + get_line()->ci_set_send_codec(use_codec); + get_line()->ci_set_recv_codec(use_codec); + ui->cb_send_codec_changed(get_line()->get_line_number(), use_codec); + ui->cb_recv_codec_changed(get_line()->get_line_number(), use_codec); + + // Determine ptime + unsigned short audio_ptime; + if (use_codec == CODEC_ILBC) { + audio_ptime = ilbc_mode; + } else { + audio_ptime = ptime; + } + + // Determine if audio must be encrypted + bool encrypt_audio = get_line()->get_try_to_encrypt(); + if (user_config->get_zrtp_send_if_supported()) { + encrypt_audio = encrypt_audio && dst_zrtp_support; + } + + // Start the RTP streams + if (dst_rtp_host == "0.0.0.0" || dst_rtp_port == 0 || + direction == SDP_RECVONLY) + { + // Local hold -> do not send RTP + log_file->write_report("Local hold. Do not send RTP.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + audio_rtp_session = new t_audio_session(this, + "0.0.0.0", get_line()->get_rtp_port(), "", 0, use_codec, + audio_ptime, recv_payload2ac, send_ac2payload, + encrypt_audio); + MEMMAN_NEW(audio_rtp_session); + } + else if (receive_host == "0.0.0.0" || receive_port == 0 || + direction == SDP_SENDONLY) + { + // Remote hold + // For music on-hold music should be played here. + // Without music on-hold do not send out RTP + /* + audio_rtp_session = new t_audio_session(this, + "", 0, dst_rtp_host, dst_rtp_port, codec, ptime); + */ + log_file->write_report("Do not start. Remote hold.", + "t_session::start_rtp", LOG_NORMAL, LOG_DEBUG); + return; + } else { + // Bi-directional audio + audio_rtp_session = new t_audio_session(this, + "0.0.0.0", get_line()->get_rtp_port(), + dst_rtp_host, dst_rtp_port, use_codec, audio_ptime, + recv_payload2ac, send_ac2payload, + encrypt_audio); + MEMMAN_NEW(audio_rtp_session); + } + + // Check if the created audio session is valid. + if (!audio_rtp_session->is_valid()) { + log_file->write_report("Audio session is invalid.", + "t_session::start_rtp", LOG_NORMAL, LOG_CRITICAL); + MEMMAN_DELETE(audio_rtp_session); + delete audio_rtp_session; + audio_rtp_session = NULL; + return; + } + + // Set dynamic payload type for DTMF events + if (recv_dtmf_pt > 0) { + unsigned short alt_dtmf_pt; + if (recv_payload2ac.find(send_dtmf_pt) == recv_payload2ac.end()) { + // Allow the payload type as signalled by the far end + // as an alternative to the payload as signalled by Twinkle. + alt_dtmf_pt = send_dtmf_pt; + } else { + // The payload type as signalled by the far end for DTMF + // is already in use by Twinkle for another codec, so it + // cannot be used as an alternative. + alt_dtmf_pt = recv_dtmf_pt; + } + audio_rtp_session->set_pt_in_dtmf(recv_dtmf_pt, alt_dtmf_pt); + } + + if (send_dtmf_pt > 0) { + audio_rtp_session->set_pt_out_dtmf(send_dtmf_pt); + + switch (user_config->get_dtmf_transport()) { + case DTMF_AUTO: + case DTMF_RFC2833: + get_line()->ci_set_dtmf_supported(true, false); + break; + case DTMF_INBAND: + get_line()->ci_set_dtmf_supported(true, true); + break; + case DTMF_INFO: + get_line()->ci_set_dtmf_supported(true, false, true); + break; + default: + assert(false); + } + + ui->cb_dtmf_supported(get_line()->get_line_number()); + } else { + switch (user_config->get_dtmf_transport()) { + case DTMF_AUTO: + case DTMF_INBAND: + get_line()->ci_set_dtmf_supported(true, true); + ui->cb_dtmf_supported(get_line()->get_line_number()); + break; + case DTMF_RFC2833: + get_line()->ci_set_dtmf_supported(false); + ui->cb_dtmf_not_supported(get_line()->get_line_number()); + break; + case DTMF_INFO: + get_line()->ci_set_dtmf_supported(true, false, true); + ui->cb_dtmf_supported(get_line()->get_line_number()); + break; + default: + assert(false); + } + } + + audio_rtp_session->run(); +} + +void t_session::stop_rtp(void) { + if (audio_rtp_session) { + MEMMAN_DELETE(audio_rtp_session); + delete audio_rtp_session; + audio_rtp_session = NULL; + + get_line()->ci_set_dtmf_supported(false); + ui->cb_line_state_changed(); + } +} + +void t_session::kill_rtp(void) { + stop_rtp(); + is_killed = true; +} + +t_audio_session *t_session::get_audio_session(void) const { + return audio_rtp_session; +} + +void t_session::set_audio_session(t_audio_session *as) { + audio_rtp_session = as; +} + +bool t_session::equal_audio(const t_session &s) const { + // According to RFC 3264 6, the SDP version in the o= line + // must be updated when the SDP is changed. + // We check for more changes to interoperate with SIP + // devices that do not adhere fully to RFC 3264 + return (receive_host == s.receive_host && + receive_port == s.receive_port && + dst_rtp_host == s.dst_rtp_host && + dst_rtp_port == s.dst_rtp_port && + direction == s.direction && + src_sdp_version == s.src_sdp_version && + dst_sdp_version == s.dst_sdp_version && + src_sdp_id == s.src_sdp_id && + dst_sdp_id == s.dst_sdp_id); +} + +void t_session::send_dtmf(char digit, bool inband) { + if (audio_rtp_session) audio_rtp_session->send_dtmf(digit, inband); +} + +t_line *t_session::get_line(void) const { + return dialog->get_line(); +} + +void t_session::set_owner(t_dialog *d) { + dialog = d; +} + +void t_session::hold(void) { + is_on_hold = true; +} + +void t_session::unhold(void) { + is_on_hold = false; +} + +bool t_session::is_rtp_active(void) const { + return (audio_rtp_session != NULL); +} |