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}
This commit is contained in:
Jason Long
2020-08-17 17:23:07 -04:00
committed by Commit Bot
parent 552d3e3d5e
commit 577fc0c395
5 changed files with 438 additions and 286 deletions

View File

@ -26,6 +26,7 @@ if (is_android) {
":resources", ":resources",
"//modules/audio_device:audio_device_java", "//modules/audio_device:audio_device_java",
"//rtc_base:base_java", "//rtc_base:base_java",
"//sdk/android:base_java",
"//sdk/android:java_audio_device_module_java", "//sdk/android:java_audio_device_module_java",
"//sdk/android:video_java", "//sdk/android:video_java",
"//third_party/android_deps:androidx_core_core_java", "//third_party/android_deps:androidx_core_core_java",

View File

@ -219,10 +219,12 @@ public class MainActivity extends Activity implements OnVoipClientTaskCompleted
} }
private void showToast(String message) { private void showToast(String message) {
toast.cancel(); if (toast != null) {
toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); toast.cancel();
toast.setGravity(Gravity.TOP, 0, 200); toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
toast.show(); toast.setGravity(Gravity.TOP, 0, 200);
toast.show();
}
} }
@Override @Override

View File

@ -15,26 +15,15 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.webrtc.CalledByNative;
public class VoipClient { public class VoipClient {
private static final String TAG = "VoipClient";
private final HandlerThread thread;
private final Handler handler;
private long nativeClient; private long nativeClient;
private OnVoipClientTaskCompleted listener; private OnVoipClientTaskCompleted listener;
public VoipClient(Context applicationContext, OnVoipClientTaskCompleted listener) { public VoipClient(Context applicationContext, OnVoipClientTaskCompleted listener) {
this.listener = listener; this.listener = listener;
thread = new HandlerThread(TAG + "Thread"); nativeClient = nativeCreateClient(applicationContext, this);
thread.start();
handler = new Handler(thread.getLooper());
handler.post(() -> {
nativeClient = nativeCreateClient(applicationContext);
listener.onVoipClientInitializationCompleted(/* isSuccessful */ nativeClient != 0);
});
} }
private boolean isInitialized() { private boolean isInitialized() {
@ -42,147 +31,161 @@ public class VoipClient {
} }
public void getAndSetUpSupportedCodecs() { public void getAndSetUpSupportedCodecs() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeGetSupportedCodecs(nativeClient);
listener.onGetSupportedCodecsCompleted(nativeGetSupportedCodecs(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void getAndSetUpLocalIPAddress() { public void getAndSetUpLocalIPAddress() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeGetLocalIPAddress(nativeClient);
listener.onGetLocalIPAddressCompleted(nativeGetLocalIPAddress(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void setEncoder(String encoder) { public void setEncoder(String encoder) {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeSetEncoder(nativeClient, encoder);
nativeSetEncoder(nativeClient, encoder); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void setDecoders(List<String> decoders) { public void setDecoders(List<String> decoders) {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeSetDecoders(nativeClient, decoders);
nativeSetDecoders(nativeClient, decoders); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void setLocalAddress(String ipAddress, int portNumber) { public void setLocalAddress(String ipAddress, int portNumber) {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeSetLocalAddress(nativeClient, ipAddress, portNumber);
nativeSetLocalAddress(nativeClient, ipAddress, portNumber); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void setRemoteAddress(String ipAddress, int portNumber) { public void setRemoteAddress(String ipAddress, int portNumber) {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeSetRemoteAddress(nativeClient, ipAddress, portNumber);
nativeSetRemoteAddress(nativeClient, ipAddress, portNumber); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void startSession() { public void startSession() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeStartSession(nativeClient);
listener.onStartSessionCompleted(nativeStartSession(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void stopSession() { public void stopSession() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeStopSession(nativeClient);
listener.onStopSessionCompleted(nativeStopSession(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void startSend() { public void startSend() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeStartSend(nativeClient);
listener.onStartSendCompleted(nativeStartSend(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void stopSend() { public void stopSend() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeStopSend(nativeClient);
listener.onStopSendCompleted(nativeStopSend(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void startPlayout() { public void startPlayout() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeStartPlayout(nativeClient);
listener.onStartPlayoutCompleted(nativeStartPlayout(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void stopPlayout() { public void stopPlayout() {
handler.post(() -> { if (isInitialized()) {
if (isInitialized()) { nativeStopPlayout(nativeClient);
listener.onStopPlayoutCompleted(nativeStopPlayout(nativeClient)); } else {
} else { listener.onUninitializedVoipClient();
listener.onUninitializedVoipClient(); }
}
});
} }
public void close() { public void close() {
handler.post(() -> { nativeDelete(nativeClient);
nativeDelete(nativeClient); nativeClient = 0;
nativeClient = 0;
});
thread.quitSafely();
} }
private static native long nativeCreateClient(Context applicationContext); @CalledByNative
private static native List<String> nativeGetSupportedCodecs(long nativeAndroidVoipClient); public void onGetLocalIPAddressCompleted(String localIPAddress) {
private static native String nativeGetLocalIPAddress(long nativeAndroidVoipClient); 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 nativeSetEncoder(long nativeAndroidVoipClient, String encoder);
private static native void nativeSetDecoders(long nativeAndroidVoipClient, List<String> decoders); private static native void nativeSetDecoders(long nativeAndroidVoipClient, List<String> decoders);
private static native void nativeSetLocalAddress( private static native void nativeSetLocalAddress(
long nativeAndroidVoipClient, String ipAddress, int portNumber); long nativeAndroidVoipClient, String ipAddress, int portNumber);
private static native void nativeSetRemoteAddress( private static native void nativeSetRemoteAddress(
long nativeAndroidVoipClient, String ipAddress, int portNumber); long nativeAndroidVoipClient, String ipAddress, int portNumber);
private static native boolean nativeStartSession(long nativeAndroidVoipClient); private static native void nativeStartSession(long nativeAndroidVoipClient);
private static native boolean nativeStopSession(long nativeAndroidVoipClient); private static native void nativeStopSession(long nativeAndroidVoipClient);
private static native boolean nativeStartSend(long nativeAndroidVoipClient); private static native void nativeStartSend(long nativeAndroidVoipClient);
private static native boolean nativeStopSend(long nativeAndroidVoipClient); private static native void nativeStopSend(long nativeAndroidVoipClient);
private static native boolean nativeStartPlayout(long nativeAndroidVoipClient); private static native void nativeStartPlayout(long nativeAndroidVoipClient);
private static native boolean nativeStopPlayout(long nativeAndroidVoipClient); private static native void nativeStopPlayout(long nativeAndroidVoipClient);
private static native void nativeDelete(long nativeAndroidVoipClient); private static native void nativeDelete(long nativeAndroidVoipClient);
} }

View File

@ -33,9 +33,20 @@
#include "rtc_base/socket_server.h" #include "rtc_base/socket_server.h"
#include "sdk/android/native_api/audio_device_module/audio_device_android.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/java_types.h"
#include "sdk/android/native_api/jni/jvm.h"
#include "sdk/android/native_api/jni/scoped_java_ref.h"
namespace { 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 // Connects a UDP socket to a public address and returns the local
// address associated with it. Since it binds to the "any" address // address associated with it. Since it binds to the "any" address
// internally, it returns the default local address on a multi-homed // internally, it returns the default local address on a multi-homed
@ -109,12 +120,9 @@ int GetPayloadType(const std::string& codec_name) {
namespace webrtc_examples { namespace webrtc_examples {
AndroidVoipClient::AndroidVoipClient( bool AndroidVoipClient::Init(
JNIEnv* env, JNIEnv* env,
const webrtc::JavaParamRef<jobject>& application_context) { const webrtc::JavaParamRef<jobject>& application_context) {
voip_thread_ = rtc::Thread::CreateWithSocketServer();
voip_thread_->Start();
webrtc::VoipEngineConfig config; webrtc::VoipEngineConfig config;
config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
@ -123,137 +131,195 @@ AndroidVoipClient::AndroidVoipClient(
webrtc::CreateJavaAudioDeviceModule(env, application_context.obj()); webrtc::CreateJavaAudioDeviceModule(env, application_context.obj());
config.audio_processing = webrtc::AudioProcessingBuilder().Create(); config.audio_processing = webrtc::AudioProcessingBuilder().Create();
supported_codecs_ = config.encoder_factory->GetSupportedEncoders(); voip_thread_->Start();
// Due to consistent thread requirement on // Due to consistent thread requirement on
// modules/audio_device/android/audio_device_template.h, // modules/audio_device/android/audio_device_template.h,
// code is invoked in the context of voip_thread_. // 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)); voip_engine_ = webrtc::CreateVoipEngine(std::move(config));
if (!voip_engine_) { if (!voip_engine_) {
RTC_LOG(LS_ERROR) << "VoipEngine creation failed"; RTC_LOG(LS_ERROR) << "VoipEngine creation failed";
return false;
} }
return true;
}); });
} }
AndroidVoipClient::~AndroidVoipClient() { 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(); voip_thread_->Stop();
} }
AndroidVoipClient* AndroidVoipClient::Create( AndroidVoipClient* AndroidVoipClient::Create(
JNIEnv* env, 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. // Using `new` to access a non-public constructor.
auto voip_client = auto voip_client =
absl::WrapUnique(new AndroidVoipClient(env, application_context)); absl::WrapUnique(new AndroidVoipClient(env, j_voip_client));
if (!voip_client->voip_engine_) { if (!voip_client->Init(env, application_context)) {
return nullptr; return nullptr;
} }
return voip_client.release(); return voip_client.release();
} }
webrtc::ScopedJavaLocalRef<jobject> AndroidVoipClient::GetSupportedCodecs( void AndroidVoipClient::GetSupportedCodecs(JNIEnv* env) {
JNIEnv* env) { RUN_ON_VOIP_THREAD(GetSupportedCodecs, env);
std::vector<std::string> names; std::vector<std::string> names;
for (const webrtc::AudioCodecSpec& spec : supported_codecs_) { for (const webrtc::AudioCodecSpec& spec : supported_codecs_) {
names.push_back(spec.format.name); names.push_back(spec.format.name);
} }
webrtc::ScopedJavaLocalRef<jstring> (*convert_function)( webrtc::ScopedJavaLocalRef<jstring> (*convert_function)(
JNIEnv*, const std::string&) = &webrtc::NativeToJavaString; 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( void AndroidVoipClient::GetLocalIPAddress(JNIEnv* env) {
JNIEnv* env) { RUN_ON_VOIP_THREAD(GetLocalIPAddress, env);
std::string local_ip_address;
rtc::IPAddress ipv4_address = QueryDefaultLocalAddress(AF_INET); rtc::IPAddress ipv4_address = QueryDefaultLocalAddress(AF_INET);
if (!ipv4_address.IsNil()) { 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); Java_VoipClient_onGetLocalIPAddressCompleted(
if (!ipv6_address.IsNil()) { env_, j_voip_client_, webrtc::NativeToJavaString(env_, local_ip_address));
return webrtc::NativeToJavaString(env, ipv6_address.ToString());
}
return webrtc::NativeToJavaString(env, "");
} }
void AndroidVoipClient::SetEncoder( void AndroidVoipClient::SetEncoder(const std::string& encoder) {
JNIEnv* env, RTC_DCHECK_RUN_ON(voip_thread_.get());
const webrtc::JavaRef<jstring>& j_encoder_string) {
if (!channel_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; RTC_LOG(LS_ERROR) << "Channel has not been created";
return; return;
} }
const std::string& chosen_encoder = for (const webrtc::AudioCodecSpec& codec : supported_codecs_) {
webrtc::JavaToNativeString(env, j_encoder_string); if (codec.format.name == encoder) {
for (const webrtc::AudioCodecSpec& encoder : supported_codecs_) {
if (encoder.format.name == chosen_encoder) {
voip_engine_->Codec().SetSendCodec( voip_engine_->Codec().SetSendCodec(
*channel_, GetPayloadType(encoder.format.name), encoder.format); *channel_, GetPayloadType(codec.format.name), codec.format);
break; return;
} }
} }
} }
void AndroidVoipClient::SetDecoders( void AndroidVoipClient::SetEncoder(
JNIEnv* env, 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_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; RTC_LOG(LS_ERROR) << "Channel has not been created";
return; 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; std::map<int, webrtc::SdpAudioFormat> decoder_specs;
for (const webrtc::AudioCodecSpec& codec : supported_codecs_) {
for (const webrtc::AudioCodecSpec& decoder : supported_codecs_) { if (std::find(decoders.begin(), decoders.end(), codec.format.name) !=
if (std::find(chosen_decoders.begin(), chosen_decoders.end(), decoders.end()) {
decoder.format.name) != chosen_decoders.end()) { decoder_specs.insert({GetPayloadType(codec.format.name), codec.format});
decoder_specs.insert(
{GetPayloadType(decoder.format.name), decoder.format});
} }
} }
voip_engine_->Codec().SetReceiveCodecs(*channel_, decoder_specs); 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( void AndroidVoipClient::SetLocalAddress(
JNIEnv* env, JNIEnv* env,
const webrtc::JavaRef<jstring>& j_ip_address_string, const webrtc::JavaParamRef<jstring>& j_ip_address_string,
jint j_port_number_int) { jint j_port_number_int) {
const std::string& ip_address = const std::string& ip_address =
webrtc::JavaToNativeString(env, j_ip_address_string); webrtc::JavaToNativeString(env, j_ip_address_string);
rtp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int); voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] {
rtcp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1); 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( void AndroidVoipClient::SetRemoteAddress(
JNIEnv* env, JNIEnv* env,
const webrtc::JavaRef<jstring>& j_ip_address_string, const webrtc::JavaParamRef<jstring>& j_ip_address_string,
jint j_port_number_int) { jint j_port_number_int) {
const std::string& ip_address = const std::string& ip_address =
webrtc::JavaToNativeString(env, j_ip_address_string); webrtc::JavaToNativeString(env, j_ip_address_string);
rtp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int); voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] {
rtcp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1); SetRemoteAddress(ip_address, j_port_number_int);
});
} }
jboolean AndroidVoipClient::StartSession(JNIEnv* env) { void AndroidVoipClient::StartSession(JNIEnv* env) {
// Due to consistent thread requirement on RUN_ON_VOIP_THREAD(StartSession, env);
// modules/utility/source/process_thread_impl.cc,
// code is invoked in the context of voip_thread_. channel_ = voip_engine_->Base().CreateChannel(this, absl::nullopt);
channel_ = voip_thread_->Invoke<absl::optional<webrtc::ChannelId>>(
RTC_FROM_HERE,
[this] { return voip_engine_->Base().CreateChannel(this, 0); });
if (!channel_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel creation failed"; 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_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(),
rtp_local_address_)); rtp_local_address_));
if (!rtp_socket_) { if (!rtp_socket_) {
RTC_LOG_ERR(LERROR) << "Socket creation failed"; RTC_LOG_ERR(LERROR) << "Socket creation failed";
return false; Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
/*isSuccessful=*/false);
return;
} }
rtp_socket_->SignalReadPacket.connect( rtp_socket_->SignalReadPacket.connect(
this, &AndroidVoipClient::OnSignalReadRTPPacket); this, &AndroidVoipClient::OnSignalReadRTPPacket);
@ -262,123 +328,171 @@ jboolean AndroidVoipClient::StartSession(JNIEnv* env) {
rtcp_local_address_)); rtcp_local_address_));
if (!rtcp_socket_) { if (!rtcp_socket_) {
RTC_LOG_ERR(LERROR) << "Socket creation failed"; RTC_LOG_ERR(LERROR) << "Socket creation failed";
return false; Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
/*isSuccessful=*/false);
return;
} }
rtcp_socket_->SignalReadPacket.connect( rtcp_socket_->SignalReadPacket.connect(
this, &AndroidVoipClient::OnSignalReadRTCPPacket); this, &AndroidVoipClient::OnSignalReadRTCPPacket);
Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
return true; /*isSuccessful=*/true);
} }
jboolean AndroidVoipClient::StopSession(JNIEnv* env) { void AndroidVoipClient::StopSession(JNIEnv* env) {
RUN_ON_VOIP_THREAD(StopSession, env);
if (!channel_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; 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)) { if (!voip_engine_->Base().StopSend(*channel_) ||
return false; !voip_engine_->Base().StopPlayout(*channel_)) {
Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_,
/*isSuccessful=*/false);
return;
} }
rtp_socket_->Close(); rtp_socket_->Close();
rtcp_socket_->Close(); rtcp_socket_->Close();
// Due to consistent thread requirement on
// modules/utility/source/process_thread_impl.cc, voip_engine_->Base().ReleaseChannel(*channel_);
// code is invoked in the context of voip_thread_.
voip_thread_->Invoke<void>(RTC_FROM_HERE, [this] {
voip_engine_->Base().ReleaseChannel(*channel_);
});
channel_ = absl::nullopt; 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_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; 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 Java_VoipClient_onStartSendCompleted(
// modules/audio_device/android/opensles_recorder.cc, env_, j_voip_client_, voip_engine_->Base().StartSend(*channel_));
// code is invoked in the context of voip_thread_.
return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
return voip_engine_->Base().StartSend(*channel_);
});
} }
jboolean AndroidVoipClient::StopSend(JNIEnv* env) { void AndroidVoipClient::StopSend(JNIEnv* env) {
RUN_ON_VOIP_THREAD(StopSend, env);
if (!channel_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; 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 Java_VoipClient_onStopSendCompleted(env_, j_voip_client_,
// modules/audio_device/android/opensles_recorder.cc, voip_engine_->Base().StopSend(*channel_));
// code is invoked in the context of voip_thread_.
return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
return voip_engine_->Base().StopSend(*channel_);
});
} }
jboolean AndroidVoipClient::StartPlayout(JNIEnv* env) { void AndroidVoipClient::StartPlayout(JNIEnv* env) {
RUN_ON_VOIP_THREAD(StartPlayout, env);
if (!channel_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; 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 Java_VoipClient_onStartPlayoutCompleted(
// modules/audio_device/android/opensles_player.cc, env_, j_voip_client_, voip_engine_->Base().StartPlayout(*channel_));
// code is invoked in the context of voip_thread_.
return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
return voip_engine_->Base().StartPlayout(*channel_);
});
} }
jboolean AndroidVoipClient::StopPlayout(JNIEnv* env) { void AndroidVoipClient::StopPlayout(JNIEnv* env) {
RUN_ON_VOIP_THREAD(StopPlayout, env);
if (!channel_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; 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 Java_VoipClient_onStopPlayoutCompleted(
// modules/audio_device/android/opensles_player.cc, env_, j_voip_client_, voip_engine_->Base().StopPlayout(*channel_));
// code is invoked in the context of voip_thread_.
return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
return voip_engine_->Base().StopPlayout(*channel_);
});
} }
void AndroidVoipClient::Delete(JNIEnv* env) { void AndroidVoipClient::Delete(JNIEnv* env) {
delete this; 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, bool AndroidVoipClient::SendRtp(const uint8_t* packet,
size_t length, size_t length,
const webrtc::PacketOptions& options) { const webrtc::PacketOptions& options) {
if (!rtp_socket_->SendTo(packet, length, rtp_remote_address_, std::vector<uint8_t> packet_copy(packet, packet + length);
rtc::PacketOptions())) { voip_thread_->PostTask(RTC_FROM_HERE,
RTC_LOG(LS_ERROR) << "Failed to send RTP packet"; [this, packet_copy = std::move(packet_copy)] {
return false; SendRtpPacket(packet_copy);
} });
return true; return true;
} }
bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) { void AndroidVoipClient::SendRtcpPacket(
if (!rtcp_socket_->SendTo(packet, length, rtcp_remote_address_, const std::vector<uint8_t>& packet_copy) {
rtc::PacketOptions())) { 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"; 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; 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, void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket,
const char* rtp_packet, const char* rtp_packet,
size_t size, size_t size,
const rtc::SocketAddress& addr, const rtc::SocketAddress& addr,
const int64_t& timestamp) { 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_) { if (!channel_) {
RTC_LOG(LS_ERROR) << "Channel has not been created"; RTC_LOG(LS_ERROR) << "Channel has not been created";
return; return;
} }
voip_engine_->Network().ReceivedRTPPacket( voip_engine_->Network().ReceivedRTCPPacket(
*channel_, rtc::ArrayView<const uint8_t>( *channel_,
reinterpret_cast<const uint8_t*>(rtp_packet), size)); rtc::ArrayView<const uint8_t>(packet_copy.data(), packet_copy.size()));
} }
void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket, void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket,
@ -386,20 +500,19 @@ void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket,
size_t size, size_t size,
const rtc::SocketAddress& addr, const rtc::SocketAddress& addr,
const int64_t& timestamp) { const int64_t& timestamp) {
if (!channel_) { std::vector<uint8_t> packet_copy(rtcp_packet, rtcp_packet + size);
RTC_LOG(LS_ERROR) << "Channel has not been created"; voip_thread_->PostTask(RTC_FROM_HERE,
return; [this, packet_copy = std::move(packet_copy)] {
} ReadRTCPPacket(packet_copy);
voip_engine_->Network().ReceivedRTCPPacket( });
*channel_, rtc::ArrayView<const uint8_t>(
reinterpret_cast<const uint8_t*>(rtcp_packet), size));
} }
static jlong JNI_VoipClient_CreateClient( static jlong JNI_VoipClient_CreateClient(
JNIEnv* env, JNIEnv* env,
const webrtc::JavaParamRef<jobject>& application_context) { const webrtc::JavaParamRef<jobject>& application_context,
const webrtc::JavaParamRef<jobject>& j_voip_client) {
return webrtc::NativeToJavaPointer( return webrtc::NativeToJavaPointer(
AndroidVoipClient::Create(env, application_context)); AndroidVoipClient::Create(env, application_context, j_voip_client));
} }
} // namespace webrtc_examples } // namespace webrtc_examples

View File

@ -36,10 +36,10 @@ namespace webrtc_examples {
// webrtc::Transport to send RTP/RTCP packets to the remote endpoint. // webrtc::Transport to send RTP/RTCP packets to the remote endpoint.
// It also creates methods (slots) for sockets to connect to in // It also creates methods (slots) for sockets to connect to in
// order to receive RTP/RTCP packets. AndroidVoipClient does all // order to receive RTP/RTCP packets. AndroidVoipClient does all
// VoipBase related operations with rtc::Thread (voip_thread_), this // operations with rtc::Thread (voip_thread_), this is to comply
// is to comply with consistent thread usage requirement with // with consistent thread usage requirement with ProcessThread used
// ProcessThread used within VoipEngine. AndroidVoipClient is meant // within VoipEngine, as well as providing asynchronicity to the
// to be used by Java through JNI. // caller. AndroidVoipClient is meant to be used by Java through JNI.
class AndroidVoipClient : public webrtc::Transport, class AndroidVoipClient : public webrtc::Transport,
public sigslot::has_slots<> { public sigslot::has_slots<> {
public: public:
@ -50,22 +50,24 @@ class AndroidVoipClient : public webrtc::Transport,
// they are done with it (this class provides a Delete() method). // they are done with it (this class provides a Delete() method).
static AndroidVoipClient* Create( static AndroidVoipClient* Create(
JNIEnv* env, JNIEnv* env,
const webrtc::JavaParamRef<jobject>& application_context); const webrtc::JavaParamRef<jobject>& application_context,
const webrtc::JavaParamRef<jobject>& j_voip_client);
~AndroidVoipClient() override; ~AndroidVoipClient() override;
// Returns a Java List of Strings containing names of the built-in // Provides client with a Java List of Strings containing names of
// supported codecs. // the built-in supported codecs through callback.
webrtc::ScopedJavaLocalRef<jobject> GetSupportedCodecs(JNIEnv* env); void GetSupportedCodecs(JNIEnv* env);
// Returns a Java String of the default local IPv4 address. If IPv4 // Provides client with a Java String of the default local IPv4 address
// address is not found, returns the default local IPv6 address. If // through callback. If IPv4 address is not found, provide the default
// IPv6 address is not found, returns an empty string. // local IPv6 address. If IPv6 address is not found, provide an empty
webrtc::ScopedJavaLocalRef<jstring> GetLocalIPAddress(JNIEnv* env); // string.
void GetLocalIPAddress(JNIEnv* env);
// Sets the encoder used by the VoIP API. // Sets the encoder used by the VoIP API.
void SetEncoder(JNIEnv* env, 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. // Sets the decoders used by the VoIP API.
void SetDecoders(JNIEnv* env, void SetDecoders(JNIEnv* env,
@ -76,36 +78,41 @@ class AndroidVoipClient : public webrtc::Transport,
// and port number j_port_number_int, the RTCP address will have IP address // 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. // j_ip_address_string and port number j_port_number_int+1.
void SetLocalAddress(JNIEnv* env, 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); jint j_port_number_int);
void SetRemoteAddress(JNIEnv* env, void SetRemoteAddress(
const webrtc::JavaRef<jstring>& j_ip_address_string, JNIEnv* env,
jint j_port_number_int); const webrtc::JavaParamRef<jstring>& j_ip_address_string,
jint j_port_number_int);
// Starts a VoIP session. The VoIP operations below can only be // Starts a VoIP session, then calls a callback method with a boolean
// used after a session has already started. Returns true if session // value indicating if the session has started successfully. The VoIP
// started successfully and false otherwise. // operations below can only be used after a session has already started.
jboolean StartSession(JNIEnv* env); void StartSession(JNIEnv* env);
// Stops the current session. Returns true if session stopped // Stops the current session, then calls a callback method with a
// successfully and false otherwise. // boolean value indicating if the session has stopped successfully.
jboolean StopSession(JNIEnv* env); void StopSession(JNIEnv* env);
// Starts sending RTP/RTCP packets to the remote endpoint. Returns // Starts sending RTP/RTCP packets to the remote endpoint, then calls
// the return value of StartSend in api/voip/voip_base.h. // a callback method with a boolean value indicating if sending
jboolean StartSend(JNIEnv* env); // has started successfully.
void StartSend(JNIEnv* env);
// Stops sending RTP/RTCP packets to the remote endpoint. Returns // Stops sending RTP/RTCP packets to the remote endpoint, then calls
// the return value of StopSend in api/voip/voip_base.h. // a callback method with a boolean value indicating if sending
jboolean StopSend(JNIEnv* env); // has stopped successfully.
void StopSend(JNIEnv* env);
// Starts playing out the voice data received from the remote endpoint. // Starts playing out the voice data received from the remote endpoint,
// Returns the return value of StartPlayout in api/voip/voip_base.h. // then calls a callback method with a boolean value indicating if
jboolean StartPlayout(JNIEnv* env); // playout has started successfully.
void StartPlayout(JNIEnv* env);
// Stops playing out the voice data received from the remote endpoint. // Stops playing out the voice data received from the remote endpoint,
// Returns the return value of StopPlayout in api/voip/voip_base.h. // then calls a callback method with a boolean value indicating if
jboolean StopPlayout(JNIEnv* env); // playout has stopped successfully.
void StopPlayout(JNIEnv* env);
// Deletes this object. Used by client when they are done. // Deletes this object. Used by client when they are done.
void Delete(JNIEnv* env); void Delete(JNIEnv* env);
@ -130,25 +137,51 @@ class AndroidVoipClient : public webrtc::Transport,
private: private:
AndroidVoipClient(JNIEnv* env, 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 bool Init(JNIEnv* env,
// RTP/RTCP packets. 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_; 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 // A list of AudioCodecSpec supported by the built-in
// encoder/decoder factories. // 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. // 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. // 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. // Members below are used for network related operations.
std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_; std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_ RTC_GUARDED_BY(voip_thread_);
std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_; std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_
rtc::SocketAddress rtp_local_address_; RTC_GUARDED_BY(voip_thread_);
rtc::SocketAddress rtcp_local_address_; rtc::SocketAddress rtp_local_address_ RTC_GUARDED_BY(voip_thread_);
rtc::SocketAddress rtp_remote_address_; rtc::SocketAddress rtcp_local_address_ RTC_GUARDED_BY(voip_thread_);
rtc::SocketAddress rtcp_remote_address_; rtc::SocketAddress rtp_remote_address_ RTC_GUARDED_BY(voip_thread_);
rtc::SocketAddress rtcp_remote_address_ RTC_GUARDED_BY(voip_thread_);
}; };
} // namespace webrtc_examples } // namespace webrtc_examples