1 /*
2 * Copyright 2020 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "examples/androidvoip/jni/android_voip_client.h"
12
13 #include <errno.h>
14 #include <sys/socket.h>
15 #include <algorithm>
16 #include <map>
17 #include <memory>
18 #include <unordered_map>
19 #include <unordered_set>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/memory/memory.h"
24 #include "api/audio_codecs/builtin_audio_decoder_factory.h"
25 #include "api/audio_codecs/builtin_audio_encoder_factory.h"
26 #include "api/task_queue/default_task_queue_factory.h"
27 #include "api/voip/voip_codec.h"
28 #include "api/voip/voip_engine_factory.h"
29 #include "api/voip/voip_network.h"
30 #include "examples/androidvoip/generated_jni/VoipClient_jni.h"
31 #include "rtc_base/logging.h"
32 #include "rtc_base/network.h"
33 #include "rtc_base/socket_server.h"
34 #include "sdk/android/native_api/audio_device_module/audio_device_android.h"
35 #include "sdk/android/native_api/jni/java_types.h"
36
37 namespace {
38
39 // Connects a UDP socket to a public address and returns the local
40 // address associated with it. Since it binds to the "any" address
41 // internally, it returns the default local address on a multi-homed
42 // endpoint. Implementation copied from
43 // BasicNetworkManager::QueryDefaultLocalAddress.
QueryDefaultLocalAddress(int family)44 rtc::IPAddress QueryDefaultLocalAddress(int family) {
45 const char kPublicIPv4Host[] = "8.8.8.8";
46 const char kPublicIPv6Host[] = "2001:4860:4860::8888";
47 const int kPublicPort = 53;
48 std::unique_ptr<rtc::Thread> thread = rtc::Thread::CreateWithSocketServer();
49
50 RTC_DCHECK(thread->socketserver() != nullptr);
51 RTC_DCHECK(family == AF_INET || family == AF_INET6);
52
53 std::unique_ptr<rtc::AsyncSocket> socket(
54 thread->socketserver()->CreateAsyncSocket(family, SOCK_DGRAM));
55 if (!socket) {
56 RTC_LOG_ERR(LERROR) << "Socket creation failed";
57 return rtc::IPAddress();
58 }
59
60 auto host = family == AF_INET ? kPublicIPv4Host : kPublicIPv6Host;
61 if (socket->Connect(rtc::SocketAddress(host, kPublicPort)) < 0) {
62 if (socket->GetError() != ENETUNREACH &&
63 socket->GetError() != EHOSTUNREACH) {
64 RTC_LOG(LS_INFO) << "Connect failed with " << socket->GetError();
65 }
66 return rtc::IPAddress();
67 }
68 return socket->GetLocalAddress().ipaddr();
69 }
70
71 // Assigned payload type for supported built-in codecs. PCMU, PCMA,
72 // and G722 have set payload types. Whereas opus, ISAC, and ILBC
73 // have dynamic payload types.
74 enum class PayloadType : int {
75 kPcmu = 0,
76 kPcma = 8,
77 kG722 = 9,
78 kOpus = 96,
79 kIsac = 97,
80 kIlbc = 98,
81 };
82
83 // Returns the payload type corresponding to codec_name. Only
84 // supports the built-in codecs.
GetPayloadType(const std::string & codec_name)85 int GetPayloadType(const std::string& codec_name) {
86 RTC_DCHECK(codec_name == "PCMU" || codec_name == "PCMA" ||
87 codec_name == "G722" || codec_name == "opus" ||
88 codec_name == "ISAC" || codec_name == "ILBC");
89
90 if (codec_name == "PCMU") {
91 return static_cast<int>(PayloadType::kPcmu);
92 } else if (codec_name == "PCMA") {
93 return static_cast<int>(PayloadType::kPcma);
94 } else if (codec_name == "G722") {
95 return static_cast<int>(PayloadType::kG722);
96 } else if (codec_name == "opus") {
97 return static_cast<int>(PayloadType::kOpus);
98 } else if (codec_name == "ISAC") {
99 return static_cast<int>(PayloadType::kIsac);
100 } else if (codec_name == "ILBC") {
101 return static_cast<int>(PayloadType::kIlbc);
102 }
103
104 RTC_NOTREACHED();
105 return -1;
106 }
107
108 } // namespace
109
110 namespace webrtc_examples {
111
AndroidVoipClient(JNIEnv * env,const webrtc::JavaParamRef<jobject> & application_context)112 AndroidVoipClient::AndroidVoipClient(
113 JNIEnv* env,
114 const webrtc::JavaParamRef<jobject>& application_context) {
115 voip_thread_ = rtc::Thread::CreateWithSocketServer();
116 voip_thread_->Start();
117
118 webrtc::VoipEngineConfig config;
119 config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
120 config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
121 config.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory();
122 config.audio_device_module =
123 webrtc::CreateJavaAudioDeviceModule(env, application_context.obj());
124 config.audio_processing = webrtc::AudioProcessingBuilder().Create();
125
126 supported_codecs_ = config.encoder_factory->GetSupportedEncoders();
127
128 // Due to consistent thread requirement on
129 // modules/audio_device/android/audio_device_template.h,
130 // code is invoked in the context of voip_thread_.
131 voip_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
132 voip_engine_ = webrtc::CreateVoipEngine(std::move(config));
133 if (!voip_engine_) {
134 RTC_LOG(LS_ERROR) << "VoipEngine creation failed";
135 }
136 });
137 }
138
~AndroidVoipClient()139 AndroidVoipClient::~AndroidVoipClient() {
140 voip_thread_->Stop();
141 }
142
Create(JNIEnv * env,const webrtc::JavaParamRef<jobject> & application_context)143 AndroidVoipClient* AndroidVoipClient::Create(
144 JNIEnv* env,
145 const webrtc::JavaParamRef<jobject>& application_context) {
146 // Using `new` to access a non-public constructor.
147 auto voip_client =
148 absl::WrapUnique(new AndroidVoipClient(env, application_context));
149 if (!voip_client->voip_engine_) {
150 return nullptr;
151 }
152 return voip_client.release();
153 }
154
GetSupportedCodecs(JNIEnv * env)155 webrtc::ScopedJavaLocalRef<jobject> AndroidVoipClient::GetSupportedCodecs(
156 JNIEnv* env) {
157 std::vector<std::string> names;
158 for (const webrtc::AudioCodecSpec& spec : supported_codecs_) {
159 names.push_back(spec.format.name);
160 }
161 webrtc::ScopedJavaLocalRef<jstring> (*convert_function)(
162 JNIEnv*, const std::string&) = &webrtc::NativeToJavaString;
163 return NativeToJavaList(env, names, convert_function);
164 }
165
GetLocalIPAddress(JNIEnv * env)166 webrtc::ScopedJavaLocalRef<jstring> AndroidVoipClient::GetLocalIPAddress(
167 JNIEnv* env) {
168 rtc::IPAddress ipv4_address = QueryDefaultLocalAddress(AF_INET);
169 if (!ipv4_address.IsNil()) {
170 return webrtc::NativeToJavaString(env, ipv4_address.ToString());
171 }
172 rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6);
173 if (!ipv6_address.IsNil()) {
174 return webrtc::NativeToJavaString(env, ipv6_address.ToString());
175 }
176 return webrtc::NativeToJavaString(env, "");
177 }
178
SetEncoder(JNIEnv * env,const webrtc::JavaRef<jstring> & j_encoder_string)179 void AndroidVoipClient::SetEncoder(
180 JNIEnv* env,
181 const webrtc::JavaRef<jstring>& j_encoder_string) {
182 if (!channel_) {
183 RTC_LOG(LS_ERROR) << "Channel has not been created";
184 return;
185 }
186 const std::string& chosen_encoder =
187 webrtc::JavaToNativeString(env, j_encoder_string);
188 for (const webrtc::AudioCodecSpec& encoder : supported_codecs_) {
189 if (encoder.format.name == chosen_encoder) {
190 voip_engine_->Codec().SetSendCodec(
191 *channel_, GetPayloadType(encoder.format.name), encoder.format);
192 break;
193 }
194 }
195 }
196
SetDecoders(JNIEnv * env,const webrtc::JavaParamRef<jobject> & j_decoder_strings)197 void AndroidVoipClient::SetDecoders(
198 JNIEnv* env,
199 const webrtc::JavaParamRef<jobject>& j_decoder_strings) {
200 if (!channel_) {
201 RTC_LOG(LS_ERROR) << "Channel has not been created";
202 return;
203 }
204 std::vector<std::string> chosen_decoders =
205 webrtc::JavaListToNativeVector<std::string, jstring>(
206 env, j_decoder_strings, &webrtc::JavaToNativeString);
207 std::map<int, webrtc::SdpAudioFormat> decoder_specs;
208
209 for (const webrtc::AudioCodecSpec& decoder : supported_codecs_) {
210 if (std::find(chosen_decoders.begin(), chosen_decoders.end(),
211 decoder.format.name) != chosen_decoders.end()) {
212 decoder_specs.insert(
213 {GetPayloadType(decoder.format.name), decoder.format});
214 }
215 }
216
217 voip_engine_->Codec().SetReceiveCodecs(*channel_, decoder_specs);
218 }
219
SetLocalAddress(JNIEnv * env,const webrtc::JavaRef<jstring> & j_ip_address_string,jint j_port_number_int)220 void AndroidVoipClient::SetLocalAddress(
221 JNIEnv* env,
222 const webrtc::JavaRef<jstring>& j_ip_address_string,
223 jint j_port_number_int) {
224 const std::string& ip_address =
225 webrtc::JavaToNativeString(env, j_ip_address_string);
226 rtp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int);
227 rtcp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1);
228 }
229
SetRemoteAddress(JNIEnv * env,const webrtc::JavaRef<jstring> & j_ip_address_string,jint j_port_number_int)230 void AndroidVoipClient::SetRemoteAddress(
231 JNIEnv* env,
232 const webrtc::JavaRef<jstring>& j_ip_address_string,
233 jint j_port_number_int) {
234 const std::string& ip_address =
235 webrtc::JavaToNativeString(env, j_ip_address_string);
236 rtp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int);
237 rtcp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1);
238 }
239
StartSession(JNIEnv * env)240 jboolean AndroidVoipClient::StartSession(JNIEnv* env) {
241 // Due to consistent thread requirement on
242 // modules/utility/source/process_thread_impl.cc,
243 // code is invoked in the context of voip_thread_.
244 channel_ = voip_thread_->Invoke<absl::optional<webrtc::ChannelId>>(
245 RTC_FROM_HERE,
246 [this] { return voip_engine_->Base().CreateChannel(this, 0); });
247 if (!channel_) {
248 RTC_LOG(LS_ERROR) << "Channel creation failed";
249 return false;
250 }
251
252 rtp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(),
253 rtp_local_address_));
254 if (!rtp_socket_) {
255 RTC_LOG_ERR(LERROR) << "Socket creation failed";
256 return false;
257 }
258 rtp_socket_->SignalReadPacket.connect(
259 this, &AndroidVoipClient::OnSignalReadRTPPacket);
260
261 rtcp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(),
262 rtcp_local_address_));
263 if (!rtcp_socket_) {
264 RTC_LOG_ERR(LERROR) << "Socket creation failed";
265 return false;
266 }
267 rtcp_socket_->SignalReadPacket.connect(
268 this, &AndroidVoipClient::OnSignalReadRTCPPacket);
269
270 return true;
271 }
272
StopSession(JNIEnv * env)273 jboolean AndroidVoipClient::StopSession(JNIEnv* env) {
274 if (!channel_) {
275 RTC_LOG(LS_ERROR) << "Channel has not been created";
276 return false;
277 }
278 if (!StopSend(env) || !StopPlayout(env)) {
279 return false;
280 }
281
282 rtp_socket_->Close();
283 rtcp_socket_->Close();
284 // Due to consistent thread requirement on
285 // modules/utility/source/process_thread_impl.cc,
286 // code is invoked in the context of voip_thread_.
287 voip_thread_->Invoke<void>(RTC_FROM_HERE, [this] {
288 voip_engine_->Base().ReleaseChannel(*channel_);
289 });
290 channel_ = absl::nullopt;
291 return true;
292 }
293
StartSend(JNIEnv * env)294 jboolean AndroidVoipClient::StartSend(JNIEnv* env) {
295 if (!channel_) {
296 RTC_LOG(LS_ERROR) << "Channel has not been created";
297 return false;
298 }
299 // Due to consistent thread requirement on
300 // modules/audio_device/android/opensles_recorder.cc,
301 // code is invoked in the context of voip_thread_.
302 return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
303 return voip_engine_->Base().StartSend(*channel_);
304 });
305 }
306
StopSend(JNIEnv * env)307 jboolean AndroidVoipClient::StopSend(JNIEnv* env) {
308 if (!channel_) {
309 RTC_LOG(LS_ERROR) << "Channel has not been created";
310 return false;
311 }
312 // Due to consistent thread requirement on
313 // modules/audio_device/android/opensles_recorder.cc,
314 // code is invoked in the context of voip_thread_.
315 return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
316 return voip_engine_->Base().StopSend(*channel_);
317 });
318 }
319
StartPlayout(JNIEnv * env)320 jboolean AndroidVoipClient::StartPlayout(JNIEnv* env) {
321 if (!channel_) {
322 RTC_LOG(LS_ERROR) << "Channel has not been created";
323 return false;
324 }
325 // Due to consistent thread requirement on
326 // modules/audio_device/android/opensles_player.cc,
327 // code is invoked in the context of voip_thread_.
328 return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
329 return voip_engine_->Base().StartPlayout(*channel_);
330 });
331 }
332
StopPlayout(JNIEnv * env)333 jboolean AndroidVoipClient::StopPlayout(JNIEnv* env) {
334 if (!channel_) {
335 RTC_LOG(LS_ERROR) << "Channel has not been created";
336 return false;
337 }
338 // Due to consistent thread requirement on
339 // modules/audio_device/android/opensles_player.cc,
340 // code is invoked in the context of voip_thread_.
341 return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
342 return voip_engine_->Base().StopPlayout(*channel_);
343 });
344 }
345
Delete(JNIEnv * env)346 void AndroidVoipClient::Delete(JNIEnv* env) {
347 delete this;
348 }
349
SendRtp(const uint8_t * packet,size_t length,const webrtc::PacketOptions & options)350 bool AndroidVoipClient::SendRtp(const uint8_t* packet,
351 size_t length,
352 const webrtc::PacketOptions& options) {
353 if (!rtp_socket_->SendTo(packet, length, rtp_remote_address_,
354 rtc::PacketOptions())) {
355 RTC_LOG(LS_ERROR) << "Failed to send RTP packet";
356 return false;
357 }
358 return true;
359 }
360
SendRtcp(const uint8_t * packet,size_t length)361 bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) {
362 if (!rtcp_socket_->SendTo(packet, length, rtcp_remote_address_,
363 rtc::PacketOptions())) {
364 RTC_LOG(LS_ERROR) << "Failed to send RTCP packet";
365 return false;
366 }
367 return true;
368 }
369
OnSignalReadRTPPacket(rtc::AsyncPacketSocket * socket,const char * rtp_packet,size_t size,const rtc::SocketAddress & addr,const int64_t & timestamp)370 void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket,
371 const char* rtp_packet,
372 size_t size,
373 const rtc::SocketAddress& addr,
374 const int64_t& timestamp) {
375 if (!channel_) {
376 RTC_LOG(LS_ERROR) << "Channel has not been created";
377 return;
378 }
379 voip_engine_->Network().ReceivedRTPPacket(
380 *channel_, rtc::ArrayView<const uint8_t>(
381 reinterpret_cast<const uint8_t*>(rtp_packet), size));
382 }
383
OnSignalReadRTCPPacket(rtc::AsyncPacketSocket * socket,const char * rtcp_packet,size_t size,const rtc::SocketAddress & addr,const int64_t & timestamp)384 void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket,
385 const char* rtcp_packet,
386 size_t size,
387 const rtc::SocketAddress& addr,
388 const int64_t& timestamp) {
389 if (!channel_) {
390 RTC_LOG(LS_ERROR) << "Channel has not been created";
391 return;
392 }
393 voip_engine_->Network().ReceivedRTCPPacket(
394 *channel_, rtc::ArrayView<const uint8_t>(
395 reinterpret_cast<const uint8_t*>(rtcp_packet), size));
396 }
397
JNI_VoipClient_CreateClient(JNIEnv * env,const webrtc::JavaParamRef<jobject> & application_context)398 static jlong JNI_VoipClient_CreateClient(
399 JNIEnv* env,
400 const webrtc::JavaParamRef<jobject>& application_context) {
401 return webrtc::NativeToJavaPointer(
402 AndroidVoipClient::Create(env, application_context));
403 }
404
405 } // namespace webrtc_examples
406