diff options
author | Jorge E. Moreira <jemoreira@google.com> | 2020-07-23 13:07:40 -0700 |
---|---|---|
committer | Jorge E. Moreira <jemoreira@google.com> | 2020-07-23 13:08:51 -0700 |
commit | 206ccd0b36df69a0d0d0d26ddf7c4ead20202f91 (patch) | |
tree | 9ba6a46a7e4cd59e1018b94136f46578efe31f2e /examples | |
parent | 889b21a2e1486d6c9df1b458a356ce6423bcdf72 (diff) | |
parent | 708b22cd0384ba5ceafd153c47e28f541c215ea6 (diff) | |
download | webrtc-206ccd0b36df69a0d0d0d26ddf7c4ead20202f91.tar.gz |
Merge remote tracking branch 'upstream-master'
Bug: 153469641
Test: run cuttlefish locally
Change-Id: Ida3bfe62ef5c6549278f4c155a1f690b008e9b9d
Diffstat (limited to 'examples')
25 files changed, 1652 insertions, 37 deletions
diff --git a/examples/BUILD.gn b/examples/BUILD.gn index 4d6d14d0d9..f0c5fa8be1 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn @@ -27,6 +27,7 @@ group("examples") { ":AppRTCMobile", ":AppRTCMobile_test_apk", ":libwebrtc_unity", + "androidvoip", ] # TODO(sakal): We include some code from the tests. Remove this dependency @@ -87,7 +88,7 @@ if (is_android) { testonly = true apk_name = "AppRTCMobile" android_manifest = "androidapp/AndroidManifest.xml" - min_sdk_version = 16 + min_sdk_version = 21 target_sdk_version = 29 deps = [ @@ -101,7 +102,7 @@ if (is_android) { rtc_android_library("AppRTCMobile_javalib") { testonly = true - android_manifest_for_lint = "androidapp/AndroidManifest.xml" + android_manifest = "androidapp/AndroidManifest.xml" sources = [ "androidapp/src/org/appspot/apprtc/AppRTCAudioManager.java", @@ -180,10 +181,10 @@ if (is_android) { "androidapp/res/layout/fragment_call.xml", "androidapp/res/layout/fragment_hud.xml", "androidapp/res/menu/connect_menu.xml", - "androidapp/res/values/arrays.xml", - "androidapp/res/values/strings.xml", "androidapp/res/values-v17/styles.xml", "androidapp/res/values-v21/styles.xml", + "androidapp/res/values/arrays.xml", + "androidapp/res/values/strings.xml", "androidapp/res/xml/preferences.xml", ] custom_package = "org.appspot.apprtc" @@ -196,7 +197,7 @@ if (is_android) { rtc_instrumentation_test_apk("AppRTCMobile_test_apk") { apk_name = "AppRTCMobileTest" android_manifest = "androidtests/AndroidManifest.xml" - min_sdk_version = 16 + min_sdk_version = 21 target_sdk_version = 21 sources = [ @@ -207,7 +208,11 @@ if (is_android) { deps = [ ":AppRTCMobile_javalib", + "../sdk/android:base_java", + "../sdk/android:camera_java", "../sdk/android:libjingle_peerconnection_java", + "../sdk/android:peerconnection_java", + "../sdk/android:video_api_java", "../sdk/android:video_java", "//third_party/android_support_test_runner:runner_java", "//third_party/junit", @@ -303,7 +308,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { } else { deps += [ "../sdk:mac_framework_objc+link" ] } - libs = [ + frameworks = [ "CoreMedia.framework", "QuartzCore.framework", ] @@ -345,7 +350,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { "../sdk:ios_framework_bundle", ] - libs = [ "AVFoundation.framework" ] + frameworks = [ "AVFoundation.framework" ] } ios_app_bundle("AppRTCMobile") { @@ -379,14 +384,18 @@ if (is_ios || (is_mac && target_cpu != "x86")) { if (rtc_apprtcmobile_broadcast_extension) { bundle_data("AppRTCMobileBroadcastUpload_extension_bundle") { testonly = true - public_deps = [ ":AppRTCMobileBroadcastUpload" ] # no-presubmit-check TODO(webrtc:8603) + public_deps = [ # no-presubmit-check TODO(webrtc:8603) + ":AppRTCMobileBroadcastUpload", # prevent code format + ] sources = [ "$root_out_dir/AppRTCMobileBroadcastUpload.appex" ] outputs = [ "{{bundle_contents_dir}}/Plugins/{{source_file_part}}" ] } bundle_data("AppRTCMobileBroadcastSetupUI_extension_bundle") { testonly = true - public_deps = [ ":AppRTCMobileBroadcastSetupUI" ] # no-presubmit-check TODO(webrtc:8603) + public_deps = [ # no-presubmit-check TODO(webrtc:8603) + ":AppRTCMobileBroadcastSetupUI", # prevent code format + ] sources = [ "$root_out_dir/AppRTCMobileBroadcastSetupUI.appex" ] outputs = [ "{{bundle_contents_dir}}/Plugins/{{source_file_part}}" ] } @@ -404,7 +413,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { "../sdk:ios_framework_bundle", ] - libs = [ "ReplayKit.framework" ] + frameworks = [ "ReplayKit.framework" ] } ios_appex_bundle("AppRTCMobileBroadcastUpload") { @@ -428,7 +437,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { info_plist = "objc/AppRTCMobile/ios/broadcast_extension/BroadcastSetupUIInfo.plist" - libs = [ "ReplayKit.framework" ] + frameworks = [ "ReplayKit.framework" ] deps = [ ":AppRTCMobile_ios_bundle_data" ] } @@ -484,6 +493,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { "../modules/audio_processing:api", "../pc:libjingle_peerconnection", "../rtc_base", + "../rtc_base/synchronization:mutex", "../sdk:base_objc", "../sdk:default_codec_factory_objc", "../sdk:helpers_objc", @@ -542,7 +552,7 @@ if (is_ios || (is_mac && target_cpu != "x86")) { info_plist = "objc/AppRTCMobile/mac/Info.plist" - libs = [ "AppKit.framework" ] + frameworks = [ "AppKit.framework" ] ldflags = [ "-rpath", @@ -587,10 +597,10 @@ if (is_ios || (is_mac && target_cpu != "x86")) { configs += [ ":socketrocket_warning_config" ] public_configs = [ ":socketrocket_include_config" ] - libs = [ + libs = [ "icucore" ] + frameworks = [ "CFNetwork.framework", "Security.framework", - "icucore", ] } @@ -829,6 +839,7 @@ if (is_android) { "../sdk/android:camera_java", "../sdk/android:libjingle_peerconnection_java", "../sdk/android:peerconnection_java", + "../sdk/android:video_api_java", "../sdk/android:video_java", "//third_party/android_deps:com_android_support_support_annotations_java", ] @@ -859,6 +870,7 @@ if (is_android) { deps = [ ":AppRTCMobile_javalib", + "../sdk/android:peerconnection_java", "//base:base_java_test_support", "//third_party/google-truth:google_truth_java", ] diff --git a/examples/aarproject/app/build.gradle b/examples/aarproject/app/build.gradle index dde0707ace..37499d468b 100644 --- a/examples/aarproject/app/build.gradle +++ b/examples/aarproject/app/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion "27.0.1" defaultConfig { applicationId "org.appspot.apprtc" - minSdkVersion 16 + minSdkVersion 21 targetSdkVersion 21 versionCode 1 versionName "1.0" diff --git a/examples/androidapp/AndroidManifest.xml b/examples/androidapp/AndroidManifest.xml index 8a9035e782..c4e1e797d0 100644 --- a/examples/androidapp/AndroidManifest.xml +++ b/examples/androidapp/AndroidManifest.xml @@ -8,7 +8,7 @@ <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:glEsVersion="0x00020000" android:required="true" /> - <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="29" /> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> diff --git a/examples/androidapp/src/org/appspot/apprtc/AppRTCAudioManager.java b/examples/androidapp/src/org/appspot/apprtc/AppRTCAudioManager.java index 7ae3d838dd..c32ab964ad 100644 --- a/examples/androidapp/src/org/appspot/apprtc/AppRTCAudioManager.java +++ b/examples/androidapp/src/org/appspot/apprtc/AppRTCAudioManager.java @@ -185,8 +185,8 @@ public class AppRTCAudioManager { // Note that, the sensor will not be active until start() has been called. proximitySensor = AppRTCProximitySensor.create(context, // This method will be called each time a state change is detected. - // Example: user holds his hand over the device (closer than ~5 cm), - // or removes his hand from the device. + // Example: user holds their hand over the device (closer than ~5 cm), + // or removes their hand from the device. this ::onProximitySensorChangedState); Log.d(TAG, "defaultAudioDevice: " + defaultAudioDevice); diff --git a/examples/androidnativeapi/AndroidManifest.xml b/examples/androidnativeapi/AndroidManifest.xml index f10f55a1b6..9257c4132e 100644 --- a/examples/androidnativeapi/AndroidManifest.xml +++ b/examples/androidnativeapi/AndroidManifest.xml @@ -2,7 +2,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.webrtc.examples.androidnativeapi"> - <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="27" /> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> diff --git a/examples/androidnativeapi/BUILD.gn b/examples/androidnativeapi/BUILD.gn index 9c114e859c..9253c0bcd9 100644 --- a/examples/androidnativeapi/BUILD.gn +++ b/examples/androidnativeapi/BUILD.gn @@ -5,7 +5,7 @@ if (is_android) { testonly = true apk_name = "androidnativeapi" android_manifest = "AndroidManifest.xml" - min_sdk_version = 19 + min_sdk_version = 21 target_sdk_version = 27 sources = [ @@ -16,6 +16,7 @@ if (is_android) { deps = [ ":resources", "//modules/audio_device:audio_device_java", + "//rtc_base:base_java", "//sdk/android:camera_java", "//sdk/android:surfaceviewrenderer_java", "//sdk/android:video_api_java", @@ -47,6 +48,7 @@ if (is_android) { deps = [ ":generated_jni", "../../api:scoped_refptr", + "../../rtc_base/synchronization:mutex", "//api:libjingle_peerconnection_api", "//api/rtc_event_log:rtc_event_log_factory", "//api/task_queue:default_task_queue_factory", diff --git a/examples/androidnativeapi/jni/android_call_client.cc b/examples/androidnativeapi/jni/android_call_client.cc index 03968335d9..f0b060632d 100644 --- a/examples/androidnativeapi/jni/android_call_client.cc +++ b/examples/androidnativeapi/jni/android_call_client.cc @@ -43,7 +43,7 @@ class AndroidCallClient::PCObserver : public webrtc::PeerConnectionObserver { void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override; private: - const AndroidCallClient* client_; + AndroidCallClient* const client_; }; namespace { @@ -88,7 +88,7 @@ void AndroidCallClient::Call(JNIEnv* env, const webrtc::JavaRef<jobject>& remote_sink) { RTC_DCHECK_RUN_ON(&thread_checker_); - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); if (call_started_) { RTC_LOG(LS_WARNING) << "Call already started."; return; @@ -112,7 +112,7 @@ void AndroidCallClient::Hangup(JNIEnv* env) { call_started_ = false; { - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); if (pc_ != nullptr) { pc_->Close(); pc_ = nullptr; @@ -174,7 +174,7 @@ void AndroidCallClient::CreatePeerConnectionFactory() { } void AndroidCallClient::CreatePeerConnection() { - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; // DTLS SRTP has to be disabled for loopback to work. @@ -205,7 +205,7 @@ void AndroidCallClient::CreatePeerConnection() { } void AndroidCallClient::Connect() { - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); pc_->CreateOffer(new rtc::RefCountedObject<CreateOfferObserver>(pc_), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } @@ -240,7 +240,7 @@ void AndroidCallClient::PCObserver::OnIceGatheringChange( void AndroidCallClient::PCObserver::OnIceCandidate( const webrtc::IceCandidateInterface* candidate) { RTC_LOG(LS_INFO) << "OnIceCandidate: " << candidate->server_url(); - rtc::CritScope lock(&client_->pc_mutex_); + webrtc::MutexLock lock(&client_->pc_mutex_); RTC_DCHECK(client_->pc_ != nullptr); client_->pc_->AddIceCandidate(candidate); } diff --git a/examples/androidnativeapi/jni/android_call_client.h b/examples/androidnativeapi/jni/android_call_client.h index 13992f5960..f3f61a4695 100644 --- a/examples/androidnativeapi/jni/android_call_client.h +++ b/examples/androidnativeapi/jni/android_call_client.h @@ -18,7 +18,7 @@ #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" -#include "rtc_base/critical_section.h" +#include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_checker.h" #include "sdk/android/native_api/jni/scoped_java_ref.h" #include "sdk/android/native_api/video/video_source.h" @@ -66,7 +66,7 @@ class AndroidCallClient { rtc::scoped_refptr<webrtc::JavaVideoTrackSourceInterface> video_source_ RTC_GUARDED_BY(thread_checker_); - rtc::CriticalSection pc_mutex_; + webrtc::Mutex pc_mutex_; rtc::scoped_refptr<webrtc::PeerConnectionInterface> pc_ RTC_GUARDED_BY(pc_mutex_); }; diff --git a/examples/androidtests/AndroidManifest.xml b/examples/androidtests/AndroidManifest.xml index dae2e980a6..8e995366dc 100644 --- a/examples/androidtests/AndroidManifest.xml +++ b/examples/androidtests/AndroidManifest.xml @@ -14,7 +14,7 @@ package="org.appspot.apprtc.test"> <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" /> - <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" /> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/examples/androidvoip/AndroidManifest.xml b/examples/androidvoip/AndroidManifest.xml new file mode 100644 index 0000000000..106f71171d --- /dev/null +++ b/examples/androidvoip/AndroidManifest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="org.webrtc.examples.androidvoip"> + + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" /> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> + + <uses-feature android:name="android.hardware.microphone" android:required="true" /> + <uses-feature android:name="android.hardware.telephony" android:required="false" /> + + <application + android:allowBackup="true" + android:label="@string/app_name" + android:supportsRtl="true"> + <activity android:name=".MainActivity" + android:windowSoftInputMode="stateHidden"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> diff --git a/examples/androidvoip/BUILD.gn b/examples/androidvoip/BUILD.gn new file mode 100644 index 0000000000..74341a78ac --- /dev/null +++ b/examples/androidvoip/BUILD.gn @@ -0,0 +1,88 @@ +# Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +import("//webrtc.gni") + +if (is_android) { + rtc_android_apk("androidvoip") { + testonly = true + apk_name = "androidvoip" + android_manifest = "AndroidManifest.xml" + min_sdk_version = 21 + target_sdk_version = 27 + + sources = [ + "java/org/webrtc/examples/androidvoip/MainActivity.java", + "java/org/webrtc/examples/androidvoip/OnVoipClientTaskCompleted.java", + "java/org/webrtc/examples/androidvoip/VoipClient.java", + ] + + deps = [ + ":resources", + "//modules/audio_device:audio_device_java", + "//rtc_base:base_java", + "//sdk/android:java_audio_device_module_java", + "//sdk/android:video_java", + "//third_party/android_deps:androidx_core_core_java", + "//third_party/android_deps:androidx_legacy_legacy_support_v4_java", + ] + + shared_libraries = [ ":examples_androidvoip_jni" ] + } + + generate_jni("generated_jni") { + testonly = true + sources = [ "java/org/webrtc/examples/androidvoip/VoipClient.java" ] + namespace = "webrtc_examples" + jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h" + } + + rtc_shared_library("examples_androidvoip_jni") { + testonly = true + sources = [ + "jni/android_voip_client.cc", + "jni/android_voip_client.h", + "jni/onload.cc", + ] + + suppressed_configs += [ "//build/config/android:hide_all_but_jni_onload" ] + configs += [ "//build/config/android:hide_all_but_jni" ] + + deps = [ + ":generated_jni", + "//api:transport_api", + "//api/audio_codecs:audio_codecs_api", + "//api/audio_codecs:builtin_audio_decoder_factory", + "//api/audio_codecs:builtin_audio_encoder_factory", + "//api/task_queue:default_task_queue_factory", + "//api/voip:voip_api", + "//api/voip:voip_engine_factory", + "//modules/utility:utility", + "//rtc_base", + "//rtc_base/third_party/sigslot:sigslot", + "//sdk/android:native_api_audio_device_module", + "//sdk/android:native_api_base", + "//sdk/android:native_api_jni", + "//third_party/abseil-cpp/absl/memory:memory", + ] + } + + android_resources("resources") { + testonly = true + custom_package = "org.webrtc.examples.androidvoip" + sources = [ + "res/layout/activity_main.xml", + "res/values/colors.xml", + "res/values/strings.xml", + ] + + # Needed for Bazel converter. + resource_dirs = [ "res" ] + assert(resource_dirs != []) # Mark as used. + } +} diff --git a/examples/androidvoip/DEPS b/examples/androidvoip/DEPS new file mode 100644 index 0000000000..edb714dd44 --- /dev/null +++ b/examples/androidvoip/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+sdk/android/native_api", +] diff --git a/examples/androidvoip/OWNERS b/examples/androidvoip/OWNERS new file mode 100644 index 0000000000..0fe5182450 --- /dev/null +++ b/examples/androidvoip/OWNERS @@ -0,0 +1,2 @@ +natim@webrtc.org +sakal@webrtc.org diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java new file mode 100644 index 0000000000..d787de59a0 --- /dev/null +++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.examples.androidvoip; + +import android.Manifest.permission; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.view.Gravity; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.RelativeLayout; +import android.widget.ScrollView; +import android.widget.Spinner; +import android.widget.Switch; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import org.webrtc.ContextUtils; + +public class MainActivity extends Activity implements OnVoipClientTaskCompleted { + private static final int NUM_SUPPORTED_CODECS = 6; + + private VoipClient voipClient; + private List<String> supportedCodecs; + private boolean[] isDecoderSelected; + private Set<Integer> selectedDecoders; + + private Toast toast; + private ScrollView scrollView; + private TextView localIPAddressTextView; + private EditText localPortNumberEditText; + private EditText remoteIPAddressEditText; + private EditText remotePortNumberEditText; + private Spinner encoderSpinner; + private Button decoderSelectionButton; + private TextView decodersTextView; + private ToggleButton sessionButton; + private RelativeLayout switchLayout; + private Switch sendSwitch; + private Switch playoutSwitch; + + @Override + protected void onCreate(Bundle savedInstance) { + ContextUtils.initialize(getApplicationContext()); + + super.onCreate(savedInstance); + setContentView(R.layout.activity_main); + + System.loadLibrary("examples_androidvoip_jni"); + + voipClient = new VoipClient(getApplicationContext(), this); + voipClient.getAndSetUpLocalIPAddress(); + voipClient.getAndSetUpSupportedCodecs(); + + isDecoderSelected = new boolean[NUM_SUPPORTED_CODECS]; + selectedDecoders = new HashSet<>(); + + toast = Toast.makeText(this, "", Toast.LENGTH_SHORT); + + scrollView = (ScrollView) findViewById(R.id.scroll_view); + localIPAddressTextView = (TextView) findViewById(R.id.local_ip_address_text_view); + localPortNumberEditText = (EditText) findViewById(R.id.local_port_number_edit_text); + remoteIPAddressEditText = (EditText) findViewById(R.id.remote_ip_address_edit_text); + remotePortNumberEditText = (EditText) findViewById(R.id.remote_port_number_edit_text); + encoderSpinner = (Spinner) findViewById(R.id.encoder_spinner); + decoderSelectionButton = (Button) findViewById(R.id.decoder_selection_button); + decodersTextView = (TextView) findViewById(R.id.decoders_text_view); + sessionButton = (ToggleButton) findViewById(R.id.session_button); + switchLayout = (RelativeLayout) findViewById(R.id.switch_layout); + sendSwitch = (Switch) findViewById(R.id.start_send_switch); + playoutSwitch = (Switch) findViewById(R.id.start_playout_switch); + + setUpSessionButton(); + setUpSendAndPlayoutSwitch(); + } + + private void setUpEncoderSpinner(List<String> supportedCodecs) { + ArrayAdapter<String> encoderAdapter = + new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, supportedCodecs); + encoderAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + encoderSpinner.setAdapter(encoderAdapter); + encoderSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + voipClient.setEncoder((String) parent.getSelectedItem()); + } + @Override + public void onNothingSelected(AdapterView<?> parent) {} + }); + } + + private List<String> getSelectedDecoders() { + List<String> decoders = new ArrayList<>(); + for (int i = 0; i < supportedCodecs.size(); i++) { + if (selectedDecoders.contains(i)) { + decoders.add(supportedCodecs.get(i)); + } + } + return decoders; + } + + private void setUpDecoderSelectionButton(List<String> supportedCodecs) { + decoderSelectionButton.setOnClickListener((view) -> { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + dialogBuilder.setTitle(R.string.dialog_title); + + // Populate multi choice items with supported decoders. + String[] supportedCodecsArray = supportedCodecs.toArray(new String[0]); + dialogBuilder.setMultiChoiceItems( + supportedCodecsArray, isDecoderSelected, (dialog, position, isChecked) -> { + if (isChecked) { + selectedDecoders.add(position); + } else if (!isChecked) { + selectedDecoders.remove(position); + } + }); + + // "Ok" button. + dialogBuilder.setPositiveButton(R.string.ok_label, (dialog, position) -> { + List<String> decoders = getSelectedDecoders(); + String result = decoders.stream().collect(Collectors.joining(", ")); + if (result.isEmpty()) { + decodersTextView.setText(R.string.decoders_text_view_default); + } else { + decodersTextView.setText(result); + } + voipClient.setDecoders(decoders); + }); + + // "Dismiss" button. + dialogBuilder.setNegativeButton( + R.string.dismiss_label, (dialog, position) -> { dialog.dismiss(); }); + + // "Clear All" button. + dialogBuilder.setNeutralButton(R.string.clear_all_label, (dialog, position) -> { + Arrays.fill(isDecoderSelected, false); + selectedDecoders.clear(); + decodersTextView.setText(R.string.decoders_text_view_default); + }); + + AlertDialog dialog = dialogBuilder.create(); + dialog.show(); + }); + } + + private void setUpSessionButton() { + sessionButton.setOnCheckedChangeListener((button, isChecked) -> { + // Ask for permission on RECORD_AUDIO if not granted. + if (ContextCompat.checkSelfPermission(this, permission.RECORD_AUDIO) + != PackageManager.PERMISSION_GRANTED) { + String[] sList = {permission.RECORD_AUDIO}; + ActivityCompat.requestPermissions(this, sList, 1); + } + + if (isChecked) { + // Order matters here, addresses have to be set before starting session + // before setting codec. + voipClient.setLocalAddress(localIPAddressTextView.getText().toString(), + Integer.parseInt(localPortNumberEditText.getText().toString())); + voipClient.setRemoteAddress(remoteIPAddressEditText.getText().toString(), + Integer.parseInt(remotePortNumberEditText.getText().toString())); + voipClient.startSession(); + voipClient.setEncoder((String) encoderSpinner.getSelectedItem()); + voipClient.setDecoders(getSelectedDecoders()); + } else { + voipClient.stopSession(); + } + }); + } + + private void setUpSendAndPlayoutSwitch() { + sendSwitch.setOnCheckedChangeListener((button, isChecked) -> { + if (isChecked) { + voipClient.startSend(); + } else { + voipClient.stopSend(); + } + }); + + playoutSwitch.setOnCheckedChangeListener((button, isChecked) -> { + if (isChecked) { + voipClient.startPlayout(); + } else { + voipClient.stopPlayout(); + } + }); + } + + private void setUpIPAddressEditTexts(String localIPAddress) { + if (localIPAddress.isEmpty()) { + showToast("Please check your network configuration"); + } else { + localIPAddressTextView.setText(localIPAddress); + // By default remote IP address is the same as local IP address. + remoteIPAddressEditText.setText(localIPAddress); + } + } + + private void showToast(String message) { + toast.cancel(); + toast = Toast.makeText(this, message, Toast.LENGTH_SHORT); + toast.setGravity(Gravity.TOP, 0, 200); + toast.show(); + } + + @Override + protected void onDestroy() { + voipClient.close(); + voipClient = null; + + super.onDestroy(); + } + + @Override + public void onGetLocalIPAddressCompleted(String localIPAddress) { + runOnUiThread(() -> { setUpIPAddressEditTexts(localIPAddress); }); + } + + @Override + public void onGetSupportedCodecsCompleted(List<String> supportedCodecs) { + runOnUiThread(() -> { + this.supportedCodecs = supportedCodecs; + setUpEncoderSpinner(supportedCodecs); + setUpDecoderSelectionButton(supportedCodecs); + }); + } + + @Override + public void onVoipClientInitializationCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (!isSuccessful) { + showToast("Error initializing audio device"); + } + }); + } + + @Override + public void onStartSessionCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (isSuccessful) { + showToast("Session started"); + switchLayout.setVisibility(View.VISIBLE); + scrollView.post(() -> { scrollView.fullScroll(ScrollView.FOCUS_DOWN); }); + } else { + showToast("Failed to start session"); + } + }); + } + + @Override + public void onStopSessionCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (isSuccessful) { + showToast("Session stopped"); + // Set listeners to null so the checked state can be changed programmatically. + sendSwitch.setOnCheckedChangeListener(null); + playoutSwitch.setOnCheckedChangeListener(null); + sendSwitch.setChecked(false); + playoutSwitch.setChecked(false); + // Redo the switch listener setup. + setUpSendAndPlayoutSwitch(); + switchLayout.setVisibility(View.GONE); + } else { + showToast("Failed to stop session"); + } + }); + } + + @Override + public void onStartSendCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (isSuccessful) { + showToast("Started sending"); + } else { + showToast("Error initializing microphone"); + } + }); + } + + @Override + public void onStopSendCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (isSuccessful) { + showToast("Stopped sending"); + } else { + showToast("Microphone termination failed"); + } + }); + } + + @Override + public void onStartPlayoutCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (isSuccessful) { + showToast("Started playout"); + } else { + showToast("Error initializing speaker"); + } + }); + } + + @Override + public void onStopPlayoutCompleted(boolean isSuccessful) { + runOnUiThread(() -> { + if (isSuccessful) { + showToast("Stopped playout"); + } else { + showToast("Speaker termination failed"); + } + }); + } + + @Override + public void onUninitializedVoipClient() { + runOnUiThread(() -> { showToast("Voip client is uninitialized"); }); + } +} diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/OnVoipClientTaskCompleted.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/OnVoipClientTaskCompleted.java new file mode 100644 index 0000000000..bb85e048bb --- /dev/null +++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/OnVoipClientTaskCompleted.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.examples.androidvoip; + +import java.util.List; + +public interface OnVoipClientTaskCompleted { + void onGetLocalIPAddressCompleted(String localIPAddress); + void onGetSupportedCodecsCompleted(List<String> supportedCodecs); + void onVoipClientInitializationCompleted(boolean isSuccessful); + void onStartSessionCompleted(boolean isSuccessful); + void onStopSessionCompleted(boolean isSuccessful); + void onStartSendCompleted(boolean isSuccessful); + void onStopSendCompleted(boolean isSuccessful); + void onStartPlayoutCompleted(boolean isSuccessful); + void onStopPlayoutCompleted(boolean isSuccessful); + void onUninitializedVoipClient(); +} diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java new file mode 100644 index 0000000000..2dcbd99b1d --- /dev/null +++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.examples.androidvoip; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import java.util.ArrayList; +import java.util.List; + +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); + }); + } + + private boolean isInitialized() { + return nativeClient != 0; + } + + public void getAndSetUpSupportedCodecs() { + handler.post(() -> { + if (isInitialized()) { + listener.onGetSupportedCodecsCompleted(nativeGetSupportedCodecs(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void getAndSetUpLocalIPAddress() { + handler.post(() -> { + if (isInitialized()) { + listener.onGetLocalIPAddressCompleted(nativeGetLocalIPAddress(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void setEncoder(String encoder) { + handler.post(() -> { + if (isInitialized()) { + nativeSetEncoder(nativeClient, encoder); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void setDecoders(List<String> decoders) { + handler.post(() -> { + 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(); + } + }); + } + + public void setRemoteAddress(String ipAddress, int portNumber) { + handler.post(() -> { + if (isInitialized()) { + nativeSetRemoteAddress(nativeClient, ipAddress, portNumber); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void startSession() { + handler.post(() -> { + if (isInitialized()) { + listener.onStartSessionCompleted(nativeStartSession(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void stopSession() { + handler.post(() -> { + if (isInitialized()) { + listener.onStopSessionCompleted(nativeStopSession(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void startSend() { + handler.post(() -> { + if (isInitialized()) { + listener.onStartSendCompleted(nativeStartSend(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void stopSend() { + handler.post(() -> { + if (isInitialized()) { + listener.onStopSendCompleted(nativeStopSend(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void startPlayout() { + handler.post(() -> { + if (isInitialized()) { + listener.onStartPlayoutCompleted(nativeStartPlayout(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void stopPlayout() { + handler.post(() -> { + if (isInitialized()) { + listener.onStopPlayoutCompleted(nativeStopPlayout(nativeClient)); + } else { + listener.onUninitializedVoipClient(); + } + }); + } + + public void close() { + handler.post(() -> { + nativeDelete(nativeClient); + nativeClient = 0; + }); + thread.quitSafely(); + } + + private static native long nativeCreateClient(Context applicationContext); + private static native List<String> nativeGetSupportedCodecs(long nativeAndroidVoipClient); + private static native String 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 nativeDelete(long nativeAndroidVoipClient); +} diff --git a/examples/androidvoip/jni/android_voip_client.cc b/examples/androidvoip/jni/android_voip_client.cc new file mode 100644 index 0000000000..13cadf2f3d --- /dev/null +++ b/examples/androidvoip/jni/android_voip_client.cc @@ -0,0 +1,405 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "examples/androidvoip/jni/android_voip_client.h" + +#include <errno.h> +#include <sys/socket.h> +#include <algorithm> +#include <map> +#include <memory> +#include <unordered_map> +#include <unordered_set> +#include <utility> +#include <vector> + +#include "absl/memory/memory.h" +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "api/voip/voip_codec.h" +#include "api/voip/voip_engine_factory.h" +#include "api/voip/voip_network.h" +#include "examples/androidvoip/generated_jni/VoipClient_jni.h" +#include "rtc_base/logging.h" +#include "rtc_base/network.h" +#include "rtc_base/socket_server.h" +#include "sdk/android/native_api/audio_device_module/audio_device_android.h" +#include "sdk/android/native_api/jni/java_types.h" + +namespace { + +// Connects a UDP socket to a public address and returns the local +// address associated with it. Since it binds to the "any" address +// internally, it returns the default local address on a multi-homed +// endpoint. Implementation copied from +// BasicNetworkManager::QueryDefaultLocalAddress. +rtc::IPAddress QueryDefaultLocalAddress(int family) { + const char kPublicIPv4Host[] = "8.8.8.8"; + const char kPublicIPv6Host[] = "2001:4860:4860::8888"; + const int kPublicPort = 53; + std::unique_ptr<rtc::Thread> thread = rtc::Thread::CreateWithSocketServer(); + + RTC_DCHECK(thread->socketserver() != nullptr); + RTC_DCHECK(family == AF_INET || family == AF_INET6); + + std::unique_ptr<rtc::AsyncSocket> socket( + thread->socketserver()->CreateAsyncSocket(family, SOCK_DGRAM)); + if (!socket) { + RTC_LOG_ERR(LERROR) << "Socket creation failed"; + return rtc::IPAddress(); + } + + auto host = family == AF_INET ? kPublicIPv4Host : kPublicIPv6Host; + if (socket->Connect(rtc::SocketAddress(host, kPublicPort)) < 0) { + if (socket->GetError() != ENETUNREACH && + socket->GetError() != EHOSTUNREACH) { + RTC_LOG(LS_INFO) << "Connect failed with " << socket->GetError(); + } + return rtc::IPAddress(); + } + return socket->GetLocalAddress().ipaddr(); +} + +// Assigned payload type for supported built-in codecs. PCMU, PCMA, +// and G722 have set payload types. Whereas opus, ISAC, and ILBC +// have dynamic payload types. +enum class PayloadType : int { + kPcmu = 0, + kPcma = 8, + kG722 = 9, + kOpus = 96, + kIsac = 97, + kIlbc = 98, +}; + +// Returns the payload type corresponding to codec_name. Only +// supports the built-in codecs. +int GetPayloadType(const std::string& codec_name) { + RTC_DCHECK(codec_name == "PCMU" || codec_name == "PCMA" || + codec_name == "G722" || codec_name == "opus" || + codec_name == "ISAC" || codec_name == "ILBC"); + + if (codec_name == "PCMU") { + return static_cast<int>(PayloadType::kPcmu); + } else if (codec_name == "PCMA") { + return static_cast<int>(PayloadType::kPcma); + } else if (codec_name == "G722") { + return static_cast<int>(PayloadType::kG722); + } else if (codec_name == "opus") { + return static_cast<int>(PayloadType::kOpus); + } else if (codec_name == "ISAC") { + return static_cast<int>(PayloadType::kIsac); + } else if (codec_name == "ILBC") { + return static_cast<int>(PayloadType::kIlbc); + } + + RTC_NOTREACHED(); + return -1; +} + +} // namespace + +namespace webrtc_examples { + +AndroidVoipClient::AndroidVoipClient( + 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(); + config.task_queue_factory = webrtc::CreateDefaultTaskQueueFactory(); + config.audio_device_module = + webrtc::CreateJavaAudioDeviceModule(env, application_context.obj()); + config.audio_processing = webrtc::AudioProcessingBuilder().Create(); + + supported_codecs_ = config.encoder_factory->GetSupportedEncoders(); + + // 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, [&] { + voip_engine_ = webrtc::CreateVoipEngine(std::move(config)); + if (!voip_engine_) { + RTC_LOG(LS_ERROR) << "VoipEngine creation failed"; + } + }); +} + +AndroidVoipClient::~AndroidVoipClient() { + voip_thread_->Stop(); +} + +AndroidVoipClient* AndroidVoipClient::Create( + JNIEnv* env, + const webrtc::JavaParamRef<jobject>& application_context) { + // Using `new` to access a non-public constructor. + auto voip_client = + absl::WrapUnique(new AndroidVoipClient(env, application_context)); + if (!voip_client->voip_engine_) { + return nullptr; + } + return voip_client.release(); +} + +webrtc::ScopedJavaLocalRef<jobject> AndroidVoipClient::GetSupportedCodecs( + JNIEnv* 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); +} + +webrtc::ScopedJavaLocalRef<jstring> AndroidVoipClient::GetLocalIPAddress( + JNIEnv* env) { + rtc::IPAddress ipv4_address = QueryDefaultLocalAddress(AF_INET); + if (!ipv4_address.IsNil()) { + return webrtc::NativeToJavaString(env, ipv4_address.ToString()); + } + rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6); + if (!ipv6_address.IsNil()) { + return webrtc::NativeToJavaString(env, ipv6_address.ToString()); + } + return webrtc::NativeToJavaString(env, ""); +} + +void AndroidVoipClient::SetEncoder( + JNIEnv* env, + const webrtc::JavaRef<jstring>& j_encoder_string) { + 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) { + voip_engine_->Codec().SetSendCodec( + *channel_, GetPayloadType(encoder.format.name), encoder.format); + break; + } + } +} + +void AndroidVoipClient::SetDecoders( + JNIEnv* env, + const webrtc::JavaParamRef<jobject>& j_decoder_strings) { + 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}); + } + } + + voip_engine_->Codec().SetReceiveCodecs(*channel_, decoder_specs); +} + +void AndroidVoipClient::SetLocalAddress( + JNIEnv* env, + const webrtc::JavaRef<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); +} + +void AndroidVoipClient::SetRemoteAddress( + JNIEnv* env, + const webrtc::JavaRef<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); +} + +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); }); + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel creation failed"; + return false; + } + + rtp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), + rtp_local_address_)); + if (!rtp_socket_) { + RTC_LOG_ERR(LERROR) << "Socket creation failed"; + return false; + } + rtp_socket_->SignalReadPacket.connect( + this, &AndroidVoipClient::OnSignalReadRTPPacket); + + rtcp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(), + rtcp_local_address_)); + if (!rtcp_socket_) { + RTC_LOG_ERR(LERROR) << "Socket creation failed"; + return false; + } + rtcp_socket_->SignalReadPacket.connect( + this, &AndroidVoipClient::OnSignalReadRTCPPacket); + + return true; +} + +jboolean AndroidVoipClient::StopSession(JNIEnv* env) { + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel has not been created"; + return false; + } + if (!StopSend(env) || !StopPlayout(env)) { + return false; + } + + 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_); + }); + channel_ = absl::nullopt; + return true; +} + +jboolean AndroidVoipClient::StartSend(JNIEnv* env) { + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel has not been created"; + return false; + } + // 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_); + }); +} + +jboolean AndroidVoipClient::StopSend(JNIEnv* env) { + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel has not been created"; + return false; + } + // 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_); + }); +} + +jboolean AndroidVoipClient::StartPlayout(JNIEnv* env) { + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel has not been created"; + return false; + } + // 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_); + }); +} + +jboolean AndroidVoipClient::StopPlayout(JNIEnv* env) { + if (!channel_) { + RTC_LOG(LS_ERROR) << "Channel has not been created"; + return false; + } + // 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_); + }); +} + +void AndroidVoipClient::Delete(JNIEnv* env) { + delete this; +} + +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; + } + return true; +} + +bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) { + if (!rtcp_socket_->SendTo(packet, length, rtcp_remote_address_, + rtc::PacketOptions())) { + RTC_LOG(LS_ERROR) << "Failed to send RTCP packet"; + return false; + } + return true; +} + +void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket, + const char* rtp_packet, + 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().ReceivedRTPPacket( + *channel_, rtc::ArrayView<const uint8_t>( + reinterpret_cast<const uint8_t*>(rtp_packet), size)); +} + +void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket, + const char* rtcp_packet, + 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)); +} + +static jlong JNI_VoipClient_CreateClient( + JNIEnv* env, + const webrtc::JavaParamRef<jobject>& application_context) { + return webrtc::NativeToJavaPointer( + AndroidVoipClient::Create(env, application_context)); +} + +} // namespace webrtc_examples diff --git a/examples/androidvoip/jni/android_voip_client.h b/examples/androidvoip/jni/android_voip_client.h new file mode 100644 index 0000000000..aed652e281 --- /dev/null +++ b/examples/androidvoip/jni/android_voip_client.h @@ -0,0 +1,156 @@ +/* + * Copyright 2020 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef EXAMPLES_ANDROIDVOIP_JNI_ANDROID_VOIP_CLIENT_H_ +#define EXAMPLES_ANDROIDVOIP_JNI_ANDROID_VOIP_CLIENT_H_ + +#include <jni.h> + +#include <memory> +#include <string> +#include <vector> + +#include "api/audio_codecs/audio_format.h" +#include "api/call/transport.h" +#include "api/voip/voip_base.h" +#include "api/voip/voip_engine.h" +#include "rtc_base/async_packet_socket.h" +#include "rtc_base/async_udp_socket.h" +#include "rtc_base/socket_address.h" +#include "rtc_base/third_party/sigslot/sigslot.h" +#include "rtc_base/thread.h" +#include "sdk/android/native_api/jni/scoped_java_ref.h" + +namespace webrtc_examples { + +// AndroidVoipClient facilitates the use of the VoIP API defined in +// api/voip/voip_engine.h. One instance of AndroidVoipClient should +// suffice for most VoIP applications. AndroidVoipClient implements +// 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. +class AndroidVoipClient : public webrtc::Transport, + public sigslot::has_slots<> { + public: + // Returns a pointer to an AndroidVoipClient object. Clients should + // use this factory method to create AndroidVoipClient objects. The + // method will return a nullptr in case of initialization errors. + // It is the client's responsibility to delete the pointer when + // they are done with it (this class provides a Delete() method). + static AndroidVoipClient* Create( + JNIEnv* env, + const webrtc::JavaParamRef<jobject>& application_context); + + ~AndroidVoipClient() override; + + // Returns a Java List of Strings containing names of the built-in + // supported codecs. + webrtc::ScopedJavaLocalRef<jobject> 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); + + // Sets the encoder used by the VoIP API. + void SetEncoder(JNIEnv* env, + const webrtc::JavaRef<jstring>& j_encoder_string); + + // Sets the decoders used by the VoIP API. + void SetDecoders(JNIEnv* env, + const webrtc::JavaParamRef<jobject>& j_decoder_strings); + + // Sets two local/remote addresses, one for RTP packets, and another for + // RTCP packets. The RTP address will have IP address j_ip_address_string + // 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, + jint j_port_number_int); + void SetRemoteAddress(JNIEnv* env, + const webrtc::JavaRef<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); + + // Stops the current session. Returns true if session stopped + // successfully and false otherwise. + jboolean 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); + + // 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); + + // 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); + + // 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); + + // Deletes this object. Used by client when they are done. + void Delete(JNIEnv* env); + + // Implementation for Transport. + bool SendRtp(const uint8_t* packet, + size_t length, + const webrtc::PacketOptions& options) override; + bool SendRtcp(const uint8_t* packet, size_t length) override; + + // Slots for sockets to connect to. + void OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket, + const char* rtp_packet, + size_t size, + const rtc::SocketAddress& addr, + const int64_t& timestamp); + void OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket, + const char* rtcp_packet, + size_t size, + const rtc::SocketAddress& addr, + const int64_t& timestamp); + + private: + AndroidVoipClient(JNIEnv* env, + const webrtc::JavaParamRef<jobject>& application_context); + + // Used to invoke VoipBase operations and send/receive + // RTP/RTCP packets. + std::unique_ptr<rtc::Thread> voip_thread_; + // A list of AudioCodecSpec supported by the built-in + // encoder/decoder factories. + std::vector<webrtc::AudioCodecSpec> supported_codecs_; + // The entry point to all VoIP APIs. + std::unique_ptr<webrtc::VoipEngine> voip_engine_; + // Used by the VoIP API to facilitate a VoIP session. + absl::optional<webrtc::ChannelId> channel_; + // 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_; +}; + +} // namespace webrtc_examples + +#endif // EXAMPLES_ANDROIDVOIP_JNI_ANDROID_VOIP_CLIENT_H_ diff --git a/examples/androidvoip/jni/onload.cc b/examples/androidvoip/jni/onload.cc new file mode 100644 index 0000000000..b952de348b --- /dev/null +++ b/examples/androidvoip/jni/onload.cc @@ -0,0 +1,28 @@ +/* + * Copyright 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <jni.h> + +#include "rtc_base/ssl_adapter.h" +#include "sdk/android/native_api/base/init.h" + +namespace webrtc_examples { + +extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) { + webrtc::InitAndroid(jvm); + RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()"; + return JNI_VERSION_1_6; +} + +extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM* jvm, void* reserved) { + RTC_CHECK(rtc::CleanupSSL()) << "Failed to CleanupSSL()"; +} + +} // namespace webrtc_examples diff --git a/examples/androidvoip/res/layout/activity_main.xml b/examples/androidvoip/res/layout/activity_main.xml new file mode 100644 index 0000000000..c7fa5a9b31 --- /dev/null +++ b/examples/androidvoip/res/layout/activity_main.xml @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/scroll_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:focusable="true" + android:focusableInTouchMode="true" + tools:context="org.webrtc.examples.androidvoip.MainActivity"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="8dp"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginTop="15dp" + android:text="@string/local_endpoint_text_view" + android:textSize="19dp" + android:textStyle="bold" + android:textColor="@color/almost_black" /> + + <!--Local IP Adress--> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_gravity="center_vertical" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp" + android:text="@string/ip_address_text_view" + android:textSize="16dp" /> + + <TextView + android:id="@+id/local_ip_address_text_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dp" + android:layout_marginRight="15dp" + android:textSize="16dp" /> + + </LinearLayout> + + <!--Local Port Number--> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp" + android:text="@string/port_number_text_view" + android:textSize="16dp" /> + + <EditText + android:id="@+id/local_port_number_edit_text" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_marginRight="15dp" + android:layout_weight="1" + android:text="10000" + android:inputType="number" + android:textSize="16dp" /> + + </LinearLayout> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginTop="30dp" + android:text="@string/remote_endpoint_text_view" + android:textSize="19dp" + android:textStyle="bold" + android:textColor="@color/almost_black" /> + + <!--Remote IP Adress--> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp" + android:text="@string/ip_address_text_view" + android:textSize="16dp" /> + + <EditText + android:id="@+id/remote_ip_address_edit_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginRight="15dp" + android:layout_weight="1" + android:inputType="number" + android:digits="0123456789." + android:textSize="16dp" /> + + </LinearLayout> + + <!--Remote Port Number--> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp" + android:text="@string/port_number_text_view" + android:textSize="16dp" /> + + <EditText + android:id="@+id/remote_port_number_edit_text" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_marginRight="15dp" + android:layout_weight="1" + android:text="10000" + android:inputType="number" + android:textSize="16dp" /> + + </LinearLayout> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginTop="30dp" + android:text="@string/encoder_text_view" + android:textSize="19dp" + android:textStyle="bold" + android:textColor="@color/almost_black" /> + + <Spinner + android:id="@+id/encoder_spinner" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginTop="10dp"/> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_marginTop="20dp" + android:layout_gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="15dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="25dp" + android:text="@string/decoder_text_view" + android:textSize="19dp" + android:textStyle="bold" + android:textColor="@color/almost_black" /> + + <Button + android:id="@+id/decoder_selection_button" + android:text="@string/decoder_selection_button" + style="?android:attr/buttonBarButtonStyle" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_marginRight="15dp" + android:layout_weight="1" /> + + </LinearLayout> + + + <TextView + android:id="@+id/decoders_text_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:layout_marginBottom="30dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp" + android:text="@string/decoders_text_view_default" + android:textSize="16dp" /> + + + <RelativeLayout + android:id="@+id/switch_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:visibility="gone" > + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_marginLeft="15dp" + android:layout_marginRight="15dp" + android:layout_marginBottom="45dp" + android:background="@color/light_gray" /> + + <LinearLayout + android:id="@+id/start_send_switch_layout" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_gravity="center_vertical" + android:layout_below="@id/divider" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginLeft="15dp" + android:gravity="left" + android:layout_weight="1" + android:text="@string/start_send_text_view" + android:textSize="16dp" /> + + <Switch + android:id="@+id/start_send_switch" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="15dp" + android:gravity="right" + android:layout_weight="1" /> + + </LinearLayout> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="48dp" + android:layout_gravity="center_vertical" + android:layout_below="@id/start_send_switch_layout"> + + <TextView + android:id="@+id/start_playout_text_view" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginLeft="15dp" + android:gravity="left" + android:layout_weight="1" + android:text="@string/start_playout_text_view" + android:textSize="16dp" /> + + <Switch + android:id="@+id/start_playout_switch" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="15dp" + android:gravity="right" + android:layout_weight="1" /> + + </LinearLayout> + + </RelativeLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical" > + + <ToggleButton + android:id="@+id/session_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:textOff="@string/session_button_text_off" + android:textOn="@string/session_button_text_on" + style="?android:attr/buttonStyle" /> + + </LinearLayout> + + </LinearLayout> + +</ScrollView> diff --git a/examples/androidvoip/res/values/colors.xml b/examples/androidvoip/res/values/colors.xml new file mode 100644 index 0000000000..4dadaa9941 --- /dev/null +++ b/examples/androidvoip/res/values/colors.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="almost_black">#484848</color> + <color name="light_gray">#D3D3D3</color> +</resources>
\ No newline at end of file diff --git a/examples/androidvoip/res/values/strings.xml b/examples/androidvoip/res/values/strings.xml new file mode 100644 index 0000000000..d519bfbbb6 --- /dev/null +++ b/examples/androidvoip/res/values/strings.xml @@ -0,0 +1,19 @@ +<resources> + <string name="app_name">androidvoip</string> + <string name="local_endpoint_text_view">Local Endpoint</string> + <string name="remote_endpoint_text_view">Remote Endpoint</string> + <string name="ip_address_text_view">IP Address:</string> + <string name="port_number_text_view">Port Number:</string> + <string name="encoder_text_view">Select Encoder</string> + <string name="decoder_text_view">Select Decoder</string> + <string name="decoder_selection_button">Configure Selection</string> + <string name="decoders_text_view_default">No decoders selected</string> + <string name="dialog_title">Choose Decoders</string> + <string name="ok_label">Ok</string> + <string name="dismiss_label">Dismiss</string> + <string name="clear_all_label">Clear All</string> + <string name="start_send_text_view">Start Sending</string> + <string name="start_playout_text_view">Start Playout</string> + <string name="session_button_text_off">Start Session</string> + <string name="session_button_text_on">Stop Session</string> +</resources> diff --git a/examples/objc/AppRTCMobile/ios/ARDMainViewController.m b/examples/objc/AppRTCMobile/ios/ARDMainViewController.m index dea7742a1b..a3ede07f3b 100644 --- a/examples/objc/AppRTCMobile/ios/ARDMainViewController.m +++ b/examples/objc/AppRTCMobile/ios/ARDMainViewController.m @@ -134,6 +134,7 @@ static NSString *const loopbackLaunchProcessArgument = @"loopback"; delegate:self]; videoCallViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; + videoCallViewController.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:videoCallViewController animated:YES completion:nil]; diff --git a/examples/objcnativeapi/objc/objc_call_client.h b/examples/objcnativeapi/objc/objc_call_client.h index 90ac20ac01..b952402bc0 100644 --- a/examples/objcnativeapi/objc/objc_call_client.h +++ b/examples/objcnativeapi/objc/objc_call_client.h @@ -18,7 +18,7 @@ #include "api/peer_connection_interface.h" #include "api/scoped_refptr.h" -#include "rtc_base/critical_section.h" +#include "rtc_base/synchronization/mutex.h" #include "rtc_base/thread_checker.h" @class RTC_OBJC_TYPE(RTCVideoCapturer); @@ -50,7 +50,7 @@ class ObjCCallClient { void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override; private: - const ObjCCallClient* client_; + ObjCCallClient* const client_; }; void CreatePeerConnectionFactory() RTC_RUN_ON(thread_checker_); @@ -73,7 +73,7 @@ class ObjCCallClient { rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> video_source_ RTC_GUARDED_BY(thread_checker_); - rtc::CriticalSection pc_mutex_; + webrtc::Mutex pc_mutex_; rtc::scoped_refptr<webrtc::PeerConnectionInterface> pc_ RTC_GUARDED_BY(pc_mutex_); }; diff --git a/examples/objcnativeapi/objc/objc_call_client.mm b/examples/objcnativeapi/objc/objc_call_client.mm index 52ee2b5f95..5ce7eb7804 100644 --- a/examples/objcnativeapi/objc/objc_call_client.mm +++ b/examples/objcnativeapi/objc/objc_call_client.mm @@ -68,7 +68,7 @@ void ObjCCallClient::Call(RTC_OBJC_TYPE(RTCVideoCapturer) * capturer, id<RTC_OBJC_TYPE(RTCVideoRenderer)> remote_renderer) { RTC_DCHECK_RUN_ON(&thread_checker_); - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); if (call_started_) { RTC_LOG(LS_WARNING) << "Call already started."; return; @@ -90,7 +90,7 @@ void ObjCCallClient::Hangup() { call_started_ = false; { - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); if (pc_ != nullptr) { pc_->Close(); pc_ = nullptr; @@ -138,7 +138,7 @@ void ObjCCallClient::CreatePeerConnectionFactory() { } void ObjCCallClient::CreatePeerConnection() { - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); webrtc::PeerConnectionInterface::RTCConfiguration config; config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan; // DTLS SRTP has to be disabled for loopback to work. @@ -165,7 +165,7 @@ void ObjCCallClient::CreatePeerConnection() { } void ObjCCallClient::Connect() { - rtc::CritScope lock(&pc_mutex_); + webrtc::MutexLock lock(&pc_mutex_); pc_->CreateOffer(new rtc::RefCountedObject<CreateOfferObserver>(pc_), webrtc::PeerConnectionInterface::RTCOfferAnswerOptions()); } @@ -198,7 +198,7 @@ void ObjCCallClient::PCObserver::OnIceGatheringChange( void ObjCCallClient::PCObserver::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) { RTC_LOG(LS_INFO) << "OnIceCandidate: " << candidate->server_url(); - rtc::CritScope lock(&client_->pc_mutex_); + webrtc::MutexLock lock(&client_->pc_mutex_); RTC_DCHECK(client_->pc_ != nullptr); client_->pc_->AddIceCandidate(candidate); } |