mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 13:20:44 +01:00
Add audio device module for android based on Oboe
This commit is contained in:
parent
47b7b9d95f
commit
a322dda556
8 changed files with 1318 additions and 2 deletions
6
DEPS
6
DEPS
|
@ -57,6 +57,12 @@ deps = {
|
|||
'src/ringrtc/opus/src':
|
||||
'https://github.com/xiph/opus.git@0e30966b198ad28943799eaf5b3b08100b6f70c3',
|
||||
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
'src/ringrtc/oboe/src': {
|
||||
'url': 'https://github.com/google/oboe.git@03242e9ef9495e418e6ae83954d239dc9193ec5c',
|
||||
'condition': 'checkout_android',
|
||||
},
|
||||
|
||||
# TODO(kjellander): Move this to be Android-only.
|
||||
'src/base':
|
||||
'https://chromium.googlesource.com/chromium/src/base@2f20fae2cd5d41fc2dbc912fd462796419c72ce6',
|
||||
|
|
1
ringrtc/oboe/.gitignore
vendored
Normal file
1
ringrtc/oboe/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/src
|
99
ringrtc/oboe/BUILD.gn
Normal file
99
ringrtc/oboe/BUILD.gn
Normal file
|
@ -0,0 +1,99 @@
|
|||
#
|
||||
# Copyright 2024 Signal Messenger, LLC
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
#
|
||||
|
||||
import("//webrtc.gni")
|
||||
|
||||
rtc_library("oboe") {
|
||||
import("//build/config/android/config.gni")
|
||||
import("//build/config/android/rules.gni")
|
||||
|
||||
defines = [
|
||||
"OBOE_ENABLE_LOGGING=0",
|
||||
]
|
||||
|
||||
cflags_cc = [
|
||||
# These came from oboe's CMakeLists.txt file.
|
||||
"-std=c++17",
|
||||
"-Wall",
|
||||
"-Wextra-semi",
|
||||
"-Wshadow",
|
||||
"-Wshadow-field",
|
||||
|
||||
# These were needed to get it building.
|
||||
"-Wno-exit-time-destructors",
|
||||
"-Wno-header-hygiene",
|
||||
"-Wno-sign-compare",
|
||||
]
|
||||
|
||||
include_dirs = [
|
||||
"src/include",
|
||||
"src/src",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/src/aaudio/AAudioLoader.cpp",
|
||||
"src/src/aaudio/AudioStreamAAudio.cpp",
|
||||
"src/src/common/AdpfWrapper.cpp",
|
||||
"src/src/common/AudioSourceCaller.cpp",
|
||||
"src/src/common/AudioStream.cpp",
|
||||
"src/src/common/AudioStreamBuilder.cpp",
|
||||
"src/src/common/DataConversionFlowGraph.cpp",
|
||||
"src/src/common/FilterAudioStream.cpp",
|
||||
"src/src/common/FixedBlockAdapter.cpp",
|
||||
"src/src/common/FixedBlockReader.cpp",
|
||||
"src/src/common/FixedBlockWriter.cpp",
|
||||
"src/src/common/LatencyTuner.cpp",
|
||||
"src/src/common/OboeExtensions.cpp",
|
||||
"src/src/common/SourceFloatCaller.cpp",
|
||||
"src/src/common/SourceI16Caller.cpp",
|
||||
"src/src/common/SourceI24Caller.cpp",
|
||||
"src/src/common/SourceI32Caller.cpp",
|
||||
"src/src/common/Utilities.cpp",
|
||||
"src/src/common/QuirksManager.cpp",
|
||||
"src/src/fifo/FifoBuffer.cpp",
|
||||
"src/src/fifo/FifoController.cpp",
|
||||
"src/src/fifo/FifoControllerBase.cpp",
|
||||
"src/src/fifo/FifoControllerIndirect.cpp",
|
||||
"src/src/flowgraph/FlowGraphNode.cpp",
|
||||
"src/src/flowgraph/ChannelCountConverter.cpp",
|
||||
"src/src/flowgraph/ClipToRange.cpp",
|
||||
"src/src/flowgraph/Limiter.cpp",
|
||||
"src/src/flowgraph/ManyToMultiConverter.cpp",
|
||||
"src/src/flowgraph/MonoBlend.cpp",
|
||||
"src/src/flowgraph/MonoToMultiConverter.cpp",
|
||||
"src/src/flowgraph/MultiToManyConverter.cpp",
|
||||
"src/src/flowgraph/MultiToMonoConverter.cpp",
|
||||
"src/src/flowgraph/RampLinear.cpp",
|
||||
"src/src/flowgraph/SampleRateConverter.cpp",
|
||||
"src/src/flowgraph/SinkFloat.cpp",
|
||||
"src/src/flowgraph/SinkI16.cpp",
|
||||
"src/src/flowgraph/SinkI24.cpp",
|
||||
"src/src/flowgraph/SinkI32.cpp",
|
||||
"src/src/flowgraph/SinkI8_24.cpp",
|
||||
"src/src/flowgraph/SourceFloat.cpp",
|
||||
"src/src/flowgraph/SourceI16.cpp",
|
||||
"src/src/flowgraph/SourceI24.cpp",
|
||||
"src/src/flowgraph/SourceI32.cpp",
|
||||
"src/src/flowgraph/SourceI8_24.cpp",
|
||||
"src/src/flowgraph/resampler/IntegerRatio.cpp",
|
||||
"src/src/flowgraph/resampler/LinearResampler.cpp",
|
||||
"src/src/flowgraph/resampler/MultiChannelResampler.cpp",
|
||||
"src/src/flowgraph/resampler/PolyphaseResampler.cpp",
|
||||
"src/src/flowgraph/resampler/PolyphaseResamplerMono.cpp",
|
||||
"src/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp",
|
||||
"src/src/flowgraph/resampler/SincResampler.cpp",
|
||||
"src/src/flowgraph/resampler/SincResamplerStereo.cpp",
|
||||
"src/src/opensles/AudioInputStreamOpenSLES.cpp",
|
||||
"src/src/opensles/AudioOutputStreamOpenSLES.cpp",
|
||||
"src/src/opensles/AudioStreamBuffered.cpp",
|
||||
"src/src/opensles/AudioStreamOpenSLES.cpp",
|
||||
"src/src/opensles/EngineOpenSLES.cpp",
|
||||
"src/src/opensles/OpenSLESUtilities.cpp",
|
||||
"src/src/opensles/OutputMixerOpenSLES.cpp",
|
||||
"src/src/common/StabilizedCallback.cpp",
|
||||
"src/src/common/Trace.cpp",
|
||||
"src/src/common/Version.cpp",
|
||||
]
|
||||
}
|
|
@ -425,6 +425,8 @@ if (is_android) {
|
|||
visibility = [ "*" ]
|
||||
sources = [
|
||||
"api/org/webrtc/audio/JavaAudioDeviceModule.java",
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
"api/org/webrtc/audio/OboeAudioDeviceModule.java",
|
||||
"src/java/org/webrtc/audio/LowLatencyAudioBufferManager.java",
|
||||
"src/java/org/webrtc/audio/VolumeLogger.java",
|
||||
"src/java/org/webrtc/audio/WebRtcAudioEffects.java",
|
||||
|
@ -818,7 +820,11 @@ if (current_os == "linux" || is_android) {
|
|||
# JNI target for java_audio_device_module_java
|
||||
rtc_library("java_audio_device_module_jni") {
|
||||
visibility = [ "*" ]
|
||||
sources = [ "src/jni/audio_device/java_audio_device_module.cc" ]
|
||||
sources = [
|
||||
"src/jni/audio_device/java_audio_device_module.cc",
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
"src/jni/audio_device/oboe_audio_device_module.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":base_jni",
|
||||
|
@ -1165,12 +1171,23 @@ if (current_os == "linux" || is_android) {
|
|||
rtc_library("audio_device_module_base") {
|
||||
visibility = [ "*" ]
|
||||
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
include_dirs = [
|
||||
"../../ringrtc/oboe/src/include"
|
||||
]
|
||||
|
||||
sources = [
|
||||
"src/jni/audio_device/audio_common.h",
|
||||
"src/jni/audio_device/audio_device_module.cc",
|
||||
"src/jni/audio_device/audio_device_module.h",
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
"src/jni/audio_device/audio_device_module_oboe.cc",
|
||||
"src/jni/audio_device/audio_device_module_oboe.h",
|
||||
]
|
||||
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
ldflags = [ "-laaudio" ]
|
||||
|
||||
deps = [
|
||||
":base_jni",
|
||||
":generated_audio_device_module_base_jni",
|
||||
|
@ -1184,6 +1201,8 @@ if (current_os == "linux" || is_android) {
|
|||
"../../rtc_base:checks",
|
||||
"../../rtc_base:logging",
|
||||
"../../system_wrappers:metrics",
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
"//ringrtc/oboe:oboe",
|
||||
]
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
|
@ -1435,7 +1454,11 @@ if (current_os == "linux" || is_android) {
|
|||
}
|
||||
|
||||
generate_jni("generated_java_audio_jni") {
|
||||
sources = [ "api/org/webrtc/audio/JavaAudioDeviceModule.java" ]
|
||||
sources = [
|
||||
"api/org/webrtc/audio/JavaAudioDeviceModule.java",
|
||||
# RingRTC change to support Oboe for audio on Android
|
||||
"api/org/webrtc/audio/OboeAudioDeviceModule.java"
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
|
||||
}
|
||||
|
|
157
sdk/android/api/org/webrtc/audio/OboeAudioDeviceModule.java
Normal file
157
sdk/android/api/org/webrtc/audio/OboeAudioDeviceModule.java
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.webrtc.audio;
|
||||
|
||||
import org.webrtc.JniCommon;
|
||||
import org.webrtc.Logging;
|
||||
|
||||
/**
|
||||
* AudioDeviceModule implemented using Oboe.
|
||||
*/
|
||||
public class OboeAudioDeviceModule implements AudioDeviceModule {
|
||||
private static final String TAG = "OboeAudioDeviceModule";
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
// By default, hardware AEC and NS will be used. These are *usually* available to
|
||||
// streams by configuration for VOICE_COMMUNICATION usage. The Client will force
|
||||
// WebRTC's software AEC3 and NS on a case-by-case basis.
|
||||
private boolean useSoftwareAcousticEchoCanceler;
|
||||
private boolean useSoftwareNoiseSuppressor;
|
||||
private boolean useExclusiveSharingMode;
|
||||
private int audioSessionId;
|
||||
|
||||
private Builder() {
|
||||
Logging.w(TAG, "Builder()");
|
||||
this.useSoftwareAcousticEchoCanceler = false;
|
||||
this.useSoftwareNoiseSuppressor = false;
|
||||
this.useExclusiveSharingMode = true;
|
||||
this.audioSessionId = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if the software noise suppressor should be used or not.
|
||||
*/
|
||||
public Builder setUseSoftwareNoiseSuppressor(boolean useSoftwareNoiseSuppressor) {
|
||||
Logging.w(TAG, "setUseSoftwareNoiseSuppressor: " + useSoftwareNoiseSuppressor);
|
||||
this.useSoftwareNoiseSuppressor = useSoftwareNoiseSuppressor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if the software acoustic echo canceler should be used or not.
|
||||
*/
|
||||
public Builder setUseSoftwareAcousticEchoCanceler(boolean useSoftwareAcousticEchoCanceler) {
|
||||
Logging.w(TAG, "setUseSoftwareAcousticEchoCanceler: " + useSoftwareAcousticEchoCanceler);
|
||||
this.useSoftwareAcousticEchoCanceler = useSoftwareAcousticEchoCanceler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if the streams should be opened with exclusive sharing mode enabled or not. The
|
||||
* default is on, but if exclusive mode can't be obtained, then shared mode will be used.
|
||||
*/
|
||||
public Builder setExclusiveSharingMode(boolean useExclusiveSharingMode) {
|
||||
Logging.w(TAG, "setExclusiveSharingMode: " + useExclusiveSharingMode);
|
||||
this.useExclusiveSharingMode = useExclusiveSharingMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an audio session ID generated from application's AudioManager.
|
||||
*/
|
||||
public Builder setAudioSessionId(int audioSessionId) {
|
||||
Logging.w(TAG, "setAudioSessionId: " + audioSessionId);
|
||||
this.audioSessionId = audioSessionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an AudioDeviceModule based on the supplied arguments. The caller takes ownership
|
||||
* and is responsible for calling release().
|
||||
*/
|
||||
public OboeAudioDeviceModule createAudioDeviceModule() {
|
||||
Logging.w(TAG, "createAudioDeviceModule");
|
||||
return new OboeAudioDeviceModule(
|
||||
useSoftwareAcousticEchoCanceler,
|
||||
useSoftwareNoiseSuppressor,
|
||||
useExclusiveSharingMode,
|
||||
audioSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useSoftwareAcousticEchoCanceler;
|
||||
private boolean useSoftwareNoiseSuppressor;
|
||||
private boolean useExclusiveSharingMode;
|
||||
private int audioSessionId;
|
||||
|
||||
private final Object nativeLock = new Object();
|
||||
private long nativeAudioDeviceModule;
|
||||
|
||||
private OboeAudioDeviceModule(
|
||||
boolean useSoftwareAcousticEchoCanceler,
|
||||
boolean useSoftwareNoiseSuppressor,
|
||||
boolean useExclusiveSharingMode,
|
||||
int audioSessionId) {
|
||||
Logging.w(TAG, "OboeAudioDeviceModule");
|
||||
this.useSoftwareAcousticEchoCanceler = useSoftwareAcousticEchoCanceler;
|
||||
this.useSoftwareNoiseSuppressor = useSoftwareNoiseSuppressor;
|
||||
this.useExclusiveSharingMode = useExclusiveSharingMode;
|
||||
this.audioSessionId = audioSessionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeAudioDeviceModulePointer() {
|
||||
Logging.w(TAG, "getNativeAudioDeviceModulePointer");
|
||||
synchronized (nativeLock) {
|
||||
if (nativeAudioDeviceModule == 0) {
|
||||
Logging.w(TAG, "calling nativeCreateAudioDeviceModule");
|
||||
nativeAudioDeviceModule = nativeCreateAudioDeviceModule(
|
||||
useSoftwareAcousticEchoCanceler,
|
||||
useSoftwareNoiseSuppressor,
|
||||
useExclusiveSharingMode,
|
||||
audioSessionId);
|
||||
}
|
||||
return nativeAudioDeviceModule;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
Logging.w(TAG, "release");
|
||||
synchronized (nativeLock) {
|
||||
if (nativeAudioDeviceModule != 0) {
|
||||
JniCommon.nativeReleaseRef(nativeAudioDeviceModule);
|
||||
nativeAudioDeviceModule = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpeakerMute(boolean mute) {
|
||||
Logging.e(TAG, "Not supported; setSpeakerMute: " + mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMicrophoneMute(boolean mute) {
|
||||
Logging.e(TAG, "Not supported; setMicrophoneMute: " + mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setNoiseSuppressorEnabled(boolean enabled) {
|
||||
Logging.e(TAG, "Not supported; setNoiseSuppressorEnabled: " + enabled);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static native long nativeCreateAudioDeviceModule(
|
||||
boolean useSoftwareAcousticEchoCanceler,
|
||||
boolean useSoftwareNoiseSuppressor,
|
||||
boolean useExclusiveSharingMode,
|
||||
int audioSessionId);
|
||||
}
|
976
sdk/android/src/jni/audio_device/audio_device_module_oboe.cc
Normal file
976
sdk/android/src/jni/audio_device/audio_device_module_oboe.cc
Normal file
|
@ -0,0 +1,976 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "sdk/android/src/jni/audio_device/audio_device_module.h"
|
||||
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "api/sequence_checker.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <atomic>
|
||||
|
||||
// We expect Oboe to use our expected configuration:
|
||||
// WebRTC is looking for signed 16-bit PCM data at 48000Hz.
|
||||
constexpr oboe::AudioFormat kSampleFormat = oboe::AudioFormat::I16;
|
||||
constexpr int32_t kSampleRate = 48000;
|
||||
// WebRTC audio callbacks handle 10ms chunks, or 480 frames at 48000Hz per channel.
|
||||
constexpr oboe::ChannelCount kChannelCount = oboe::ChannelCount::Mono;
|
||||
constexpr size_t kMaxFramesPerCallback = (kSampleRate / 100);
|
||||
|
||||
// When delay can't be obtained, use a fairly high latency delay value by default.
|
||||
constexpr uint16_t kDefaultPlayoutDelayMs = 150;
|
||||
|
||||
constexpr int kInvalidAudioSessionId = -1;
|
||||
|
||||
// Limit logging values that get updated frequently.
|
||||
constexpr int kLogEveryN = 100;
|
||||
|
||||
namespace webrtc {
|
||||
namespace jni {
|
||||
|
||||
namespace {
|
||||
|
||||
// Implements an Audio Device Manager using the Oboe C++ audio library (https://github.com/google/oboe).
|
||||
class AndroidAudioDeviceModuleOboe :
|
||||
public AudioDeviceModule,
|
||||
public oboe::AudioStreamDataCallback,
|
||||
public oboe::AudioStreamErrorCallback {
|
||||
public:
|
||||
AndroidAudioDeviceModuleOboe(
|
||||
bool use_software_acoustic_echo_canceler,
|
||||
bool use_software_noise_suppressor,
|
||||
bool use_exclusive_sharing_mode,
|
||||
int audio_session_id)
|
||||
: use_software_acoustic_echo_canceler_(use_software_acoustic_echo_canceler),
|
||||
use_software_noise_suppressor_(use_software_noise_suppressor),
|
||||
use_exclusive_sharing_mode_(use_exclusive_sharing_mode),
|
||||
audio_session_id_(audio_session_id) {
|
||||
RTC_LOG(LS_WARNING) << "AndroidAudioDeviceModuleOboe::AndroidAudioDeviceModuleOboe";
|
||||
thread_checker_.Detach();
|
||||
}
|
||||
|
||||
~AndroidAudioDeviceModuleOboe() override {
|
||||
RTC_LOG(LS_WARNING) << "AndroidAudioDeviceModuleOboe::~AndroidAudioDeviceModuleOboe";
|
||||
}
|
||||
|
||||
// Retrieve the currently utilized audio layer
|
||||
// There is only one audio layer as far as this implementation is concerned. Use
|
||||
// kAndroidJavaAudio to make sure the default implementation isn't used.
|
||||
int32_t ActiveAudioLayer(AudioDeviceModule::AudioLayer* audioLayer) const override {
|
||||
RTC_LOG(LS_WARNING) << "ActiveAudioLayer always kAndroidJavaAudio";
|
||||
*audioLayer = kAndroidJavaAudio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Full-duplex transportation of PCM audio
|
||||
// Invoked when the VoIP Engine is initialized.
|
||||
int32_t RegisterAudioCallback(AudioTransport* audioCallback) override {
|
||||
RTC_LOG(LS_WARNING) << __FUNCTION__;
|
||||
if (is_playing_ || is_recording_) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to set audio transport since media was active";
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio_callback_ = audioCallback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Main initialization and termination
|
||||
|
||||
// Perform general initialization, when a call is going to be established, but
|
||||
// before the user should start sending and receiving audio.
|
||||
int32_t Init() override {
|
||||
RTC_LOG(LS_WARNING) << "Init, using Oboe version: " << oboe::Version::Text;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t Terminate() override {
|
||||
RTC_LOG(LS_WARNING) << __FUNCTION__;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (input_stream_) {
|
||||
input_stream_->stop();
|
||||
input_stream_->close();
|
||||
input_stream_ = nullptr;
|
||||
}
|
||||
if (output_stream_) {
|
||||
output_stream_->stop();
|
||||
output_stream_->close();
|
||||
output_stream_ = nullptr;
|
||||
}
|
||||
|
||||
playout_initialized_ = false;
|
||||
recording_initialized_ = false;
|
||||
|
||||
is_playing_ = false;
|
||||
is_recording_ = false;
|
||||
|
||||
initialized_ = false;
|
||||
thread_checker_.Detach();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Initialized() const override {
|
||||
RTC_LOG(LS_WARNING) << "Initialized " << initialized_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
// Device enumeration
|
||||
|
||||
// This implementation only supports one playout device.
|
||||
int16_t PlayoutDevices() override {
|
||||
RTC_LOG(LS_WARNING) << "PlayoutDevices always 1";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This implementation only supports one playout device.
|
||||
int16_t RecordingDevices() override {
|
||||
RTC_LOG(LS_WARNING) << "RecordingDevices always 1";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t PlayoutDeviceName(uint16_t index,
|
||||
char name[kAdmMaxDeviceNameSize],
|
||||
char guid[kAdmMaxGuidSize]) override {
|
||||
RTC_LOG(LS_ERROR) << "PlayoutDeviceName (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t RecordingDeviceName(uint16_t index,
|
||||
char name[kAdmMaxDeviceNameSize],
|
||||
char guid[kAdmMaxGuidSize]) override {
|
||||
RTC_LOG(LS_ERROR) << "RecordingDeviceName (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Device selection
|
||||
|
||||
int32_t SetPlayoutDevice(uint16_t index) override {
|
||||
// OK to use but it has no effect currently since device selection is done
|
||||
// using Android APIs instead.
|
||||
RTC_LOG(LS_WARNING) << "SetPlayoutDevice " << index << ", (no effect!)";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device) override {
|
||||
RTC_LOG(LS_ERROR) << "SetPlayoutDevice (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t SetRecordingDevice(uint16_t index) override {
|
||||
// OK to use but it has no effect currently since device selection is done
|
||||
// using Android APIs instead.
|
||||
RTC_LOG(LS_WARNING) << "SetRecordingDevice " << index << ", (no effect!)";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device) override {
|
||||
RTC_LOG(LS_ERROR) << "SetRecordingDevice (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Audio transport initialization
|
||||
|
||||
int32_t PlayoutIsAvailable(bool* available) override {
|
||||
RTC_LOG(LS_WARNING) << "PlayoutIsAvailable always true";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*available = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t InitPlayout() override {
|
||||
RTC_LOG(LS_WARNING) << "InitPlayout";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
if (playout_initialized_) {
|
||||
RTC_LOG(LS_WARNING) << "Playout is already initialized!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (CreateOutputStream() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
playout_initialized_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PlayoutIsInitialized() const override {
|
||||
RTC_LOG(LS_WARNING) << "PlayoutIsInitialized " << playout_initialized_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return playout_initialized_;
|
||||
}
|
||||
|
||||
int32_t RecordingIsAvailable(bool* available) override {
|
||||
RTC_LOG(LS_WARNING) << "RecordingIsAvailable always true";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*available = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t InitRecording() override {
|
||||
RTC_LOG(LS_WARNING) << "InitRecording";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
if (recording_initialized_) {
|
||||
RTC_LOG(LS_WARNING) << "Recording is already initialized!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (CreateInputStream() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
recording_initialized_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool RecordingIsInitialized() const override {
|
||||
RTC_LOG(LS_WARNING) << "RecordingIsInitialized " << recording_initialized_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return recording_initialized_;
|
||||
}
|
||||
|
||||
// Audio transport control
|
||||
// Note: We generally choose non-blocking operations when starting/stopping streams.
|
||||
// Note that the general is_playing_ and is_recording_ flags may not be in sync with
|
||||
// the Oboe states, but they are good enough to use as intentions.
|
||||
|
||||
int32_t StartPlayout() override {
|
||||
RTC_LOG(LS_WARNING) << "StartPlayout";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
if (!playout_initialized_) {
|
||||
RTC_LOG(LS_ERROR) << "Playout is not initialized!";
|
||||
return -1;
|
||||
}
|
||||
if (is_playing_) {
|
||||
RTC_LOG(LS_WARNING) << "Playout is already started!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
oboe::Result result = output_stream_->requestStart();
|
||||
if (result == oboe::Result::OK) {
|
||||
is_playing_ = true;
|
||||
return 0;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Failed to start the output stream: " << oboe::convertToText(result);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t StopPlayout() override {
|
||||
RTC_LOG(LS_WARNING) << "StopPlayout";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
if (!is_playing_) {
|
||||
RTC_LOG(LS_WARNING) << "Playout is already stopped!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Blocking call to stop the output stream.
|
||||
oboe::Result result = output_stream_->stop();
|
||||
// In most error cases, playout is stopped, and we'll return -1, so clear the state flag.
|
||||
is_playing_ = false;
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to stop the output stream: " << oboe::convertToText(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Playing() const override {
|
||||
RTC_LOG(LS_WARNING) << "Playing " << is_playing_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return is_playing_;
|
||||
}
|
||||
|
||||
int32_t StartRecording() override {
|
||||
RTC_LOG(LS_WARNING) << "StartRecording";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
if (!recording_initialized_) {
|
||||
RTC_LOG(LS_ERROR) << "Recording is not initialized!";
|
||||
return -1;
|
||||
}
|
||||
if (is_recording_) {
|
||||
RTC_LOG(LS_WARNING) << "Recording is already started!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
oboe::Result result = input_stream_->requestStart();
|
||||
if (result == oboe::Result::OK) {
|
||||
is_recording_ = true;
|
||||
return 0;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Failed to start the input stream: " << oboe::convertToText(result);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t StopRecording() override {
|
||||
RTC_LOG(LS_WARNING) << "StopRecording";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
if (!is_recording_) {
|
||||
RTC_LOG(LS_WARNING) << "Recording is already stopped!";
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Blocking call to stop the output stream.
|
||||
oboe::Result result = input_stream_->stop();
|
||||
// In most error cases, recording is stopped, and we'll return -1, so clear the state flag.
|
||||
is_recording_ = false;
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to stop the input stream: " << oboe::convertToText(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Recording() const override {
|
||||
RTC_LOG(LS_WARNING) << "Recording " << is_recording_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return is_recording_;
|
||||
}
|
||||
|
||||
// Audio mixer initialization
|
||||
// Use the module initialization to indicate device readiness.
|
||||
|
||||
int32_t InitSpeaker() override {
|
||||
RTC_LOG(LS_WARNING) << "InitSpeaker " << (initialized_ ? 0 : -1);
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return initialized_ ? 0 : -1;
|
||||
}
|
||||
|
||||
bool SpeakerIsInitialized() const override {
|
||||
RTC_LOG(LS_WARNING) << "SpeakerIsInitialized " << initialized_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
int32_t InitMicrophone() override {
|
||||
RTC_LOG(LS_WARNING) << "InitMicrophone " << (initialized_ ? 0 : -1);
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return initialized_ ? 0 : -1;
|
||||
}
|
||||
|
||||
bool MicrophoneIsInitialized() const override {
|
||||
RTC_LOG(LS_WARNING) << "MicrophoneIsInitialized " << initialized_;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
// Speaker volume controls
|
||||
|
||||
int32_t SpeakerVolumeIsAvailable(bool* available) override {
|
||||
RTC_LOG(LS_WARNING) << "SpeakerVolumeIsAvailable always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t SetSpeakerVolume(uint32_t volume) override {
|
||||
RTC_LOG(LS_WARNING) << "SetSpeakerVolume always success";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t SpeakerVolume(uint32_t* output_volume) const override {
|
||||
RTC_LOG(LS_WARNING) << "SpeakerVolume always 0";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
*output_volume = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t MaxSpeakerVolume(uint32_t* output_max_volume) const override {
|
||||
RTC_LOG(LS_WARNING) << "MaxSpeakerVolume always 0";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
*output_max_volume = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t MinSpeakerVolume(uint32_t* output_min_volume) const override {
|
||||
RTC_LOG(LS_WARNING) << "MinSpeakerVolume always 0";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
*output_min_volume = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Microphone volume controls
|
||||
|
||||
int32_t MicrophoneVolumeIsAvailable(bool* available) override {
|
||||
RTC_LOG(LS_WARNING) << "MicrophoneVolumeIsAvailable always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*available = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t SetMicrophoneVolume(uint32_t volume) override {
|
||||
RTC_LOG(LS_ERROR) << "SetMicrophoneVolume (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t MicrophoneVolume(uint32_t* volume) const override {
|
||||
RTC_LOG(LS_ERROR) << "MicrophoneVolume (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override {
|
||||
RTC_LOG(LS_ERROR) << "MaxMicrophoneVolume (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t MinMicrophoneVolume(uint32_t* minVolume) const override {
|
||||
RTC_LOG(LS_ERROR) << "MinMicrophoneVolume (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Speaker mute control
|
||||
|
||||
int32_t SpeakerMuteIsAvailable(bool* available) override {
|
||||
RTC_LOG(LS_ERROR) << "SpeakerMuteIsAvailable (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t SetSpeakerMute(bool enable) override {
|
||||
RTC_LOG(LS_ERROR) << "SetSpeakerMute (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t SpeakerMute(bool* enabled) const override {
|
||||
RTC_LOG(LS_ERROR) << "SpeakerMute (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Microphone mute control
|
||||
|
||||
int32_t MicrophoneMuteIsAvailable(bool* available) override {
|
||||
RTC_LOG(LS_ERROR) << "MicrophoneMuteIsAvailable (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t SetMicrophoneMute(bool enable) override {
|
||||
RTC_LOG(LS_ERROR) << "SetMicrophoneMute (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t MicrophoneMute(bool* enabled) const override {
|
||||
RTC_LOG(LS_ERROR) << "MicrophoneMute (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Stereo support
|
||||
|
||||
// None of our models support stereo playout for communication. Speech is always captured
|
||||
// in mono and the playout device should up-mix to all applicable output emitters.
|
||||
int32_t StereoPlayoutIsAvailable(bool* available) const override {
|
||||
RTC_LOG(LS_WARNING) << "StereoPlayoutIsAvailable always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We don't expect stereo to be enabled, especially on-the-fly.
|
||||
int32_t SetStereoPlayout(bool enable) override {
|
||||
RTC_LOG(LS_WARNING) << "SetStereoPlayout " << enable;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (enable) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t StereoPlayout(bool* enabled) const override {
|
||||
RTC_LOG(LS_WARNING) << "StereoPlayout always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// None of our models support stereo recording for communication. Speech is always
|
||||
// captured in mono.
|
||||
int32_t StereoRecordingIsAvailable(bool* available) const override {
|
||||
RTC_LOG(LS_WARNING) << "StereoRecordingIsAvailable always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We don't expect stereo to be enabled, especially on-the-fly.
|
||||
int32_t SetStereoRecording(bool enable) override {
|
||||
RTC_LOG(LS_WARNING) << "SetStereoRecording " << enable;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (enable) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t StereoRecording(bool* enabled) const override {
|
||||
RTC_LOG(LS_WARNING) << "StereoRecording always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
*enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Playout delay calculation.
|
||||
int32_t PlayoutDelay(uint16_t* delay_ms) const override {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
|
||||
// Return the latest value set by the data callback.
|
||||
*delay_ms = playout_delay_ms_;
|
||||
|
||||
// Limit logging of the playout delay.
|
||||
if (++delay_log_counter_ % kLogEveryN == 0) {
|
||||
RTC_LOG(LS_WARNING) << "playout_delay_ms_: " << *delay_ms;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only supported on Android.
|
||||
|
||||
bool BuiltInAECIsAvailable() const override {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_)
|
||||
return false;
|
||||
|
||||
RTC_LOG(LS_WARNING) << "BuiltInAECIsAvailable " << !use_software_acoustic_echo_canceler_;
|
||||
return !use_software_acoustic_echo_canceler_;
|
||||
}
|
||||
|
||||
// Not implemented for any input device on Android.
|
||||
bool BuiltInAGCIsAvailable() const override {
|
||||
RTC_LOG(LS_WARNING) << "BuiltInAGCIsAvailable always false";
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BuiltInNSIsAvailable() const override {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_)
|
||||
return false;
|
||||
|
||||
RTC_LOG(LS_WARNING) << "BuiltInNSIsAvailable " << !use_software_noise_suppressor_;
|
||||
return !use_software_noise_suppressor_;
|
||||
}
|
||||
|
||||
// Enables the built-in audio effects. Only supported on Android.
|
||||
|
||||
int32_t EnableBuiltInAEC(bool enable) override {
|
||||
RTC_LOG(LS_WARNING) << "EnableBuiltInAEC " << enable;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_)
|
||||
return -1;
|
||||
|
||||
// This is a noop for us.
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t EnableBuiltInAGC(bool enable) override {
|
||||
RTC_LOG(LS_ERROR) << "EnableBuiltInAGC " << enable << ", (should not be reached)";
|
||||
RTC_DCHECK_NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t EnableBuiltInNS(bool enable) override {
|
||||
RTC_LOG(LS_WARNING) << "EnableBuiltInNS " << enable;
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
if (!initialized_)
|
||||
return -1;
|
||||
|
||||
// This is a noop for us.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Playout underrun count. Only supported on Android.
|
||||
int32_t GetPlayoutUnderrunCount() const override {
|
||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||
|
||||
// Return the latest value set by the data callback.
|
||||
if (playout_underrun_count_ != 0) {
|
||||
// Limit logging of the playout underrun count.
|
||||
if (++underrun_log_counter_ % kLogEveryN == 0) {
|
||||
RTC_LOG(LS_WARNING) << "playout_underrun_count_: " << playout_underrun_count_;
|
||||
}
|
||||
}
|
||||
return playout_underrun_count_;
|
||||
}
|
||||
|
||||
// Used to generate RTC stats.
|
||||
absl::optional<Stats> GetStats() const override {
|
||||
// Returning nullopt because stats are not supported in this implementation.
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// Oboe Callbacks
|
||||
|
||||
bool onError(oboe::AudioStream *audioStream, oboe::Result error) override {
|
||||
if (audioStream == output_stream_.get()) {
|
||||
RTC_LOG(LS_WARNING) << "onError on output stream: " << oboe::convertToText(error);
|
||||
if (error == oboe::Result::ErrorDisconnected) {
|
||||
oboe::Result result = output_stream_->stop();
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to stop the output stream: " << oboe::convertToText(result);
|
||||
}
|
||||
result = output_stream_->close();
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to close the output stream: " << oboe::convertToText(result);
|
||||
}
|
||||
|
||||
output_stream_.reset();
|
||||
|
||||
if (playout_initialized_) {
|
||||
if (CreateOutputStream() != 0) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to recreate the output stream!";
|
||||
playout_initialized_ = false;
|
||||
is_playing_ = false;
|
||||
} else {
|
||||
if (is_playing_) {
|
||||
result = output_stream_->requestStart();
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to start the output stream.";
|
||||
is_playing_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Unhandled output stream error";
|
||||
return false;
|
||||
}
|
||||
} else if (audioStream == input_stream_.get()) {
|
||||
RTC_LOG(LS_WARNING) << "onError on input stream: " << oboe::convertToText(error);
|
||||
if (error == oboe::Result::ErrorDisconnected) {
|
||||
oboe::Result result = input_stream_->stop();
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to stop the input stream: " << oboe::convertToText(result);
|
||||
}
|
||||
result = input_stream_->close();
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to close the input stream: " << oboe::convertToText(result);
|
||||
}
|
||||
|
||||
input_stream_.reset();
|
||||
|
||||
if (recording_initialized_) {
|
||||
if (CreateInputStream() != 0) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to recreate the input stream!";
|
||||
recording_initialized_ = false;
|
||||
is_recording_ = false;
|
||||
} else {
|
||||
if (is_recording_) {
|
||||
result = input_stream_->requestStart();
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to start the input stream.";
|
||||
is_recording_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "Unhandled input stream error";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "onError for unknown stream: " << oboe::convertToText(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void onErrorBeforeClose(oboe::AudioStream *audioStream, oboe::Result error) override {
|
||||
RTC_LOG(LS_ERROR) << "onErrorBeforeClose invoked! " << oboe::convertToText(error);
|
||||
}
|
||||
|
||||
void onErrorAfterClose(oboe::AudioStream *audioStream, oboe::Result error) override {
|
||||
RTC_LOG(LS_ERROR) << "onErrorAfterClose invoked! " << oboe::convertToText(error);
|
||||
}
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) override {
|
||||
if (!audio_callback_) {
|
||||
RTC_LOG(LS_ERROR) << "Audio callback is not set!";
|
||||
std::fill(static_cast<int16_t*>(audioData), static_cast<int16_t*>(audioData) + numFrames, 0);
|
||||
return oboe::DataCallbackResult::Stop;
|
||||
}
|
||||
|
||||
if (audioStream == output_stream_.get()) {
|
||||
size_t num_samples_out = 0;
|
||||
|
||||
// TODO: Hook up these extra fields if necessary.
|
||||
int64_t elapsed_time_ms = -1;
|
||||
int64_t ntp_time_ms = -1;
|
||||
|
||||
int32_t result = audio_callback_->NeedMorePlayData(
|
||||
numFrames,
|
||||
sizeof(int16_t),
|
||||
kChannelCount,
|
||||
kSampleRate,
|
||||
static_cast<int16_t*>(audioData),
|
||||
num_samples_out,
|
||||
&elapsed_time_ms,
|
||||
&ntp_time_ms);
|
||||
if (result != 0) {
|
||||
RTC_LOG(LS_ERROR) << "NeedMorePlayData failed with error: " << result;
|
||||
std::fill(static_cast<int16_t*>(audioData), static_cast<int16_t*>(audioData) + numFrames, 0);
|
||||
}
|
||||
|
||||
// Update the delay of the playout.
|
||||
auto latencyResult = output_stream_->calculateLatencyMillis();
|
||||
if (latencyResult) {
|
||||
playout_delay_ms_ = static_cast<uint16_t>(
|
||||
std::clamp(latencyResult.value(), static_cast<double>(0), static_cast<double>(UINT16_MAX))
|
||||
);
|
||||
}
|
||||
|
||||
// Update the playout underrun count.
|
||||
auto countResult = output_stream_->getXRunCount();
|
||||
if (countResult) {
|
||||
playout_underrun_count_ = countResult.value();
|
||||
}
|
||||
} else if (audioStream == input_stream_.get()) {
|
||||
// TODO: Hook up these extra fields if necessary.
|
||||
uint32_t new_mic_level = 0;
|
||||
|
||||
int32_t result = audio_callback_->RecordedDataIsAvailable(
|
||||
static_cast<int16_t*>(audioData),
|
||||
numFrames,
|
||||
sizeof(int16_t),
|
||||
kChannelCount,
|
||||
kSampleRate,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
new_mic_level);
|
||||
if (result != 0) {
|
||||
RTC_LOG(LS_ERROR) << "RecordedDataIsAvailable failed with error: " << result;
|
||||
std::fill(static_cast<int16_t*>(audioData), static_cast<int16_t*>(audioData) + numFrames, 0);
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "onAudioReady on unknown stream!";
|
||||
std::fill(static_cast<int16_t*>(audioData), static_cast<int16_t*>(audioData) + numFrames, 0);
|
||||
}
|
||||
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void LogStreamConfiguration(const std::shared_ptr<oboe::AudioStream>& stream) {
|
||||
RTC_LOG(LS_WARNING) << " audioApi: " << oboe::convertToText(stream->getAudioApi());
|
||||
RTC_LOG(LS_WARNING) << " deviceId: " << stream->getDeviceId();
|
||||
RTC_LOG(LS_WARNING) << " format: " << oboe::convertToText(stream->getFormat());
|
||||
RTC_LOG(LS_WARNING) << " sampleRate: " << stream->getSampleRate();
|
||||
RTC_LOG(LS_WARNING) << " channelCount: " << stream->getChannelCount();
|
||||
RTC_LOG(LS_WARNING) << " sharingMode: " << oboe::convertToText(stream->getSharingMode());
|
||||
RTC_LOG(LS_WARNING) << " performanceMode: " << oboe::convertToText(stream->getPerformanceMode());
|
||||
if (stream->getDirection() == oboe::Direction::Output) {
|
||||
RTC_LOG(LS_WARNING) << " usage: " << oboe::convertToText(stream->getUsage());
|
||||
RTC_LOG(LS_WARNING) << " contentType: " << oboe::convertToText(stream->getContentType());
|
||||
} else if (stream->getDirection() == oboe::Direction::Input) {
|
||||
RTC_LOG(LS_WARNING) << " inputPreset: " << oboe::convertToText(stream->getInputPreset());
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureCommonStreamSettings(oboe::AudioStreamBuilder& builder, oboe::Direction direction) {
|
||||
builder.setDirection(direction);
|
||||
|
||||
// Keep using OpenSL-ES on Android 8.1 due to problems with AAudio.
|
||||
if (oboe::getSdkVersion() == __ANDROID_API_O_MR1__) {
|
||||
builder.setAudioApi(oboe::AudioApi::OpenSLES);
|
||||
}
|
||||
|
||||
// Let Oboe manage the configuration we want.
|
||||
builder.setFormat(kSampleFormat);
|
||||
builder.setSampleRate(kSampleRate);
|
||||
builder.setChannelCount(kChannelCount);
|
||||
builder.setFramesPerDataCallback(kMaxFramesPerCallback);
|
||||
|
||||
// And allow Oboe to perform conversions if necessary.
|
||||
builder.setFormatConversionAllowed(true);
|
||||
// Use medium to balance performance and quality.
|
||||
builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium);
|
||||
builder.setChannelConversionAllowed(true);
|
||||
|
||||
if (use_exclusive_sharing_mode_) {
|
||||
// Attempt to use Exclusive sharing mode for the lowest possible latency.
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive);
|
||||
}
|
||||
|
||||
// Set the performance mode to get the lowest possible latency.
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
|
||||
// Set callbacks for handling PCM data and other underlying API notifications.
|
||||
// To avoid an abstraction, we'll set the shared pointer with a no-op deleter.
|
||||
builder.setDataCallback(std::shared_ptr<oboe::AudioStreamDataCallback>(
|
||||
this,
|
||||
[](oboe::AudioStreamDataCallback*) {}
|
||||
));
|
||||
builder.setErrorCallback(std::shared_ptr<oboe::AudioStreamErrorCallback>(
|
||||
this,
|
||||
[](oboe::AudioStreamErrorCallback*) {}
|
||||
));
|
||||
|
||||
if (audio_session_id_ != kInvalidAudioSessionId) {
|
||||
RTC_LOG(LS_WARNING) << "Setting session_id: " << audio_session_id_;
|
||||
builder.setSessionId((oboe::SessionId) audio_session_id_);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CreateOutputStream() {
|
||||
RTC_LOG(LS_WARNING) << "CreateOutputStream";
|
||||
|
||||
// Create a builder for an Oboe output audio stream.
|
||||
oboe::AudioStreamBuilder builder;
|
||||
ConfigureCommonStreamSettings(builder, oboe::Direction::Output);
|
||||
|
||||
// Specifying usage and contentType might result in better volume and routing decisions.
|
||||
// These settings are specific to the output stream.
|
||||
builder.setUsage(oboe::Usage::VoiceCommunication);
|
||||
builder.setContentType(oboe::ContentType::Speech);
|
||||
|
||||
oboe::Result result = builder.openStream(output_stream_);
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to open the output stream: " << oboe::convertToText(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LogStreamConfiguration(output_stream_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t CreateInputStream() {
|
||||
RTC_LOG(LS_WARNING) << "CreateInputStream";
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
ConfigureCommonStreamSettings(builder, oboe::Direction::Input);
|
||||
|
||||
// Specifying an inputPreset might result in better volume and routing decisions (and privacy).
|
||||
// This setting is specific to the input stream.
|
||||
builder.setInputPreset(oboe::InputPreset::VoiceCommunication);
|
||||
|
||||
oboe::Result result = builder.openStream(input_stream_);
|
||||
if (result != oboe::Result::OK) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to open the input stream: " << oboe::convertToText(result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LogStreamConfiguration(input_stream_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SequenceChecker thread_checker_;
|
||||
|
||||
const bool use_software_acoustic_echo_canceler_;
|
||||
const bool use_software_noise_suppressor_;
|
||||
const bool use_exclusive_sharing_mode_;
|
||||
const int32_t audio_session_id_;
|
||||
|
||||
std::shared_ptr<oboe::AudioStream> input_stream_;
|
||||
std::shared_ptr<oboe::AudioStream> output_stream_;
|
||||
|
||||
webrtc::AudioTransport* audio_callback_ = nullptr;
|
||||
|
||||
std::atomic<bool> initialized_ { false };
|
||||
|
||||
std::atomic<bool> playout_initialized_ { false };
|
||||
std::atomic<bool> recording_initialized_ { false };
|
||||
|
||||
std::atomic<bool> is_playing_ { false };
|
||||
std::atomic<bool> is_recording_ { false };
|
||||
|
||||
std::atomic<uint16_t> playout_delay_ms_ { kDefaultPlayoutDelayMs };
|
||||
std::atomic<int32_t> playout_underrun_count_ { 0 };
|
||||
|
||||
mutable std::atomic<uint32_t> delay_log_counter_ { 0 };
|
||||
mutable std::atomic<uint32_t> underrun_log_counter_ { 0 };
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModuleOboe(
|
||||
bool use_software_acoustic_echo_canceler,
|
||||
bool use_software_noise_suppressor,
|
||||
bool use_exclusive_sharing_mode,
|
||||
int audio_session_id) {
|
||||
RTC_LOG(LS_WARNING) << "CreateAudioDeviceModuleOboe";
|
||||
return rtc::make_ref_counted<AndroidAudioDeviceModuleOboe>(
|
||||
use_software_acoustic_echo_canceler,
|
||||
use_software_noise_suppressor,
|
||||
use_exclusive_sharing_mode,
|
||||
audio_session_id);
|
||||
}
|
||||
|
||||
} // namespace jni
|
||||
} // namespace webrtc
|
24
sdk/android/src/jni/audio_device/audio_device_module_oboe.h
Normal file
24
sdk/android/src/jni/audio_device/audio_device_module_oboe.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
#ifndef SDK_ANDROID_SRC_JNI_AUDIO_DEVICE_AUDIO_DEVICE_MODULE_OBOE_H_
|
||||
#define SDK_ANDROID_SRC_JNI_AUDIO_DEVICE_AUDIO_DEVICE_MODULE_OBOE_H_
|
||||
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace jni {
|
||||
|
||||
// Create the Oboe-based AudioDeviceModule.
|
||||
rtc::scoped_refptr<AudioDeviceModule> CreateAudioDeviceModuleOboe(
|
||||
bool use_software_acoustic_echo_canceler,
|
||||
bool use_software_noise_suppressor,
|
||||
bool use_exclusive_sharing_mode,
|
||||
int audio_session_id);
|
||||
|
||||
} // namespace jni
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // SDK_ANDROID_SRC_JNI_AUDIO_DEVICE_AUDIO_DEVICE_MODULE_OBOE_H_
|
30
sdk/android/src/jni/audio_device/oboe_audio_device_module.cc
Normal file
30
sdk/android/src/jni/audio_device/oboe_audio_device_module.cc
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
#include "sdk/android/generated_java_audio_jni/OboeAudioDeviceModule_jni.h"
|
||||
#include "sdk/android/src/jni/audio_device/audio_device_module_oboe.h"
|
||||
#include "sdk/android/src/jni/jni_helpers.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace jni {
|
||||
|
||||
static jlong JNI_OboeAudioDeviceModule_CreateAudioDeviceModule(
|
||||
JNIEnv* env,
|
||||
jboolean j_use_software_acoustic_echo_canceler,
|
||||
jboolean j_use_software_noise_suppressor,
|
||||
jboolean j_use_exclusive_sharing_mode,
|
||||
int audio_session_id) {
|
||||
RTC_LOG(LS_WARNING) << "JNI_OboeAudioDeviceModule_CreateAudioDeviceModule";
|
||||
return jlongFromPointer(CreateAudioDeviceModuleOboe(
|
||||
j_use_software_acoustic_echo_canceler,
|
||||
j_use_software_noise_suppressor,
|
||||
j_use_exclusive_sharing_mode,
|
||||
audio_session_id)
|
||||
.release());
|
||||
}
|
||||
|
||||
} // namespace jni
|
||||
} // namespace webrtc
|
Loading…
Reference in a new issue