/* * Copyright 2020 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "examples/androidvoip/jni/android_voip_client.h" #include #include #include #include #include #include #include #include #include #include "absl/memory/memory.h" #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "api/task_queue/default_task_queue_factory.h" #include "api/voip/voip_codec.h" #include "api/voip/voip_engine_factory.h" #include "api/voip/voip_network.h" #include "examples/androidvoip/generated_jni/VoipClient_jni.h" #include "rtc_base/logging.h" #include "rtc_base/network.h" #include "rtc_base/socket_server.h" #include "sdk/android/native_api/audio_device_module/audio_device_android.h" #include "sdk/android/native_api/jni/java_types.h" #include "sdk/android/native_api/jni/jvm.h" #include "sdk/android/native_api/jni/scoped_java_ref.h" namespace { #define RUN_ON_VOIP_THREAD(method, ...) \ if (!voip_thread_->IsCurrent()) { \ voip_thread_->PostTask( \ std::bind(&AndroidVoipClient::method, this, ##__VA_ARGS__)); \ return; \ } \ RTC_DCHECK_RUN_ON(voip_thread_.get()); // Connects a UDP socket to a public address and returns the local // address associated with it. Since it binds to the "any" address // internally, it returns the default local address on a multi-homed // endpoint. Implementation copied from // BasicNetworkManager::QueryDefaultLocalAddress. rtc::IPAddress QueryDefaultLocalAddress(int family) { const char kPublicIPv4Host[] = "8.8.8.8"; const char kPublicIPv6Host[] = "2001:4860:4860::8888"; const int kPublicPort = 53; std::unique_ptr thread = rtc::Thread::CreateWithSocketServer(); RTC_DCHECK(thread->socketserver() != nullptr); RTC_DCHECK(family == AF_INET || family == AF_INET6); std::unique_ptr socket( thread->socketserver()->CreateSocket(family, SOCK_DGRAM)); if (!socket) { RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; return rtc::IPAddress(); } auto host = family == AF_INET ? kPublicIPv4Host : kPublicIPv6Host; if (socket->Connect(rtc::SocketAddress(host, kPublicPort)) < 0) { if (socket->GetError() != ENETUNREACH && socket->GetError() != EHOSTUNREACH) { RTC_LOG(LS_INFO) << "Connect failed with " << socket->GetError(); } return rtc::IPAddress(); } return socket->GetLocalAddress().ipaddr(); } // Assigned payload type for supported built-in codecs. PCMU, PCMA, // and G722 have set payload types. Whereas opus, ISAC, and ILBC // have dynamic payload types. enum class PayloadType : int { kPcmu = 0, kPcma = 8, kG722 = 9, kOpus = 96, kIsac = 97, kIlbc = 98, }; // Returns the payload type corresponding to codec_name. Only // supports the built-in codecs. int GetPayloadType(const std::string& codec_name) { RTC_DCHECK(codec_name == "PCMU" || codec_name == "PCMA" || codec_name == "G722" || codec_name == "opus" || codec_name == "ISAC" || codec_name == "ILBC"); if (codec_name == "PCMU") { return static_cast(PayloadType::kPcmu); } else if (codec_name == "PCMA") { return static_cast(PayloadType::kPcma); } else if (codec_name == "G722") { return static_cast(PayloadType::kG722); } else if (codec_name == "opus") { return static_cast(PayloadType::kOpus); } else if (codec_name == "ISAC") { return static_cast(PayloadType::kIsac); } else if (codec_name == "ILBC") { return static_cast(PayloadType::kIlbc); } RTC_DCHECK_NOTREACHED(); return -1; } } // namespace namespace webrtc_examples { void AndroidVoipClient::Init( JNIEnv* env, const webrtc::JavaParamRef& application_context) { webrtc::VoipEngineConfig config; config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); config.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); config.audio_device_module = webrtc::CreateJavaAudioDeviceModule(env, application_context.obj()); config.audio_processing = webrtc::AudioProcessingBuilder().Create(); voip_thread_->Start(); // Due to consistent thread requirement on // modules/audio_device/android/audio_device_template.h, // code is invoked in the context of voip_thread_. voip_thread_->Invoke(RTC_FROM_HERE, [this, &config] { RTC_DCHECK_RUN_ON(voip_thread_.get()); supported_codecs_ = config.encoder_factory->GetSupportedEncoders(); env_ = webrtc::AttachCurrentThreadIfNeeded(); voip_engine_ = webrtc::CreateVoipEngine(std::move(config)); }); } AndroidVoipClient::~AndroidVoipClient() { voip_thread_->Invoke(RTC_FROM_HERE, [this] { RTC_DCHECK_RUN_ON(voip_thread_.get()); JavaVM* jvm = nullptr; env_->GetJavaVM(&jvm); if (!jvm) { RTC_LOG(LS_ERROR) << "Failed to retrieve JVM"; return; } jint res = jvm->DetachCurrentThread(); if (res != JNI_OK) { RTC_LOG(LS_ERROR) << "DetachCurrentThread failed: " << res; } }); voip_thread_->Stop(); } AndroidVoipClient* AndroidVoipClient::Create( JNIEnv* env, const webrtc::JavaParamRef& application_context, const webrtc::JavaParamRef& j_voip_client) { // Using `new` to access a non-public constructor. auto voip_client = absl::WrapUnique(new AndroidVoipClient(env, j_voip_client)); voip_client->Init(env, application_context); return voip_client.release(); } void AndroidVoipClient::GetSupportedCodecs(JNIEnv* env) { RUN_ON_VOIP_THREAD(GetSupportedCodecs, env); std::vector names; for (const webrtc::AudioCodecSpec& spec : supported_codecs_) { names.push_back(spec.format.name); } webrtc::ScopedJavaLocalRef (*convert_function)( JNIEnv*, const std::string&) = &webrtc::NativeToJavaString; Java_VoipClient_onGetSupportedCodecsCompleted( env_, j_voip_client_, NativeToJavaList(env_, names, convert_function)); } void AndroidVoipClient::GetLocalIPAddress(JNIEnv* env) { RUN_ON_VOIP_THREAD(GetLocalIPAddress, env); std::string local_ip_address; rtc::IPAddress ipv4_address = QueryDefaultLocalAddress(AF_INET); if (!ipv4_address.IsNil()) { local_ip_address = ipv4_address.ToString(); } else { rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6); if (!ipv6_address.IsNil()) { local_ip_address = ipv6_address.ToString(); } } Java_VoipClient_onGetLocalIPAddressCompleted( env_, j_voip_client_, webrtc::NativeToJavaString(env_, local_ip_address)); } void AndroidVoipClient::SetEncoder(const std::string& encoder) { RTC_DCHECK_RUN_ON(voip_thread_.get()); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; return; } for (const webrtc::AudioCodecSpec& codec : supported_codecs_) { if (codec.format.name == encoder) { webrtc::VoipResult result = voip_engine_->Codec().SetSendCodec( *channel_, GetPayloadType(codec.format.name), codec.format); RTC_CHECK(result == webrtc::VoipResult::kOk); return; } } } void AndroidVoipClient::SetEncoder( JNIEnv* env, const webrtc::JavaParamRef& j_encoder_string) { const std::string& chosen_encoder = webrtc::JavaToNativeString(env, j_encoder_string); voip_thread_->PostTask( [this, chosen_encoder] { SetEncoder(chosen_encoder); }); } void AndroidVoipClient::SetDecoders(const std::vector& decoders) { RTC_DCHECK_RUN_ON(voip_thread_.get()); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; return; } std::map decoder_specs; for (const webrtc::AudioCodecSpec& codec : supported_codecs_) { if (std::find(decoders.begin(), decoders.end(), codec.format.name) != decoders.end()) { decoder_specs.insert({GetPayloadType(codec.format.name), codec.format}); } } webrtc::VoipResult result = voip_engine_->Codec().SetReceiveCodecs(*channel_, decoder_specs); RTC_CHECK(result == webrtc::VoipResult::kOk); } void AndroidVoipClient::SetDecoders( JNIEnv* env, const webrtc::JavaParamRef& j_decoder_strings) { const std::vector& chosen_decoders = webrtc::JavaListToNativeVector( env, j_decoder_strings, &webrtc::JavaToNativeString); voip_thread_->PostTask( [this, chosen_decoders] { SetDecoders(chosen_decoders); }); } void AndroidVoipClient::SetLocalAddress(const std::string& ip_address, const int port_number) { RTC_DCHECK_RUN_ON(voip_thread_.get()); rtp_local_address_ = rtc::SocketAddress(ip_address, port_number); rtcp_local_address_ = rtc::SocketAddress(ip_address, port_number + 1); } void AndroidVoipClient::SetLocalAddress( JNIEnv* env, const webrtc::JavaParamRef& j_ip_address_string, jint j_port_number_int) { const std::string& ip_address = webrtc::JavaToNativeString(env, j_ip_address_string); voip_thread_->PostTask([this, ip_address, j_port_number_int] { SetLocalAddress(ip_address, j_port_number_int); }); } void AndroidVoipClient::SetRemoteAddress(const std::string& ip_address, const int port_number) { RTC_DCHECK_RUN_ON(voip_thread_.get()); rtp_remote_address_ = rtc::SocketAddress(ip_address, port_number); rtcp_remote_address_ = rtc::SocketAddress(ip_address, port_number + 1); } void AndroidVoipClient::SetRemoteAddress( JNIEnv* env, const webrtc::JavaParamRef& j_ip_address_string, jint j_port_number_int) { const std::string& ip_address = webrtc::JavaToNativeString(env, j_ip_address_string); voip_thread_->PostTask([this, ip_address, j_port_number_int] { SetRemoteAddress(ip_address, j_port_number_int); }); } void AndroidVoipClient::StartSession(JNIEnv* env) { RUN_ON_VOIP_THREAD(StartSession, env); // CreateChannel guarantees to return valid channel id. channel_ = voip_engine_->Base().CreateChannel(this, absl::nullopt); rtp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), rtp_local_address_)); if (!rtp_socket_) { RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } rtp_socket_->SignalReadPacket.connect( this, &AndroidVoipClient::OnSignalReadRTPPacket); rtcp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), rtcp_local_address_)); if (!rtcp_socket_) { RTC_LOG_ERR(LS_ERROR) << "Socket creation failed"; Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } rtcp_socket_->SignalReadPacket.connect( this, &AndroidVoipClient::OnSignalReadRTCPPacket); Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/true); } void AndroidVoipClient::StopSession(JNIEnv* env) { RUN_ON_VOIP_THREAD(StopSession, env); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } if (voip_engine_->Base().StopSend(*channel_) != webrtc::VoipResult::kOk || voip_engine_->Base().StopPlayout(*channel_) != webrtc::VoipResult::kOk) { Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } rtp_socket_->Close(); rtcp_socket_->Close(); webrtc::VoipResult result = voip_engine_->Base().ReleaseChannel(*channel_); RTC_CHECK(result == webrtc::VoipResult::kOk); channel_ = absl::nullopt; Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_, /*isSuccessful=*/true); } void AndroidVoipClient::StartSend(JNIEnv* env) { RUN_ON_VOIP_THREAD(StartSend, env); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; Java_VoipClient_onStartSendCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } bool sending_started = (voip_engine_->Base().StartSend(*channel_) == webrtc::VoipResult::kOk); Java_VoipClient_onStartSendCompleted(env_, j_voip_client_, sending_started); } void AndroidVoipClient::StopSend(JNIEnv* env) { RUN_ON_VOIP_THREAD(StopSend, env); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; Java_VoipClient_onStopSendCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } bool sending_stopped = (voip_engine_->Base().StopSend(*channel_) == webrtc::VoipResult::kOk); Java_VoipClient_onStopSendCompleted(env_, j_voip_client_, sending_stopped); } void AndroidVoipClient::StartPlayout(JNIEnv* env) { RUN_ON_VOIP_THREAD(StartPlayout, env); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; Java_VoipClient_onStartPlayoutCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } bool playout_started = (voip_engine_->Base().StartPlayout(*channel_) == webrtc::VoipResult::kOk); Java_VoipClient_onStartPlayoutCompleted(env_, j_voip_client_, playout_started); } void AndroidVoipClient::StopPlayout(JNIEnv* env) { RUN_ON_VOIP_THREAD(StopPlayout, env); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; Java_VoipClient_onStopPlayoutCompleted(env_, j_voip_client_, /*isSuccessful=*/false); return; } bool playout_stopped = (voip_engine_->Base().StopPlayout(*channel_) == webrtc::VoipResult::kOk); Java_VoipClient_onStopPlayoutCompleted(env_, j_voip_client_, playout_stopped); } void AndroidVoipClient::Delete(JNIEnv* env) { delete this; } void AndroidVoipClient::SendRtpPacket(const std::vector& packet_copy) { RTC_DCHECK_RUN_ON(voip_thread_.get()); if (!rtp_socket_->SendTo(packet_copy.data(), packet_copy.size(), rtp_remote_address_, rtc::PacketOptions())) { RTC_LOG(LS_ERROR) << "Failed to send RTP packet"; } } bool AndroidVoipClient::SendRtp(const uint8_t* packet, size_t length, const webrtc::PacketOptions& options) { std::vector packet_copy(packet, packet + length); voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { SendRtpPacket(packet_copy); }); return true; } void AndroidVoipClient::SendRtcpPacket( const std::vector& packet_copy) { RTC_DCHECK_RUN_ON(voip_thread_.get()); if (!rtcp_socket_->SendTo(packet_copy.data(), packet_copy.size(), rtcp_remote_address_, rtc::PacketOptions())) { RTC_LOG(LS_ERROR) << "Failed to send RTCP packet"; } } bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) { std::vector packet_copy(packet, packet + length); voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { SendRtcpPacket(packet_copy); }); return true; } void AndroidVoipClient::ReadRTPPacket(const std::vector& packet_copy) { RTC_DCHECK_RUN_ON(voip_thread_.get()); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; return; } webrtc::VoipResult result = voip_engine_->Network().ReceivedRTPPacket( *channel_, rtc::ArrayView(packet_copy.data(), packet_copy.size())); RTC_CHECK(result == webrtc::VoipResult::kOk); } void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket, const char* rtp_packet, size_t size, const rtc::SocketAddress& addr, const int64_t& timestamp) { std::vector packet_copy(rtp_packet, rtp_packet + size); voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { ReadRTPPacket(packet_copy); }); } void AndroidVoipClient::ReadRTCPPacket( const std::vector& packet_copy) { RTC_DCHECK_RUN_ON(voip_thread_.get()); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; return; } webrtc::VoipResult result = voip_engine_->Network().ReceivedRTCPPacket( *channel_, rtc::ArrayView(packet_copy.data(), packet_copy.size())); RTC_CHECK(result == webrtc::VoipResult::kOk); } void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket, const char* rtcp_packet, size_t size, const rtc::SocketAddress& addr, const int64_t& timestamp) { std::vector packet_copy(rtcp_packet, rtcp_packet + size); voip_thread_->PostTask([this, packet_copy = std::move(packet_copy)] { ReadRTCPPacket(packet_copy); }); } static jlong JNI_VoipClient_CreateClient( JNIEnv* env, const webrtc::JavaParamRef& application_context, const webrtc::JavaParamRef& j_voip_client) { return webrtc::NativeToJavaPointer( AndroidVoipClient::Create(env, application_context, j_voip_client)); } } // namespace webrtc_examples