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",
"//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",

View file

@ -219,10 +219,12 @@ public class MainActivity extends Activity implements OnVoipClientTaskCompleted
}
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

View file

@ -15,26 +15,15 @@ import android.os.Handler;
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 class VoipClient {
}
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);
}

View file

@ -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 @@ int GetPayloadType(const std::string& codec_name) {
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 @@ AndroidVoipClient::AndroidVoipClient(
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 @@ jboolean AndroidVoipClient::StartSession(JNIEnv* env) {
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 @@ void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket,
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

View file

@ -36,10 +36,10 @@ namespace webrtc_examples {
// 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 @@ class AndroidVoipClient : public webrtc::Transport,
// 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 @@ class AndroidVoipClient : public webrtc::Transport,
// 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 @@ class AndroidVoipClient : public webrtc::Transport,
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