Moved Asynchronicity From Java to C++ for AndroidVoip Demo App Moved asynchronicity from Java to C++. Bug: webrtc:11723 Change-Id: I985693dc7d4312b6072314088716167b9cdd9999 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180774 Commit-Queue: Tim Na <natim@webrtc.org> Reviewed-by: Tim Na <natim@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31958}
diff --git a/examples/androidvoip/BUILD.gn b/examples/androidvoip/BUILD.gn index 74341a7..649e601 100644 --- a/examples/androidvoip/BUILD.gn +++ b/examples/androidvoip/BUILD.gn
@@ -26,6 +26,7 @@ ":resources", "//modules/audio_device:audio_device_java", "//rtc_base:base_java", + "//sdk/android:base_java", "//sdk/android:java_audio_device_module_java", "//sdk/android:video_java", "//third_party/android_deps:androidx_core_core_java",
diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java index d787de5..d06d6ad 100644 --- a/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java +++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java
@@ -219,10 +219,12 @@ } private void showToast(String message) { - toast.cancel(); - toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); - toast.setGravity(Gravity.TOP, 0, 200); - toast.show(); + if (toast != null) { + toast.cancel(); + toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.TOP, 0, 200); + toast.show(); + } } @Override
diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java index 2dcbd99..69a993d 100644 --- a/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java +++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java
@@ -15,26 +15,15 @@ import android.os.HandlerThread; import java.util.ArrayList; import java.util.List; +import org.webrtc.CalledByNative; public class VoipClient { - private static final String TAG = "VoipClient"; - - private final HandlerThread thread; - private final Handler handler; - private long nativeClient; private OnVoipClientTaskCompleted listener; public VoipClient(Context applicationContext, OnVoipClientTaskCompleted listener) { this.listener = listener; - thread = new HandlerThread(TAG + "Thread"); - thread.start(); - handler = new Handler(thread.getLooper()); - - handler.post(() -> { - nativeClient = nativeCreateClient(applicationContext); - listener.onVoipClientInitializationCompleted(/* isSuccessful */ nativeClient != 0); - }); + nativeClient = nativeCreateClient(applicationContext, this); } private boolean isInitialized() { @@ -42,147 +31,161 @@ } public void getAndSetUpSupportedCodecs() { - handler.post(() -> { - if (isInitialized()) { - listener.onGetSupportedCodecsCompleted(nativeGetSupportedCodecs(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeGetSupportedCodecs(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void getAndSetUpLocalIPAddress() { - handler.post(() -> { - if (isInitialized()) { - listener.onGetLocalIPAddressCompleted(nativeGetLocalIPAddress(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeGetLocalIPAddress(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void setEncoder(String encoder) { - handler.post(() -> { - if (isInitialized()) { - nativeSetEncoder(nativeClient, encoder); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeSetEncoder(nativeClient, encoder); + } else { + listener.onUninitializedVoipClient(); + } } public void setDecoders(List<String> decoders) { - handler.post(() -> { - if (isInitialized()) { - nativeSetDecoders(nativeClient, decoders); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeSetDecoders(nativeClient, decoders); + } else { + listener.onUninitializedVoipClient(); + } } public void setLocalAddress(String ipAddress, int portNumber) { - handler.post(() -> { - if (isInitialized()) { - nativeSetLocalAddress(nativeClient, ipAddress, portNumber); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeSetLocalAddress(nativeClient, ipAddress, portNumber); + } else { + listener.onUninitializedVoipClient(); + } } public void setRemoteAddress(String ipAddress, int portNumber) { - handler.post(() -> { - if (isInitialized()) { - nativeSetRemoteAddress(nativeClient, ipAddress, portNumber); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeSetRemoteAddress(nativeClient, ipAddress, portNumber); + } else { + listener.onUninitializedVoipClient(); + } } public void startSession() { - handler.post(() -> { - if (isInitialized()) { - listener.onStartSessionCompleted(nativeStartSession(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeStartSession(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void stopSession() { - handler.post(() -> { - if (isInitialized()) { - listener.onStopSessionCompleted(nativeStopSession(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeStopSession(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void startSend() { - handler.post(() -> { - if (isInitialized()) { - listener.onStartSendCompleted(nativeStartSend(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeStartSend(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void stopSend() { - handler.post(() -> { - if (isInitialized()) { - listener.onStopSendCompleted(nativeStopSend(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeStopSend(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void startPlayout() { - handler.post(() -> { - if (isInitialized()) { - listener.onStartPlayoutCompleted(nativeStartPlayout(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeStartPlayout(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void stopPlayout() { - handler.post(() -> { - if (isInitialized()) { - listener.onStopPlayoutCompleted(nativeStopPlayout(nativeClient)); - } else { - listener.onUninitializedVoipClient(); - } - }); + if (isInitialized()) { + nativeStopPlayout(nativeClient); + } else { + listener.onUninitializedVoipClient(); + } } public void close() { - handler.post(() -> { - nativeDelete(nativeClient); - nativeClient = 0; - }); - thread.quitSafely(); + nativeDelete(nativeClient); + nativeClient = 0; } - private static native long nativeCreateClient(Context applicationContext); - private static native List<String> nativeGetSupportedCodecs(long nativeAndroidVoipClient); - private static native String nativeGetLocalIPAddress(long nativeAndroidVoipClient); + @CalledByNative + public void onGetLocalIPAddressCompleted(String localIPAddress) { + listener.onGetLocalIPAddressCompleted(localIPAddress); + } + + @CalledByNative + public void onGetSupportedCodecsCompleted(List<String> supportedCodecs) { + listener.onGetSupportedCodecsCompleted(supportedCodecs); + } + + @CalledByNative + public void onStartSessionCompleted(boolean isSuccessful) { + listener.onStartSessionCompleted(isSuccessful); + } + + @CalledByNative + public void onStopSessionCompleted(boolean isSuccessful) { + listener.onStopSessionCompleted(isSuccessful); + } + + @CalledByNative + public void onStartSendCompleted(boolean isSuccessful) { + listener.onStartSendCompleted(isSuccessful); + } + + @CalledByNative + public void onStopSendCompleted(boolean isSuccessful) { + listener.onStopSendCompleted(isSuccessful); + } + + @CalledByNative + public void onStartPlayoutCompleted(boolean isSuccessful) { + listener.onStartPlayoutCompleted(isSuccessful); + } + + @CalledByNative + public void onStopPlayoutCompleted(boolean isSuccessful) { + listener.onStopPlayoutCompleted(isSuccessful); + } + + private static native long nativeCreateClient( + Context applicationContext, VoipClient javaVoipClient); + private static native void nativeGetSupportedCodecs(long nativeAndroidVoipClient); + private static native void nativeGetLocalIPAddress(long nativeAndroidVoipClient); private static native void nativeSetEncoder(long nativeAndroidVoipClient, String encoder); private static native void nativeSetDecoders(long nativeAndroidVoipClient, List<String> decoders); private static native void nativeSetLocalAddress( long nativeAndroidVoipClient, String ipAddress, int portNumber); private static native void nativeSetRemoteAddress( long nativeAndroidVoipClient, String ipAddress, int portNumber); - private static native boolean nativeStartSession(long nativeAndroidVoipClient); - private static native boolean nativeStopSession(long nativeAndroidVoipClient); - private static native boolean nativeStartSend(long nativeAndroidVoipClient); - private static native boolean nativeStopSend(long nativeAndroidVoipClient); - private static native boolean nativeStartPlayout(long nativeAndroidVoipClient); - private static native boolean nativeStopPlayout(long nativeAndroidVoipClient); + private static native void nativeStartSession(long nativeAndroidVoipClient); + private static native void nativeStopSession(long nativeAndroidVoipClient); + private static native void nativeStartSend(long nativeAndroidVoipClient); + private static native void nativeStopSend(long nativeAndroidVoipClient); + private static native void nativeStartPlayout(long nativeAndroidVoipClient); + private static native void nativeStopPlayout(long nativeAndroidVoipClient); private static native void nativeDelete(long nativeAndroidVoipClient); }
diff --git a/examples/androidvoip/jni/android_voip_client.cc b/examples/androidvoip/jni/android_voip_client.cc index 13cadf2..2ad95bc 100644 --- a/examples/androidvoip/jni/android_voip_client.cc +++ b/examples/androidvoip/jni/android_voip_client.cc
@@ -33,9 +33,20 @@ #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( \ + RTC_FROM_HERE, \ + 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 @@ -109,12 +120,9 @@ namespace webrtc_examples { -AndroidVoipClient::AndroidVoipClient( +bool AndroidVoipClient::Init( JNIEnv* env, const webrtc::JavaParamRef<jobject>& application_context) { - voip_thread_ = rtc::Thread::CreateWithSocketServer(); - voip_thread_->Start(); - webrtc::VoipEngineConfig config; config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); @@ -123,137 +131,195 @@ webrtc::CreateJavaAudioDeviceModule(env, application_context.obj()); config.audio_processing = webrtc::AudioProcessingBuilder().Create(); - supported_codecs_ = config.encoder_factory->GetSupportedEncoders(); - + 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<void>(RTC_FROM_HERE, [&] { + return voip_thread_->Invoke<bool>(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)); if (!voip_engine_) { RTC_LOG(LS_ERROR) << "VoipEngine creation failed"; + return false; } + return true; }); } AndroidVoipClient::~AndroidVoipClient() { + voip_thread_->Invoke<void>(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<jobject>& application_context) { + const webrtc::JavaParamRef<jobject>& application_context, + const webrtc::JavaParamRef<jobject>& j_voip_client) { // Using `new` to access a non-public constructor. auto voip_client = - absl::WrapUnique(new AndroidVoipClient(env, application_context)); - if (!voip_client->voip_engine_) { + absl::WrapUnique(new AndroidVoipClient(env, j_voip_client)); + if (!voip_client->Init(env, application_context)) { return nullptr; } return voip_client.release(); } -webrtc::ScopedJavaLocalRef<jobject> AndroidVoipClient::GetSupportedCodecs( - JNIEnv* env) { +void AndroidVoipClient::GetSupportedCodecs(JNIEnv* env) { + RUN_ON_VOIP_THREAD(GetSupportedCodecs, env); + std::vector<std::string> names; for (const webrtc::AudioCodecSpec& spec : supported_codecs_) { names.push_back(spec.format.name); } webrtc::ScopedJavaLocalRef<jstring> (*convert_function)( JNIEnv*, const std::string&) = &webrtc::NativeToJavaString; - return NativeToJavaList(env, names, convert_function); + Java_VoipClient_onGetSupportedCodecsCompleted( + env_, j_voip_client_, NativeToJavaList(env_, names, convert_function)); } -webrtc::ScopedJavaLocalRef<jstring> AndroidVoipClient::GetLocalIPAddress( - JNIEnv* env) { +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()) { - return webrtc::NativeToJavaString(env, ipv4_address.ToString()); + local_ip_address = ipv4_address.ToString(); + } else { + rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6); + if (!ipv6_address.IsNil()) { + local_ip_address = ipv6_address.ToString(); + } } - rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6); - if (!ipv6_address.IsNil()) { - return webrtc::NativeToJavaString(env, ipv6_address.ToString()); - } - return webrtc::NativeToJavaString(env, ""); + Java_VoipClient_onGetLocalIPAddressCompleted( + env_, j_voip_client_, webrtc::NativeToJavaString(env_, local_ip_address)); } -void AndroidVoipClient::SetEncoder( - JNIEnv* env, - const webrtc::JavaRef<jstring>& j_encoder_string) { +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; } - const std::string& chosen_encoder = - webrtc::JavaToNativeString(env, j_encoder_string); - for (const webrtc::AudioCodecSpec& encoder : supported_codecs_) { - if (encoder.format.name == chosen_encoder) { + for (const webrtc::AudioCodecSpec& codec : supported_codecs_) { + if (codec.format.name == encoder) { voip_engine_->Codec().SetSendCodec( - *channel_, GetPayloadType(encoder.format.name), encoder.format); - break; + *channel_, GetPayloadType(codec.format.name), codec.format); + return; } } } -void AndroidVoipClient::SetDecoders( +void AndroidVoipClient::SetEncoder( JNIEnv* env, - const webrtc::JavaParamRef<jobject>& j_decoder_strings) { + const webrtc::JavaParamRef<jstring>& j_encoder_string) { + const std::string& chosen_encoder = + webrtc::JavaToNativeString(env, j_encoder_string); + voip_thread_->PostTask( + RTC_FROM_HERE, [this, chosen_encoder] { SetEncoder(chosen_encoder); }); +} + +void AndroidVoipClient::SetDecoders(const std::vector<std::string>& decoders) { + RTC_DCHECK_RUN_ON(voip_thread_.get()); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; return; } - std::vector<std::string> chosen_decoders = - webrtc::JavaListToNativeVector<std::string, jstring>( - env, j_decoder_strings, &webrtc::JavaToNativeString); std::map<int, webrtc::SdpAudioFormat> decoder_specs; - - for (const webrtc::AudioCodecSpec& decoder : supported_codecs_) { - if (std::find(chosen_decoders.begin(), chosen_decoders.end(), - decoder.format.name) != chosen_decoders.end()) { - decoder_specs.insert( - {GetPayloadType(decoder.format.name), decoder.format}); + 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}); } } voip_engine_->Codec().SetReceiveCodecs(*channel_, decoder_specs); } +void AndroidVoipClient::SetDecoders( + JNIEnv* env, + const webrtc::JavaParamRef<jobject>& j_decoder_strings) { + const std::vector<std::string>& chosen_decoders = + webrtc::JavaListToNativeVector<std::string, jstring>( + env, j_decoder_strings, &webrtc::JavaToNativeString); + voip_thread_->PostTask( + RTC_FROM_HERE, [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::JavaRef<jstring>& j_ip_address_string, + const webrtc::JavaParamRef<jstring>& j_ip_address_string, jint j_port_number_int) { const std::string& ip_address = webrtc::JavaToNativeString(env, j_ip_address_string); - rtp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int); - rtcp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1); + voip_thread_->PostTask(RTC_FROM_HERE, [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::JavaRef<jstring>& j_ip_address_string, + const webrtc::JavaParamRef<jstring>& j_ip_address_string, jint j_port_number_int) { const std::string& ip_address = webrtc::JavaToNativeString(env, j_ip_address_string); - rtp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int); - rtcp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1); + voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] { + SetRemoteAddress(ip_address, j_port_number_int); + }); } -jboolean AndroidVoipClient::StartSession(JNIEnv* env) { - // Due to consistent thread requirement on - // modules/utility/source/process_thread_impl.cc, - // code is invoked in the context of voip_thread_. - channel_ = voip_thread_->Invoke<absl::optional<webrtc::ChannelId>>( - RTC_FROM_HERE, - [this] { return voip_engine_->Base().CreateChannel(this, 0); }); +void AndroidVoipClient::StartSession(JNIEnv* env) { + RUN_ON_VOIP_THREAD(StartSession, env); + + channel_ = voip_engine_->Base().CreateChannel(this, absl::nullopt); if (!channel_) { RTC_LOG(LS_ERROR) << "Channel creation failed"; - return false; + Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } rtp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), rtp_local_address_)); if (!rtp_socket_) { RTC_LOG_ERR(LERROR) << "Socket creation failed"; - return false; + Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } rtp_socket_->SignalReadPacket.connect( this, &AndroidVoipClient::OnSignalReadRTPPacket); @@ -262,123 +328,171 @@ rtcp_local_address_)); if (!rtcp_socket_) { RTC_LOG_ERR(LERROR) << "Socket creation failed"; - return false; + Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } rtcp_socket_->SignalReadPacket.connect( this, &AndroidVoipClient::OnSignalReadRTCPPacket); - - return true; + Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/true); } -jboolean AndroidVoipClient::StopSession(JNIEnv* env) { +void AndroidVoipClient::StopSession(JNIEnv* env) { + RUN_ON_VOIP_THREAD(StopSession, env); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; - return false; + Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } - if (!StopSend(env) || !StopPlayout(env)) { - return false; + if (!voip_engine_->Base().StopSend(*channel_) || + !voip_engine_->Base().StopPlayout(*channel_)) { + Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } rtp_socket_->Close(); rtcp_socket_->Close(); - // Due to consistent thread requirement on - // modules/utility/source/process_thread_impl.cc, - // code is invoked in the context of voip_thread_. - voip_thread_->Invoke<void>(RTC_FROM_HERE, [this] { - voip_engine_->Base().ReleaseChannel(*channel_); - }); + + voip_engine_->Base().ReleaseChannel(*channel_); channel_ = absl::nullopt; - return true; + Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_, + /*isSuccessful=*/true); } -jboolean AndroidVoipClient::StartSend(JNIEnv* env) { +void AndroidVoipClient::StartSend(JNIEnv* env) { + RUN_ON_VOIP_THREAD(StartSend, env); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; - return false; + Java_VoipClient_onStartSendCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } - // Due to consistent thread requirement on - // modules/audio_device/android/opensles_recorder.cc, - // code is invoked in the context of voip_thread_. - return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] { - return voip_engine_->Base().StartSend(*channel_); - }); + Java_VoipClient_onStartSendCompleted( + env_, j_voip_client_, voip_engine_->Base().StartSend(*channel_)); } -jboolean AndroidVoipClient::StopSend(JNIEnv* env) { +void AndroidVoipClient::StopSend(JNIEnv* env) { + RUN_ON_VOIP_THREAD(StopSend, env); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; - return false; + Java_VoipClient_onStopSendCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } - // Due to consistent thread requirement on - // modules/audio_device/android/opensles_recorder.cc, - // code is invoked in the context of voip_thread_. - return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] { - return voip_engine_->Base().StopSend(*channel_); - }); + Java_VoipClient_onStopSendCompleted(env_, j_voip_client_, + voip_engine_->Base().StopSend(*channel_)); } -jboolean AndroidVoipClient::StartPlayout(JNIEnv* env) { +void AndroidVoipClient::StartPlayout(JNIEnv* env) { + RUN_ON_VOIP_THREAD(StartPlayout, env); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; - return false; + Java_VoipClient_onStartPlayoutCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } - // Due to consistent thread requirement on - // modules/audio_device/android/opensles_player.cc, - // code is invoked in the context of voip_thread_. - return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] { - return voip_engine_->Base().StartPlayout(*channel_); - }); + Java_VoipClient_onStartPlayoutCompleted( + env_, j_voip_client_, voip_engine_->Base().StartPlayout(*channel_)); } -jboolean AndroidVoipClient::StopPlayout(JNIEnv* env) { +void AndroidVoipClient::StopPlayout(JNIEnv* env) { + RUN_ON_VOIP_THREAD(StopPlayout, env); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; - return false; + Java_VoipClient_onStopPlayoutCompleted(env_, j_voip_client_, + /*isSuccessful=*/false); + return; } - // Due to consistent thread requirement on - // modules/audio_device/android/opensles_player.cc, - // code is invoked in the context of voip_thread_. - return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] { - return voip_engine_->Base().StopPlayout(*channel_); - }); + Java_VoipClient_onStopPlayoutCompleted( + env_, j_voip_client_, voip_engine_->Base().StopPlayout(*channel_)); } void AndroidVoipClient::Delete(JNIEnv* env) { delete this; } +void AndroidVoipClient::SendRtpPacket(const std::vector<uint8_t>& 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) { - if (!rtp_socket_->SendTo(packet, length, rtp_remote_address_, - rtc::PacketOptions())) { - RTC_LOG(LS_ERROR) << "Failed to send RTP packet"; - return false; - } + std::vector<uint8_t> packet_copy(packet, packet + length); + voip_thread_->PostTask(RTC_FROM_HERE, + [this, packet_copy = std::move(packet_copy)] { + SendRtpPacket(packet_copy); + }); return true; } -bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) { - if (!rtcp_socket_->SendTo(packet, length, rtcp_remote_address_, - rtc::PacketOptions())) { +void AndroidVoipClient::SendRtcpPacket( + const std::vector<uint8_t>& 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"; - return false; } +} + +bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) { + std::vector<uint8_t> packet_copy(packet, packet + length); + voip_thread_->PostTask(RTC_FROM_HERE, + [this, packet_copy = std::move(packet_copy)] { + SendRtcpPacket(packet_copy); + }); return true; } +void AndroidVoipClient::ReadRTPPacket(const std::vector<uint8_t>& packet_copy) { + RTC_DCHECK_RUN_ON(voip_thread_.get()); + + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel has not been created"; + return; + } + voip_engine_->Network().ReceivedRTPPacket( + *channel_, + rtc::ArrayView<const uint8_t>(packet_copy.data(), packet_copy.size())); +} + void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket, const char* rtp_packet, size_t size, const rtc::SocketAddress& addr, const int64_t& timestamp) { + std::vector<uint8_t> packet_copy(rtp_packet, rtp_packet + size); + voip_thread_->PostTask(RTC_FROM_HERE, + [this, packet_copy = std::move(packet_copy)] { + ReadRTPPacket(packet_copy); + }); +} + +void AndroidVoipClient::ReadRTCPPacket( + const std::vector<uint8_t>& packet_copy) { + RTC_DCHECK_RUN_ON(voip_thread_.get()); + if (!channel_) { RTC_LOG(LS_ERROR) << "Channel has not been created"; return; } - voip_engine_->Network().ReceivedRTPPacket( - *channel_, rtc::ArrayView<const uint8_t>( - reinterpret_cast<const uint8_t*>(rtp_packet), size)); + voip_engine_->Network().ReceivedRTCPPacket( + *channel_, + rtc::ArrayView<const uint8_t>(packet_copy.data(), packet_copy.size())); } void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket, @@ -386,20 +500,19 @@ size_t size, const rtc::SocketAddress& addr, const int64_t& timestamp) { - if (!channel_) { - RTC_LOG(LS_ERROR) << "Channel has not been created"; - return; - } - voip_engine_->Network().ReceivedRTCPPacket( - *channel_, rtc::ArrayView<const uint8_t>( - reinterpret_cast<const uint8_t*>(rtcp_packet), size)); + std::vector<uint8_t> packet_copy(rtcp_packet, rtcp_packet + size); + voip_thread_->PostTask(RTC_FROM_HERE, + [this, packet_copy = std::move(packet_copy)] { + ReadRTCPPacket(packet_copy); + }); } static jlong JNI_VoipClient_CreateClient( JNIEnv* env, - const webrtc::JavaParamRef<jobject>& application_context) { + const webrtc::JavaParamRef<jobject>& application_context, + const webrtc::JavaParamRef<jobject>& j_voip_client) { return webrtc::NativeToJavaPointer( - AndroidVoipClient::Create(env, application_context)); + AndroidVoipClient::Create(env, application_context, j_voip_client)); } } // namespace webrtc_examples
diff --git a/examples/androidvoip/jni/android_voip_client.h b/examples/androidvoip/jni/android_voip_client.h index aed652e..4dd0b0a 100644 --- a/examples/androidvoip/jni/android_voip_client.h +++ b/examples/androidvoip/jni/android_voip_client.h
@@ -36,10 +36,10 @@ // webrtc::Transport to send RTP/RTCP packets to the remote endpoint. // It also creates methods (slots) for sockets to connect to in // order to receive RTP/RTCP packets. AndroidVoipClient does all -// VoipBase related operations with rtc::Thread (voip_thread_), this -// is to comply with consistent thread usage requirement with -// ProcessThread used within VoipEngine. AndroidVoipClient is meant -// to be used by Java through JNI. +// operations with rtc::Thread (voip_thread_), this is to comply +// with consistent thread usage requirement with ProcessThread used +// within VoipEngine, as well as providing asynchronicity to the +// caller. AndroidVoipClient is meant to be used by Java through JNI. class AndroidVoipClient : public webrtc::Transport, public sigslot::has_slots<> { public: @@ -50,22 +50,24 @@ // they are done with it (this class provides a Delete() method). static AndroidVoipClient* Create( JNIEnv* env, - const webrtc::JavaParamRef<jobject>& application_context); + const webrtc::JavaParamRef<jobject>& application_context, + const webrtc::JavaParamRef<jobject>& j_voip_client); ~AndroidVoipClient() override; - // Returns a Java List of Strings containing names of the built-in - // supported codecs. - webrtc::ScopedJavaLocalRef<jobject> GetSupportedCodecs(JNIEnv* env); + // Provides client with a Java List of Strings containing names of + // the built-in supported codecs through callback. + void GetSupportedCodecs(JNIEnv* env); - // Returns a Java String of the default local IPv4 address. If IPv4 - // address is not found, returns the default local IPv6 address. If - // IPv6 address is not found, returns an empty string. - webrtc::ScopedJavaLocalRef<jstring> GetLocalIPAddress(JNIEnv* env); + // Provides client with a Java String of the default local IPv4 address + // through callback. If IPv4 address is not found, provide the default + // local IPv6 address. If IPv6 address is not found, provide an empty + // string. + void GetLocalIPAddress(JNIEnv* env); // Sets the encoder used by the VoIP API. void SetEncoder(JNIEnv* env, - const webrtc::JavaRef<jstring>& j_encoder_string); + const webrtc::JavaParamRef<jstring>& j_encoder_string); // Sets the decoders used by the VoIP API. void SetDecoders(JNIEnv* env, @@ -76,36 +78,41 @@ // and port number j_port_number_int, the RTCP address will have IP address // j_ip_address_string and port number j_port_number_int+1. void SetLocalAddress(JNIEnv* env, - const webrtc::JavaRef<jstring>& j_ip_address_string, + const webrtc::JavaParamRef<jstring>& j_ip_address_string, jint j_port_number_int); - void SetRemoteAddress(JNIEnv* env, - const webrtc::JavaRef<jstring>& j_ip_address_string, - jint j_port_number_int); + void SetRemoteAddress( + JNIEnv* env, + const webrtc::JavaParamRef<jstring>& j_ip_address_string, + jint j_port_number_int); - // Starts a VoIP session. The VoIP operations below can only be - // used after a session has already started. Returns true if session - // started successfully and false otherwise. - jboolean StartSession(JNIEnv* env); + // Starts a VoIP session, then calls a callback method with a boolean + // value indicating if the session has started successfully. The VoIP + // operations below can only be used after a session has already started. + void StartSession(JNIEnv* env); - // Stops the current session. Returns true if session stopped - // successfully and false otherwise. - jboolean StopSession(JNIEnv* env); + // Stops the current session, then calls a callback method with a + // boolean value indicating if the session has stopped successfully. + void StopSession(JNIEnv* env); - // Starts sending RTP/RTCP packets to the remote endpoint. Returns - // the return value of StartSend in api/voip/voip_base.h. - jboolean StartSend(JNIEnv* env); + // Starts sending RTP/RTCP packets to the remote endpoint, then calls + // a callback method with a boolean value indicating if sending + // has started successfully. + void StartSend(JNIEnv* env); - // Stops sending RTP/RTCP packets to the remote endpoint. Returns - // the return value of StopSend in api/voip/voip_base.h. - jboolean StopSend(JNIEnv* env); + // Stops sending RTP/RTCP packets to the remote endpoint, then calls + // a callback method with a boolean value indicating if sending + // has stopped successfully. + void StopSend(JNIEnv* env); - // Starts playing out the voice data received from the remote endpoint. - // Returns the return value of StartPlayout in api/voip/voip_base.h. - jboolean StartPlayout(JNIEnv* env); + // Starts playing out the voice data received from the remote endpoint, + // then calls a callback method with a boolean value indicating if + // playout has started successfully. + void StartPlayout(JNIEnv* env); - // Stops playing out the voice data received from the remote endpoint. - // Returns the return value of StopPlayout in api/voip/voip_base.h. - jboolean StopPlayout(JNIEnv* env); + // Stops playing out the voice data received from the remote endpoint, + // then calls a callback method with a boolean value indicating if + // playout has stopped successfully. + void StopPlayout(JNIEnv* env); // Deletes this object. Used by client when they are done. void Delete(JNIEnv* env); @@ -130,25 +137,51 @@ private: AndroidVoipClient(JNIEnv* env, - const webrtc::JavaParamRef<jobject>& application_context); + const webrtc::JavaParamRef<jobject>& j_voip_client) + : voip_thread_(rtc::Thread::CreateWithSocketServer()), + j_voip_client_(env, j_voip_client) {} - // Used to invoke VoipBase operations and send/receive - // RTP/RTCP packets. + bool Init(JNIEnv* env, + const webrtc::JavaParamRef<jobject>& application_context); + + // Overloaded methods having native C++ variables as arguments. + void SetEncoder(const std::string& encoder); + void SetDecoders(const std::vector<std::string>& decoders); + void SetLocalAddress(const std::string& ip_address, const int port_number); + void SetRemoteAddress(const std::string& ip_address, const int port_number); + + // Methods to send and receive RTP/RTCP packets. Takes in a + // copy of a packet as a vector to prolong the lifetime of + // the packet as these methods will be called asynchronously. + void SendRtpPacket(const std::vector<uint8_t>& packet_copy); + void SendRtcpPacket(const std::vector<uint8_t>& packet_copy); + void ReadRTPPacket(const std::vector<uint8_t>& packet_copy); + void ReadRTCPPacket(const std::vector<uint8_t>& packet_copy); + + // Used to invoke operations and send/receive RTP/RTCP packets. std::unique_ptr<rtc::Thread> voip_thread_; + // Reference to the VoipClient java instance used to + // invoke callbacks when operations are finished. + webrtc::ScopedJavaGlobalRef<jobject> j_voip_client_ + RTC_GUARDED_BY(voip_thread_); // A list of AudioCodecSpec supported by the built-in // encoder/decoder factories. - std::vector<webrtc::AudioCodecSpec> supported_codecs_; + std::vector<webrtc::AudioCodecSpec> supported_codecs_ + RTC_GUARDED_BY(voip_thread_); + // A JNI context used by the voip_thread_. + JNIEnv* env_ RTC_GUARDED_BY(voip_thread_); // The entry point to all VoIP APIs. - std::unique_ptr<webrtc::VoipEngine> voip_engine_; + std::unique_ptr<webrtc::VoipEngine> voip_engine_ RTC_GUARDED_BY(voip_thread_); // Used by the VoIP API to facilitate a VoIP session. - absl::optional<webrtc::ChannelId> channel_; + absl::optional<webrtc::ChannelId> channel_ RTC_GUARDED_BY(voip_thread_); // Members below are used for network related operations. - std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_; - std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_; - rtc::SocketAddress rtp_local_address_; - rtc::SocketAddress rtcp_local_address_; - rtc::SocketAddress rtp_remote_address_; - rtc::SocketAddress rtcp_remote_address_; + std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_ RTC_GUARDED_BY(voip_thread_); + std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_ + RTC_GUARDED_BY(voip_thread_); + rtc::SocketAddress rtp_local_address_ RTC_GUARDED_BY(voip_thread_); + rtc::SocketAddress rtcp_local_address_ RTC_GUARDED_BY(voip_thread_); + rtc::SocketAddress rtp_remote_address_ RTC_GUARDED_BY(voip_thread_); + rtc::SocketAddress rtcp_remote_address_ RTC_GUARDED_BY(voip_thread_); }; } // namespace webrtc_examples