1 /* 2 * libjingle 3 * Copyright 2010 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef MSILBC_LIBRARY 29 #define MSILBC_LIBRARY "/usr/lib/mediastreamer/plugins/libmsilbc.so" 30 #endif 31 32 // LinphoneMediaEngine is a Linphone implementation of MediaEngine 33 extern "C" { 34 #include <mediastreamer2/mediastream.h> 35 #include <mediastreamer2/msfilter.h> 36 #include <mediastreamer2/mssndcard.h> 37 } 38 39 #include "talk/media/other/linphonemediaengine.h" 40 41 #include "talk/media/base/rtpdump.h" 42 #include "webrtc/base/buffer.h" 43 #include "webrtc/base/event.h" 44 #include "webrtc/base/logging.h" 45 #include "webrtc/base/pathutils.h" 46 #include "webrtc/base/stream.h" 47 48 #ifndef WIN32 49 #include <libgen.h> 50 #endif 51 52 namespace cricket { 53 54 /////////////////////////////////////////////////////////////////////////// 55 // Implementation of LinphoneMediaEngine. 56 /////////////////////////////////////////////////////////////////////////// 57 LinphoneMediaEngine::LinphoneMediaEngine(const std::string& ringWav, const std::string& callWav) : ring_wav_(ringWav), call_wav_(callWav) { } 58 59 bool LinphoneMediaEngine::Init() { 60 ortp_init(); 61 ms_init(); 62 63 #ifdef HAVE_ILBC 64 #ifndef WIN32 65 char * path = strdup(MSILBC_LIBRARY); 66 char * dirc = dirname(path); 67 ms_load_plugins(dirc); 68 #endif 69 if (ms_filter_codec_supported("iLBC")) 70 have_ilbc = 1; 71 else 72 have_ilbc = 0; 73 #else 74 have_ilbc = 0; 75 #endif 76 77 #ifdef HAVE_SPEEX 78 voice_codecs_.push_back(AudioCodec(110, payload_type_speex_wb.mime_type, payload_type_speex_wb.clock_rate, 0, 1, 8)); 79 voice_codecs_.push_back(AudioCodec(111, payload_type_speex_nb.mime_type, payload_type_speex_nb.clock_rate, 0, 1, 7)); 80 #endif 81 82 #ifdef HAVE_ILBC 83 if (have_ilbc) 84 voice_codecs_.push_back(AudioCodec(102, payload_type_ilbc.mime_type, payload_type_ilbc.clock_rate, 0, 1, 4)); 85 #endif 86 87 voice_codecs_.push_back(AudioCodec(0, payload_type_pcmu8000.mime_type, payload_type_pcmu8000.clock_rate, 0, 1, 2)); 88 voice_codecs_.push_back(AudioCodec(101, payload_type_telephone_event.mime_type, payload_type_telephone_event.clock_rate, 0, 1, 1)); 89 return true; 90 } 91 92 void LinphoneMediaEngine::Terminate() { 93 fflush(stdout); 94 } 95 96 97 int LinphoneMediaEngine::GetCapabilities() { 98 int capabilities = 0; 99 capabilities |= AUDIO_SEND; 100 capabilities |= AUDIO_RECV; 101 return capabilities; 102 } 103 104 VoiceMediaChannel* LinphoneMediaEngine::CreateChannel() { 105 return new LinphoneVoiceChannel(this); 106 } 107 108 VideoMediaChannel* LinphoneMediaEngine::CreateVideoChannel(VoiceMediaChannel* voice_ch) { 109 return NULL; 110 } 111 112 bool LinphoneMediaEngine::FindAudioCodec(const AudioCodec &c) { 113 if (c.id == 0) 114 return true; 115 if (c.name == payload_type_telephone_event.mime_type) 116 return true; 117 #ifdef HAVE_SPEEX 118 if (c.name == payload_type_speex_wb.mime_type && c.clockrate == payload_type_speex_wb.clock_rate) 119 return true; 120 if (c.name == payload_type_speex_nb.mime_type && c.clockrate == payload_type_speex_nb.clock_rate) 121 return true; 122 #endif 123 #ifdef HAVE_ILBC 124 if (have_ilbc && c.name == payload_type_ilbc.mime_type) 125 return true; 126 #endif 127 return false; 128 } 129 130 /////////////////////////////////////////////////////////////////////////// 131 // Implementation of LinphoneVoiceChannel. 132 /////////////////////////////////////////////////////////////////////////// 133 LinphoneVoiceChannel::LinphoneVoiceChannel(LinphoneMediaEngine*eng) 134 : pt_(-1), 135 audio_stream_(0), 136 engine_(eng), 137 ring_stream_(0) 138 { 139 140 rtc::Thread *thread = rtc::ThreadManager::CurrentThread(); 141 rtc::SocketServer *ss = thread->socketserver(); 142 socket_.reset(ss->CreateAsyncSocket(SOCK_DGRAM)); 143 144 socket_->Bind(rtc::SocketAddress("localhost",3000)); 145 socket_->SignalReadEvent.connect(this, &LinphoneVoiceChannel::OnIncomingData); 146 147 } 148 149 LinphoneVoiceChannel::~LinphoneVoiceChannel() 150 { 151 fflush(stdout); 152 StopRing(); 153 154 if (audio_stream_) 155 audio_stream_stop(audio_stream_); 156 } 157 158 bool LinphoneVoiceChannel::SetPlayout(bool playout) { 159 play_ = playout; 160 return true; 161 } 162 163 bool LinphoneVoiceChannel::SetSendCodecs(const std::vector<AudioCodec>& codecs) { 164 165 bool first = true; 166 std::vector<AudioCodec>::const_iterator i; 167 168 ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL); 169 170 for (i = codecs.begin(); i < codecs.end(); i++) { 171 172 if (!engine_->FindAudioCodec(*i)) 173 continue; 174 #ifdef HAVE_ILBC 175 if (engine_->have_ilbc && i->name == payload_type_ilbc.mime_type) { 176 rtp_profile_set_payload(&av_profile, i->id, &payload_type_ilbc); 177 } 178 #endif 179 #ifdef HAVE_SPEEX 180 if (i->name == payload_type_speex_wb.mime_type && i->clockrate == payload_type_speex_wb.clock_rate) { 181 rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_wb); 182 } else if (i->name == payload_type_speex_nb.mime_type && i->clockrate == payload_type_speex_nb.clock_rate) { 183 rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_nb); 184 } 185 #endif 186 187 if (i->id == 0) 188 rtp_profile_set_payload(&av_profile, 0, &payload_type_pcmu8000); 189 190 if (i->name == payload_type_telephone_event.mime_type) { 191 rtp_profile_set_payload(&av_profile, i->id, &payload_type_telephone_event); 192 } 193 194 if (first) { 195 StopRing(); 196 LOG(LS_INFO) << "Using " << i->name << "/" << i->clockrate; 197 pt_ = i->id; 198 audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, i->id, 250, 0); 199 first = false; 200 } 201 } 202 203 if (first) { 204 StopRing(); 205 // We're being asked to set an empty list of codecs. This will only happen when 206 // working with a buggy client; let's try PCMU. 207 LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000"; 208 audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, 0, 250, 0); 209 } 210 211 return true; 212 } 213 214 bool LinphoneVoiceChannel::SetSend(SendFlags flag) { 215 mute_ = !flag; 216 return true; 217 } 218 219 void LinphoneVoiceChannel::OnPacketReceived(rtc::Buffer* packet) { 220 const void* data = packet->data(); 221 int len = packet->length(); 222 uint8 buf[2048]; 223 memcpy(buf, data, len); 224 225 /* We may receive packets with payload type 13: comfort noise. Linphone can't 226 * handle them, so let's ignore those packets. 227 */ 228 int payloadtype = buf[1] & 0x7f; 229 if (play_ && payloadtype != 13) 230 socket_->SendTo(buf, len, rtc::SocketAddress("localhost",2000)); 231 } 232 233 void LinphoneVoiceChannel::StartRing(bool bIncomingCall) 234 { 235 MSSndCard *sndcard = NULL; 236 sndcard=ms_snd_card_manager_get_default_card(ms_snd_card_manager_get()); 237 if (sndcard) 238 { 239 if (bIncomingCall) 240 { 241 if (engine_->GetRingWav().size() > 0) 242 { 243 LOG(LS_VERBOSE) << "incoming ring. sound file: " << engine_->GetRingWav().c_str() << "\n"; 244 ring_stream_ = ring_start (engine_->GetRingWav().c_str(), 1, sndcard); 245 } 246 } 247 else 248 { 249 if (engine_->GetCallWav().size() > 0) 250 { 251 LOG(LS_VERBOSE) << "outgoing ring. sound file: " << engine_->GetCallWav().c_str() << "\n"; 252 ring_stream_ = ring_start (engine_->GetCallWav().c_str(), 1, sndcard); 253 } 254 } 255 } 256 } 257 258 void LinphoneVoiceChannel::StopRing() 259 { 260 if (ring_stream_) { 261 ring_stop(ring_stream_); 262 ring_stream_ = 0; 263 } 264 } 265 266 void LinphoneVoiceChannel::OnIncomingData(rtc::AsyncSocket *s) 267 { 268 char *buf[2048]; 269 int len; 270 len = s->Recv(buf, sizeof(buf)); 271 rtc::Buffer packet(buf, len); 272 if (network_interface_ && !mute_) 273 network_interface_->SendPacket(&packet); 274 } 275 276 } 277