Android: Add AudioDeviceModule interface and clean up implementation code
This CL introduces sdk/android/api/org/webrtc/audio/AudioDeviceModule.java, which is the new interface for audio device modules on Android. This CL also refactors the main AudioDeviceModule implementation, which is sdk/android/api/org/webrtc/audio/JavaAudioDeviceModule.java and makes it conform to the new interface. The old code used global static methods to configure the audio device code. This CL gets rid of all that and uses a builder pattern in JavaAudioDeviceModule instead. The only two dynamic methods left in the interface are setSpeakerMute() and setMicrophoneMute(). Removing the global static methods allowed a significant cleanup, and e.g. the file sdk/android/src/jni/audio_device/audio_manager.cc has been completely removed. The PeerConnectionFactory interface is also updated to allow passing in an external AudioDeviceModule. The current built-in ADM is encapsulated under LegacyAudioDeviceModule.java, which is the default for now to ensure backwards compatibility. Bug: webrtc:7452 Change-Id: I64d5f4dba9a004da001f1acb2bd0c1b1f2b64f21 Reviewed-on: https://webrtc-review.googlesource.com/65360 Commit-Queue: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Paulina Hensman <phensman@webrtc.org> Cr-Commit-Position: refs/heads/master@{#22765}
This commit is contained in:
committed by
Commit Bot
parent
3ab5c40f72
commit
66f1e9eb34
@ -13,6 +13,8 @@ package org.webrtc;
|
||||
import android.content.Context;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
import org.webrtc.audio.LegacyAudioDeviceModule;
|
||||
|
||||
/**
|
||||
* Java wrapper for a C++ PeerConnectionFactoryInterface. Main entry point to
|
||||
@ -132,6 +134,7 @@ public class PeerConnectionFactory {
|
||||
|
||||
public static class Builder {
|
||||
private @Nullable Options options;
|
||||
private @Nullable AudioDeviceModule audioDeviceModule = new LegacyAudioDeviceModule();
|
||||
private @Nullable VideoEncoderFactory encoderFactory;
|
||||
private @Nullable VideoDecoderFactory decoderFactory;
|
||||
private @Nullable AudioProcessingFactory audioProcessingFactory;
|
||||
@ -144,6 +147,11 @@ public class PeerConnectionFactory {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setAudioDeviceModule(AudioDeviceModule audioDeviceModule) {
|
||||
this.audioDeviceModule = audioDeviceModule;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVideoEncoderFactory(VideoEncoderFactory encoderFactory) {
|
||||
this.encoderFactory = encoderFactory;
|
||||
return this;
|
||||
@ -170,7 +178,7 @@ public class PeerConnectionFactory {
|
||||
}
|
||||
|
||||
public PeerConnectionFactory createPeerConnectionFactory() {
|
||||
return new PeerConnectionFactory(options, encoderFactory, decoderFactory,
|
||||
return new PeerConnectionFactory(options, audioDeviceModule, encoderFactory, decoderFactory,
|
||||
audioProcessingFactory, fecControllerFactoryFactory);
|
||||
}
|
||||
}
|
||||
@ -255,8 +263,8 @@ public class PeerConnectionFactory {
|
||||
public PeerConnectionFactory(
|
||||
Options options, VideoEncoderFactory encoderFactory, VideoDecoderFactory decoderFactory) {
|
||||
checkInitializeHasBeenCalled();
|
||||
nativeFactory = nativeCreatePeerConnectionFactory(
|
||||
ContextUtils.getApplicationContext(), options, encoderFactory, decoderFactory, 0, 0);
|
||||
nativeFactory = nativeCreatePeerConnectionFactory(ContextUtils.getApplicationContext(), options,
|
||||
0 /* audioDeviceModule */, encoderFactory, decoderFactory, 0, 0);
|
||||
if (nativeFactory == 0) {
|
||||
throw new RuntimeException("Failed to initialize PeerConnectionFactory!");
|
||||
}
|
||||
@ -265,16 +273,17 @@ public class PeerConnectionFactory {
|
||||
@Deprecated
|
||||
public PeerConnectionFactory(Options options, VideoEncoderFactory encoderFactory,
|
||||
VideoDecoderFactory decoderFactory, AudioProcessingFactory audioProcessingFactory) {
|
||||
this(options, encoderFactory, decoderFactory, audioProcessingFactory,
|
||||
null /* fecControllerFactoryFactory */);
|
||||
this(options, new LegacyAudioDeviceModule(), encoderFactory, decoderFactory,
|
||||
audioProcessingFactory, null /* fecControllerFactoryFactory */);
|
||||
}
|
||||
|
||||
private PeerConnectionFactory(Options options, @Nullable VideoEncoderFactory encoderFactory,
|
||||
@Nullable VideoDecoderFactory decoderFactory,
|
||||
private PeerConnectionFactory(Options options, @Nullable AudioDeviceModule audioDeviceModule,
|
||||
@Nullable VideoEncoderFactory encoderFactory, @Nullable VideoDecoderFactory decoderFactory,
|
||||
@Nullable AudioProcessingFactory audioProcessingFactory,
|
||||
@Nullable FecControllerFactoryFactoryInterface fecControllerFactoryFactory) {
|
||||
checkInitializeHasBeenCalled();
|
||||
nativeFactory = nativeCreatePeerConnectionFactory(ContextUtils.getApplicationContext(), options,
|
||||
audioDeviceModule == null ? 0 : audioDeviceModule.getNativeAudioDeviceModulePointer(),
|
||||
encoderFactory, decoderFactory,
|
||||
audioProcessingFactory == null ? 0 : audioProcessingFactory.createNative(),
|
||||
fecControllerFactoryFactory == null ? 0 : fecControllerFactoryFactory.createNative());
|
||||
@ -483,8 +492,9 @@ public class PeerConnectionFactory {
|
||||
private static native boolean nativeStartInternalTracingCapture(String tracingFilename);
|
||||
private static native void nativeStopInternalTracingCapture();
|
||||
private static native long nativeCreatePeerConnectionFactory(Context context, Options options,
|
||||
VideoEncoderFactory encoderFactory, VideoDecoderFactory decoderFactory,
|
||||
long nativeAudioProcessor, long nativeFecControllerFactory);
|
||||
long nativeAudioDeviceModule, VideoEncoderFactory encoderFactory,
|
||||
VideoDecoderFactory decoderFactory, long nativeAudioProcessor,
|
||||
long nativeFecControllerFactory);
|
||||
private static native long nativeCreatePeerConnection(long factory,
|
||||
PeerConnection.RTCConfiguration rtcConfig, MediaConstraints constraints, long nativeObserver);
|
||||
private static native long nativeCreateLocalMediaStream(long factory, String label);
|
||||
|
||||
38
sdk/android/api/org/webrtc/audio/AudioDeviceModule.java
Normal file
38
sdk/android/api/org/webrtc/audio/AudioDeviceModule.java
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2018 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.audio;
|
||||
|
||||
/**
|
||||
* This interface is a thin wrapper on top of a native C++ webrtc::AudioDeviceModule (ADM). The
|
||||
* reason for basing it on a native ADM instead of a pure Java interface is that we have two native
|
||||
* Android implementations (OpenSLES and AAudio) that does not make sense to wrap through JNI.
|
||||
*
|
||||
* <p>Note: This class is still under development and may change without notice.
|
||||
*/
|
||||
public interface AudioDeviceModule {
|
||||
/**
|
||||
* Returns a C++ pointer to a webrtc::AudioDeviceModule. Caller does _not_ take ownership and
|
||||
* lifetime is handled through the release() call.
|
||||
*/
|
||||
long getNativeAudioDeviceModulePointer();
|
||||
|
||||
/**
|
||||
* Release resources for this AudioDeviceModule, including native resources. The object should not
|
||||
* be used after this call.
|
||||
*/
|
||||
void release();
|
||||
|
||||
/** Control muting/unmuting the speaker. */
|
||||
void setSpeakerMute(boolean mute);
|
||||
|
||||
/** Control muting/unmuting the microphone. */
|
||||
void setMicrophoneMute(boolean mute);
|
||||
}
|
||||
@ -10,21 +10,143 @@
|
||||
|
||||
package org.webrtc.audio;
|
||||
|
||||
import org.webrtc.audio.WebRtcAudioManager;
|
||||
import org.webrtc.audio.WebRtcAudioRecord;
|
||||
import org.webrtc.audio.WebRtcAudioTrack;
|
||||
import org.webrtc.audio.WebRtcAudioUtils;
|
||||
import android.media.AudioManager;
|
||||
import android.content.Context;
|
||||
import org.webrtc.JNINamespace;
|
||||
import org.webrtc.JniCommon;
|
||||
import org.webrtc.Logging;
|
||||
|
||||
/**
|
||||
* AudioDeviceModule implemented using android.media.AudioRecord as input and
|
||||
* android.media.AudioTrack as output.
|
||||
*
|
||||
* <p>Note: This class is still under development and may change without notice.
|
||||
*/
|
||||
public class JavaAudioDeviceModule {
|
||||
/* AudioManager */
|
||||
public static void setStereoInput(boolean enable) {
|
||||
WebRtcAudioManager.setStereoInput(enable);
|
||||
@JNINamespace("webrtc::jni")
|
||||
public class JavaAudioDeviceModule implements AudioDeviceModule {
|
||||
private static final String TAG = "JavaAudioDeviceModule";
|
||||
|
||||
public static Builder builder(Context context) {
|
||||
return new Builder(context);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final Context context;
|
||||
private final AudioManager audioManager;
|
||||
private int sampleRate;
|
||||
private int audioSource = WebRtcAudioRecord.DEFAULT_AUDIO_SOURCE;
|
||||
private AudioTrackErrorCallback audioTrackErrorCallback;
|
||||
private AudioRecordErrorCallback audioRecordErrorCallback;
|
||||
private SamplesReadyCallback samplesReadyCallback;
|
||||
private boolean useHardwareAcousticEchoCanceler = isBuiltInAcousticEchoCancelerSupported();
|
||||
private boolean useHardwareNoiseSuppressor = isBuiltInNoiseSuppressorSupported();
|
||||
private boolean useStereoInput;
|
||||
private boolean useStereoOutput;
|
||||
|
||||
private Builder(Context context) {
|
||||
this.context = context;
|
||||
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
this.sampleRate = WebRtcAudioManager.getSampleRate(audioManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method if the default handling of querying the native sample rate shall be
|
||||
* overridden. Can be useful on some devices where the available Android APIs are known to
|
||||
* return invalid results.
|
||||
*/
|
||||
public Builder setSampleRate(int sampleRate) {
|
||||
this.sampleRate = sampleRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to change the audio source. The argument should be one of the values from
|
||||
* android.media.MediaRecorder.AudioSource. The default is AudioSource.VOICE_COMMUNICATION.
|
||||
*/
|
||||
public Builder setAudioSource(int audioSource) {
|
||||
this.audioSource = audioSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback to retrieve errors from the AudioTrack.
|
||||
*/
|
||||
public Builder setAudioTrackErrorCallback(AudioTrackErrorCallback audioTrackErrorCallback) {
|
||||
this.audioTrackErrorCallback = audioTrackErrorCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback to retrieve errors from the AudioRecord.
|
||||
*/
|
||||
public Builder setAudioRecordErrorCallback(AudioRecordErrorCallback audioRecordErrorCallback) {
|
||||
this.audioRecordErrorCallback = audioRecordErrorCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback to listen to the raw audio input from the AudioRecord.
|
||||
*/
|
||||
public Builder setSamplesReadyCallback(SamplesReadyCallback samplesReadyCallback) {
|
||||
this.samplesReadyCallback = samplesReadyCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if the built-in HW noise suppressor should be used or not. The default is on if it is
|
||||
* supported. It is possible to query support by calling isBuiltInNoiseSuppressorSupported().
|
||||
*/
|
||||
public Builder setUseHardwareNoiseSuppressor(boolean useHardwareNoiseSuppressor) {
|
||||
if (useHardwareNoiseSuppressor && !isBuiltInNoiseSuppressorSupported()) {
|
||||
Logging.e(TAG, "HW noise suppressor not supported");
|
||||
useHardwareNoiseSuppressor = false;
|
||||
}
|
||||
this.useHardwareNoiseSuppressor = useHardwareNoiseSuppressor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if the built-in HW acoustic echo canceler should be used or not. The default is on if
|
||||
* it is supported. It is possible to query support by calling
|
||||
* isBuiltInAcousticEchoCancelerSupported().
|
||||
*/
|
||||
public Builder setUseHardwareAcousticEchoCanceler(boolean useHardwareAcousticEchoCanceler) {
|
||||
if (useHardwareAcousticEchoCanceler && !isBuiltInAcousticEchoCancelerSupported()) {
|
||||
Logging.e(TAG, "HW acoustic echo canceler not supported");
|
||||
useHardwareAcousticEchoCanceler = false;
|
||||
}
|
||||
this.useHardwareAcousticEchoCanceler = useHardwareAcousticEchoCanceler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if stereo input should be used or not. The default is mono.
|
||||
*/
|
||||
public Builder setUseStereoInput(boolean useStereoInput) {
|
||||
this.useStereoInput = useStereoInput;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Control if stereo output should be used or not. The default is mono.
|
||||
*/
|
||||
public Builder setUseStereoOutput(boolean useStereoOutput) {
|
||||
this.useStereoOutput = useStereoOutput;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an AudioDeviceModule based on the supplied arguments. The caller takes ownership
|
||||
* and is responsible for calling release().
|
||||
*/
|
||||
public AudioDeviceModule createAudioDeviceModule() {
|
||||
final WebRtcAudioRecord audioInput =
|
||||
new WebRtcAudioRecord(context, audioManager, audioSource, audioRecordErrorCallback,
|
||||
samplesReadyCallback, useHardwareAcousticEchoCanceler, useHardwareNoiseSuppressor);
|
||||
final WebRtcAudioTrack audioOutput =
|
||||
new WebRtcAudioTrack(context, audioManager, audioTrackErrorCallback);
|
||||
final long nativeAudioDeviceModule = nativeCreateAudioDeviceModule(context, audioManager,
|
||||
audioInput, audioOutput, sampleRate, useStereoInput, useStereoOutput);
|
||||
return new JavaAudioDeviceModule(audioInput, audioOutput, nativeAudioDeviceModule);
|
||||
}
|
||||
}
|
||||
|
||||
/* AudioRecord */
|
||||
@ -82,14 +204,6 @@ public class JavaAudioDeviceModule {
|
||||
void onWebRtcAudioRecordSamplesReady(AudioSamples samples);
|
||||
}
|
||||
|
||||
public static void setErrorCallback(AudioRecordErrorCallback errorCallback) {
|
||||
WebRtcAudioRecord.setErrorCallback(errorCallback);
|
||||
}
|
||||
|
||||
public static void setOnAudioSamplesReady(SamplesReadyCallback callback) {
|
||||
WebRtcAudioRecord.setOnAudioSamplesReady(callback);
|
||||
}
|
||||
|
||||
/* AudioTrack */
|
||||
// Audio playout/track error handler functions.
|
||||
public enum AudioTrackStartErrorCode {
|
||||
@ -103,37 +217,57 @@ public class JavaAudioDeviceModule {
|
||||
void onWebRtcAudioTrackError(String errorMessage);
|
||||
}
|
||||
|
||||
public static void setErrorCallback(AudioTrackErrorCallback errorCallback) {
|
||||
WebRtcAudioTrack.setErrorCallback(errorCallback);
|
||||
/**
|
||||
* Returns true if the device supports built-in HW AEC, and the UUID is approved (some UUIDs can
|
||||
* be excluded).
|
||||
*/
|
||||
public static boolean isBuiltInAcousticEchoCancelerSupported() {
|
||||
return WebRtcAudioEffects.isAcousticEchoCancelerSupported();
|
||||
}
|
||||
|
||||
/* AudioUtils */
|
||||
public static void setWebRtcBasedAcousticEchoCanceler(boolean enable) {
|
||||
WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(enable);
|
||||
/**
|
||||
* Returns true if the device supports built-in HW NS, and the UUID is approved (some UUIDs can be
|
||||
* excluded).
|
||||
*/
|
||||
public static boolean isBuiltInNoiseSuppressorSupported() {
|
||||
return WebRtcAudioEffects.isNoiseSuppressorSupported();
|
||||
}
|
||||
|
||||
public static void setWebRtcBasedNoiseSuppressor(boolean enable) {
|
||||
WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(enable);
|
||||
private final WebRtcAudioRecord audioInput;
|
||||
private final WebRtcAudioTrack audioOutput;
|
||||
private long nativeAudioDeviceModule;
|
||||
|
||||
private JavaAudioDeviceModule(
|
||||
WebRtcAudioRecord audioInput, WebRtcAudioTrack audioOutput, long nativeAudioDeviceModule) {
|
||||
this.audioInput = audioInput;
|
||||
this.audioOutput = audioOutput;
|
||||
this.nativeAudioDeviceModule = nativeAudioDeviceModule;
|
||||
}
|
||||
|
||||
// Returns true if the device supports an audio effect (AEC or NS).
|
||||
// Four conditions must be fulfilled if functions are to return true:
|
||||
// 1) the platform must support the built-in (HW) effect,
|
||||
// 2) explicit use (override) of a WebRTC based version must not be set,
|
||||
// 3) the device must not be blacklisted for use of the effect, and
|
||||
// 4) the UUID of the effect must be approved (some UUIDs can be excluded).
|
||||
public static boolean isAcousticEchoCancelerSupported() {
|
||||
return WebRtcAudioEffects.canUseAcousticEchoCanceler();
|
||||
}
|
||||
public static boolean isNoiseSuppressorSupported() {
|
||||
return WebRtcAudioEffects.canUseNoiseSuppressor();
|
||||
@Override
|
||||
public long getNativeAudioDeviceModulePointer() {
|
||||
return nativeAudioDeviceModule;
|
||||
}
|
||||
|
||||
// Call this method if the default handling of querying the native sample
|
||||
// rate shall be overridden. Can be useful on some devices where the
|
||||
// available Android APIs are known to return invalid results.
|
||||
// TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression.
|
||||
public static void setDefaultSampleRateHz(int sampleRateHz) {
|
||||
WebRtcAudioUtils.setDefaultSampleRateHz(sampleRateHz);
|
||||
@Override
|
||||
public void release() {
|
||||
if (nativeAudioDeviceModule != 0) {
|
||||
JniCommon.nativeReleaseRef(nativeAudioDeviceModule);
|
||||
nativeAudioDeviceModule = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpeakerMute(boolean mute) {
|
||||
audioOutput.setSpeakerMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMicrophoneMute(boolean mute) {
|
||||
audioInput.setMicrophoneMute(mute);
|
||||
}
|
||||
|
||||
private static native long nativeCreateAudioDeviceModule(Context context,
|
||||
AudioManager audioManager, WebRtcAudioRecord audioInput, WebRtcAudioTrack audioOutput,
|
||||
int sampleRate, boolean useStereoInput, boolean useStereoOutput);
|
||||
}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2018 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.audio;
|
||||
|
||||
import org.webrtc.voiceengine.WebRtcAudioRecord;
|
||||
import org.webrtc.voiceengine.WebRtcAudioTrack;
|
||||
|
||||
/**
|
||||
* This class represents the legacy AudioDeviceModule that is currently hardcoded into C++ WebRTC.
|
||||
* It will return a null native AudioDeviceModule pointer, leading to an internal object being
|
||||
* created inside WebRTC that is controlled by static calls to the classes under the voiceengine
|
||||
* package. Please use the new JavaAudioDeviceModule instead of this class.
|
||||
*/
|
||||
@Deprecated
|
||||
public class LegacyAudioDeviceModule implements AudioDeviceModule {
|
||||
public static AudioDeviceModule Create() {
|
||||
return new LegacyAudioDeviceModule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeAudioDeviceModulePointer() {
|
||||
// Returning a null pointer will make WebRTC construct the built-in legacy AudioDeviceModule for
|
||||
// Android internally.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
// All control for this ADM goes through static global methods and the C++ object is owned
|
||||
// internally by WebRTC.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpeakerMute(boolean mute) {
|
||||
WebRtcAudioTrack.setSpeakerMute(mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMicrophoneMute(boolean mute) {
|
||||
WebRtcAudioRecord.setMicrophoneMute(mute);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user