int32_t SetLoudspeakerStatus(bool enable) int32_t GetLoudspeakerStatus(bool* enabled) const These APIs are only implemented on iOS and they do not belong in the native audio layer since the client can achieve the same functionality by using the shared audio session in sdk/objc/Framework/Headers/WebRTC/RTCAudioSession.h. It also gives the client a better flexibility in how the audio routing is done. Bug: webrtc:7306 Change-Id: I853e2f57e0f5ae0a0f9fc4729ce961d81f92588b Reviewed-on: https://webrtc-review.googlesource.com/23740 Commit-Queue: Henrik Andreassson <henrika@webrtc.org> Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20721}
296 lines
13 KiB
C++
296 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2012 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 MODULES_AUDIO_DEVICE_IOS_AUDIO_DEVICE_IOS_H_
|
|
#define MODULES_AUDIO_DEVICE_IOS_AUDIO_DEVICE_IOS_H_
|
|
|
|
#include <memory>
|
|
|
|
#include "sdk/objc/Framework/Headers/WebRTC/RTCMacros.h"
|
|
#include "modules/audio_device/audio_device_generic.h"
|
|
#include "modules/audio_device/ios/audio_session_observer.h"
|
|
#include "modules/audio_device/ios/voice_processing_audio_unit.h"
|
|
#include "rtc_base/buffer.h"
|
|
#include "rtc_base/gtest_prod_util.h"
|
|
#include "rtc_base/thread.h"
|
|
#include "rtc_base/thread_annotations.h"
|
|
#include "rtc_base/thread_checker.h"
|
|
|
|
RTC_FWD_DECL_OBJC_CLASS(RTCAudioSessionDelegateAdapter);
|
|
|
|
namespace webrtc {
|
|
|
|
class FineAudioBuffer;
|
|
|
|
// Implements full duplex 16-bit mono PCM audio support for iOS using a
|
|
// Voice-Processing (VP) I/O audio unit in Core Audio. The VP I/O audio unit
|
|
// supports audio echo cancellation. It also adds automatic gain control,
|
|
// adjustment of voice-processing quality and muting.
|
|
//
|
|
// An instance must be created and destroyed on one and the same thread.
|
|
// All supported public methods must also be called on the same thread.
|
|
// A thread checker will RTC_DCHECK if any supported method is called on an
|
|
// invalid thread.
|
|
//
|
|
// Recorded audio will be delivered on a real-time internal I/O thread in the
|
|
// audio unit. The audio unit will also ask for audio data to play out on this
|
|
// same thread.
|
|
class AudioDeviceIOS : public AudioDeviceGeneric,
|
|
public AudioSessionObserver,
|
|
public VoiceProcessingAudioUnitObserver,
|
|
public rtc::MessageHandler {
|
|
public:
|
|
AudioDeviceIOS();
|
|
~AudioDeviceIOS();
|
|
|
|
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override;
|
|
|
|
InitStatus Init() override;
|
|
int32_t Terminate() override;
|
|
bool Initialized() const override;
|
|
|
|
int32_t InitPlayout() override;
|
|
bool PlayoutIsInitialized() const override;
|
|
|
|
int32_t InitRecording() override;
|
|
bool RecordingIsInitialized() const override;
|
|
|
|
int32_t StartPlayout() override;
|
|
int32_t StopPlayout() override;
|
|
bool Playing() const override { return playing_; }
|
|
|
|
int32_t StartRecording() override;
|
|
int32_t StopRecording() override;
|
|
bool Recording() const override { return recording_; }
|
|
|
|
// These methods returns hard-coded delay values and not dynamic delay
|
|
// estimates. The reason is that iOS supports a built-in AEC and the WebRTC
|
|
// AEC will always be disabled in the Libjingle layer to avoid running two
|
|
// AEC implementations at the same time. And, it saves resources to avoid
|
|
// updating these delay values continuously.
|
|
// TODO(henrika): it would be possible to mark these two methods as not
|
|
// implemented since they are only called for A/V-sync purposes today and
|
|
// A/V-sync is not supported on iOS. However, we avoid adding error messages
|
|
// the log by using these dummy implementations instead.
|
|
int32_t PlayoutDelay(uint16_t& delayMS) const override;
|
|
|
|
// Native audio parameters stored during construction.
|
|
// These methods are unique for the iOS implementation.
|
|
int GetPlayoutAudioParameters(AudioParameters* params) const override;
|
|
int GetRecordAudioParameters(AudioParameters* params) const override;
|
|
|
|
// These methods are currently not fully implemented on iOS:
|
|
|
|
// See audio_device_not_implemented.cc for trivial implementations.
|
|
int32_t ActiveAudioLayer(
|
|
AudioDeviceModule::AudioLayer& audioLayer) const override;
|
|
int32_t PlayoutIsAvailable(bool& available) override;
|
|
int32_t RecordingIsAvailable(bool& available) override;
|
|
int32_t SetAGC(bool enable) override;
|
|
bool AGC() const override;
|
|
int16_t PlayoutDevices() override;
|
|
int16_t RecordingDevices() override;
|
|
int32_t PlayoutDeviceName(uint16_t index,
|
|
char name[kAdmMaxDeviceNameSize],
|
|
char guid[kAdmMaxGuidSize]) override;
|
|
int32_t RecordingDeviceName(uint16_t index,
|
|
char name[kAdmMaxDeviceNameSize],
|
|
char guid[kAdmMaxGuidSize]) override;
|
|
int32_t SetPlayoutDevice(uint16_t index) override;
|
|
int32_t SetPlayoutDevice(
|
|
AudioDeviceModule::WindowsDeviceType device) override;
|
|
int32_t SetRecordingDevice(uint16_t index) override;
|
|
int32_t SetRecordingDevice(
|
|
AudioDeviceModule::WindowsDeviceType device) override;
|
|
int32_t InitSpeaker() override;
|
|
bool SpeakerIsInitialized() const override;
|
|
int32_t InitMicrophone() override;
|
|
bool MicrophoneIsInitialized() const override;
|
|
int32_t SpeakerVolumeIsAvailable(bool& available) override;
|
|
int32_t SetSpeakerVolume(uint32_t volume) override;
|
|
int32_t SpeakerVolume(uint32_t& volume) const override;
|
|
int32_t MaxSpeakerVolume(uint32_t& maxVolume) const override;
|
|
int32_t MinSpeakerVolume(uint32_t& minVolume) const override;
|
|
int32_t MicrophoneVolumeIsAvailable(bool& available) override;
|
|
int32_t SetMicrophoneVolume(uint32_t volume) override;
|
|
int32_t MicrophoneVolume(uint32_t& volume) const override;
|
|
int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const override;
|
|
int32_t MinMicrophoneVolume(uint32_t& minVolume) const override;
|
|
int32_t MicrophoneMuteIsAvailable(bool& available) override;
|
|
int32_t SetMicrophoneMute(bool enable) override;
|
|
int32_t MicrophoneMute(bool& enabled) const override;
|
|
int32_t SpeakerMuteIsAvailable(bool& available) override;
|
|
int32_t SetSpeakerMute(bool enable) override;
|
|
int32_t SpeakerMute(bool& enabled) const override;
|
|
int32_t StereoPlayoutIsAvailable(bool& available) override;
|
|
int32_t SetStereoPlayout(bool enable) override;
|
|
int32_t StereoPlayout(bool& enabled) const override;
|
|
int32_t StereoRecordingIsAvailable(bool& available) override;
|
|
int32_t SetStereoRecording(bool enable) override;
|
|
int32_t StereoRecording(bool& enabled) const override;
|
|
|
|
// AudioSessionObserver methods. May be called from any thread.
|
|
void OnInterruptionBegin() override;
|
|
void OnInterruptionEnd() override;
|
|
void OnValidRouteChange() override;
|
|
void OnCanPlayOrRecordChange(bool can_play_or_record) override;
|
|
void OnChangedOutputVolume() override;
|
|
|
|
// VoiceProcessingAudioUnitObserver methods.
|
|
OSStatus OnDeliverRecordedData(AudioUnitRenderActionFlags* flags,
|
|
const AudioTimeStamp* time_stamp,
|
|
UInt32 bus_number,
|
|
UInt32 num_frames,
|
|
AudioBufferList* io_data) override;
|
|
OSStatus OnGetPlayoutData(AudioUnitRenderActionFlags* flags,
|
|
const AudioTimeStamp* time_stamp,
|
|
UInt32 bus_number,
|
|
UInt32 num_frames,
|
|
AudioBufferList* io_data) override;
|
|
|
|
// Handles messages from posts.
|
|
void OnMessage(rtc::Message *msg) override;
|
|
|
|
private:
|
|
// Called by the relevant AudioSessionObserver methods on |thread_|.
|
|
void HandleInterruptionBegin();
|
|
void HandleInterruptionEnd();
|
|
void HandleValidRouteChange();
|
|
void HandleCanPlayOrRecordChange(bool can_play_or_record);
|
|
void HandleSampleRateChange(float sample_rate);
|
|
void HandlePlayoutGlitchDetected();
|
|
void HandleOutputVolumeChange();
|
|
|
|
// Uses current |playout_parameters_| and |record_parameters_| to inform the
|
|
// audio device buffer (ADB) about our internal audio parameters.
|
|
void UpdateAudioDeviceBuffer();
|
|
|
|
// Since the preferred audio parameters are only hints to the OS, the actual
|
|
// values may be different once the AVAudioSession has been activated.
|
|
// This method asks for the current hardware parameters and takes actions
|
|
// if they should differ from what we have asked for initially. It also
|
|
// defines |playout_parameters_| and |record_parameters_|.
|
|
void SetupAudioBuffersForActiveAudioSession();
|
|
|
|
// Creates the audio unit.
|
|
bool CreateAudioUnit();
|
|
|
|
// Updates the audio unit state based on current state.
|
|
void UpdateAudioUnit(bool can_play_or_record);
|
|
|
|
// Configures the audio session for WebRTC.
|
|
bool ConfigureAudioSession();
|
|
// Unconfigures the audio session.
|
|
void UnconfigureAudioSession();
|
|
|
|
// Activates our audio session, creates and initializes the voice-processing
|
|
// audio unit and verifies that we got the preferred native audio parameters.
|
|
bool InitPlayOrRecord();
|
|
|
|
// Closes and deletes the voice-processing I/O unit.
|
|
void ShutdownPlayOrRecord();
|
|
|
|
// Ensures that methods are called from the same thread as this object is
|
|
// created on.
|
|
rtc::ThreadChecker thread_checker_;
|
|
|
|
// Native I/O audio thread checker.
|
|
rtc::ThreadChecker io_thread_checker_;
|
|
|
|
// Thread that this object is created on.
|
|
rtc::Thread* thread_;
|
|
|
|
// Raw pointer handle provided to us in AttachAudioBuffer(). Owned by the
|
|
// AudioDeviceModuleImpl class and called by AudioDeviceModule::Create().
|
|
// The AudioDeviceBuffer is a member of the AudioDeviceModuleImpl instance
|
|
// and therefore outlives this object.
|
|
AudioDeviceBuffer* audio_device_buffer_;
|
|
|
|
// Contains audio parameters (sample rate, #channels, buffer size etc.) for
|
|
// the playout and recording sides. These structure is set in two steps:
|
|
// first, native sample rate and #channels are defined in Init(). Next, the
|
|
// audio session is activated and we verify that the preferred parameters
|
|
// were granted by the OS. At this stage it is also possible to add a third
|
|
// component to the parameters; the native I/O buffer duration.
|
|
// A RTC_CHECK will be hit if we for some reason fail to open an audio session
|
|
// using the specified parameters.
|
|
AudioParameters playout_parameters_;
|
|
AudioParameters record_parameters_;
|
|
|
|
// The AudioUnit used to play and record audio.
|
|
std::unique_ptr<VoiceProcessingAudioUnit> audio_unit_;
|
|
|
|
// FineAudioBuffer takes an AudioDeviceBuffer which delivers audio data
|
|
// in chunks of 10ms. It then allows for this data to be pulled in
|
|
// a finer or coarser granularity. I.e. interacting with this class instead
|
|
// of directly with the AudioDeviceBuffer one can ask for any number of
|
|
// audio data samples. Is also supports a similar scheme for the recording
|
|
// side.
|
|
// Example: native buffer size can be 128 audio frames at 16kHz sample rate.
|
|
// WebRTC will provide 480 audio frames per 10ms but iOS asks for 128
|
|
// in each callback (one every 8ms). This class can then ask for 128 and the
|
|
// FineAudioBuffer will ask WebRTC for new data only when needed and also
|
|
// cache non-utilized audio between callbacks. On the recording side, iOS
|
|
// can provide audio data frames of size 128 and these are accumulated until
|
|
// enough data to supply one 10ms call exists. This 10ms chunk is then sent
|
|
// to WebRTC and the remaining part is stored.
|
|
std::unique_ptr<FineAudioBuffer> fine_audio_buffer_;
|
|
|
|
// Temporary storage for recorded data. AudioUnitRender() renders into this
|
|
// array as soon as a frame of the desired buffer size has been recorded.
|
|
// On real iOS devices, the size will be fixed and set once. For iOS
|
|
// simulators, the size can vary from callback to callback and the size
|
|
// will be changed dynamically to account for this behavior.
|
|
rtc::BufferT<int8_t> record_audio_buffer_;
|
|
|
|
// Set to 1 when recording is active and 0 otherwise.
|
|
volatile int recording_;
|
|
|
|
// Set to 1 when playout is active and 0 otherwise.
|
|
volatile int playing_;
|
|
|
|
// Set to true after successful call to Init(), false otherwise.
|
|
bool initialized_ RTC_ACCESS_ON(thread_checker_);
|
|
|
|
// Set to true after successful call to InitRecording() or InitPlayout(),
|
|
// false otherwise.
|
|
bool audio_is_initialized_;
|
|
|
|
// Set to true if audio session is interrupted, false otherwise.
|
|
bool is_interrupted_;
|
|
|
|
// Audio interruption observer instance.
|
|
RTCAudioSessionDelegateAdapter* audio_session_observer_
|
|
RTC_ACCESS_ON(thread_checker_);
|
|
|
|
// Set to true if we've activated the audio session.
|
|
bool has_configured_session_ RTC_ACCESS_ON(thread_checker_);
|
|
|
|
// Counts number of detected audio glitches on the playout side.
|
|
int64_t num_detected_playout_glitches_ RTC_ACCESS_ON(thread_checker_);
|
|
int64_t last_playout_time_ RTC_ACCESS_ON(io_thread_checker_);
|
|
|
|
// Counts number of playout callbacks per call.
|
|
// The value isupdated on the native I/O thread and later read on the
|
|
// creating thread (see thread_checker_) but at this stage no audio is
|
|
// active. Hence, it is a "thread safe" design and no lock is needed.
|
|
int64_t num_playout_callbacks_;
|
|
|
|
// Contains the time for when the last output volume change was detected.
|
|
int64_t last_output_volume_change_time_ RTC_ACCESS_ON(thread_checker_);
|
|
|
|
// Exposes private members for testing purposes only.
|
|
FRIEND_TEST_ALL_PREFIXES(AudioDeviceTest, testInterruptedAudioSession);
|
|
};
|
|
|
|
} // namespace webrtc
|
|
|
|
#endif // MODULES_AUDIO_DEVICE_IOS_AUDIO_DEVICE_IOS_H_
|