/* * Jingle call example * Copyright 2004--2005, Google Inc. * * 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 */ // LinphoneMediaEngine is a Linphone implementation of MediaEngine #include //#ifdef HAVE_ILBC //#include //#endif //#ifdef HAVE_SPEEX //#include //#endif #include #include //#include //#include #include #include #include "talk/base/logging.h" #include "talk/base/thread.h" #include "talk/base/helpers.h" #include "talk/session/phone/codec.h" #include "talk/session/phone/linphonemediaengine.h" using namespace cricket; void LinphoneMediaChannel::OnIncomingData(talk_base::AsyncSocket *s) { char *buf[2048]; int len; len = s->Recv(buf, sizeof(buf)); if (network_interface_ && !mute_) network_interface_->SendPacket(buf, len); } LinphoneMediaChannel::LinphoneMediaChannel(LinphoneMediaEngine*eng) : pt_(-1), audio_stream_(0), engine_(eng) { talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread(); talk_base::SocketServer *ss = thread->socketserver(); socket_.reset(ss->CreateAsyncSocket(SOCK_DGRAM)); port_in_ = 2000 + CreateRandomId() % 1000; port_out_ = 3000 + CreateRandomId() % 1000; socket_->Bind(talk_base::SocketAddress("localhost", port_out_)); socket_->SignalReadEvent.connect(this, &LinphoneMediaChannel::OnIncomingData); } LinphoneMediaChannel::~LinphoneMediaChannel() { if (audio_stream_ != NULL) audio_stream_stop(audio_stream_); } void LinphoneMediaChannel::SetCodecs(const std::vector &codecs) { bool first = true; std::vector::const_iterator i; for (i = codecs.begin(); i < codecs.end(); i++) { if (!engine_->FindCodec(*i)) continue; #ifdef HAVE_ILBC if (i->name == payload_type_ilbc.mime_type) { rtp_profile_set_payload(&av_profile, i->id, &payload_type_ilbc); } #endif #ifdef HAVE_SPEEX if (i->name == payload_type_speex_wb.mime_type && i->clockrate == payload_type_speex_wb.clock_rate) { rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_wb); } else if (i->name == payload_type_speex_nb.mime_type && i->clockrate == payload_type_speex_nb.clock_rate) { rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_nb); } #endif if (i->id == 0) rtp_profile_set_payload(&av_profile, 0, &payload_type_pcmu8000); if (i->name == payload_type_telephone_event.mime_type) { rtp_profile_set_payload(&av_profile, i->id, &payload_type_telephone_event); } if (first) { LOG(LS_INFO) << "Using " << i->name << "/" << i->clockrate; pt_ = i->id; audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, i->id, 250, false); first = false; } } if (first) { // We're being asked to set an empty list of codecs. This will only happen when // working with a buggy client; let's try PCMU. LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000"; audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, 0, 250, false); } } bool LinphoneMediaEngine::FindCodec(const Codec &c) { if (c.id == 0) return true; if (c.name == payload_type_telephone_event.mime_type) return true; #ifdef HAVE_SPEEX if (c.name == payload_type_speex_wb.mime_type && c.clockrate == payload_type_speex_wb.clock_rate) return true; if (c.name == payload_type_speex_nb.mime_type && c.clockrate == payload_type_speex_nb.clock_rate) return true; #endif #ifdef HAVE_ILBC if (c.name == payload_type_ilbc.mime_type) return true; #endif return false; } void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) { uint8 buf[2048]; memcpy(buf, data, len); /* We may receive packets with payload type 13: comfort noise. Linphone can't * handle them, so let's ignore those packets. */ int payloadtype = buf[1] & 0x7f; if (play_ && payloadtype != 13) socket_->SendTo(buf, len, talk_base::SocketAddress("localhost", port_in_)); } void LinphoneMediaChannel::SetPlayout(bool playout) { play_ = playout; } void LinphoneMediaChannel::SetSend(bool send) { mute_ = !send; } int LinphoneMediaChannel::GetOutputLevel() { return 0; } LinphoneMediaEngine::LinphoneMediaEngine() {} LinphoneMediaEngine::~LinphoneMediaEngine() {} /* static void null_log_handler(const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data) { LOG(LS_INFO) << log_domain << " " << message; } */ bool LinphoneMediaEngine::Init() { // g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, this); // g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, this); // g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, this); ortp_init(); ms_init(); #ifdef HAVE_SPEEX //ms_speex_codec_init(); codecs_.push_back(Codec(110, payload_type_speex_wb.mime_type, payload_type_speex_wb.clock_rate, 0, 1, 8)); codecs_.push_back(Codec(111, payload_type_speex_nb.mime_type, payload_type_speex_nb.clock_rate, 0, 1, 7)); #endif #ifdef HAVE_ILBC //ms_ilbc_codec_init(); codecs_.push_back(Codec(102, payload_type_ilbc.mime_type, payload_type_ilbc.clock_rate, 0, 1, 4)); #endif codecs_.push_back(Codec(0, payload_type_pcmu8000.mime_type, payload_type_pcmu8000.clock_rate, 0, 1, 2)); codecs_.push_back(Codec(101, payload_type_telephone_event.mime_type, payload_type_telephone_event.clock_rate, 0, 1, 1)); return true; } void LinphoneMediaEngine::Terminate() { } MediaChannel *LinphoneMediaEngine::CreateChannel() { return new LinphoneMediaChannel(this); } int LinphoneMediaEngine::SetAudioOptions(int options) { return 0; } int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { return 0; } float LinphoneMediaEngine::GetCurrentQuality() { return 0; } int LinphoneMediaEngine::GetInputLevel() { return 0; }