1/* 2 * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import <AudioUnit/AudioUnit.h> 12#import <Foundation/Foundation.h> 13 14#import "objc_audio_device.h" 15#import "objc_audio_device_delegate.h" 16 17#include "api/make_ref_counted.h" 18#include "api/ref_counted_base.h" 19#include "rtc_base/checks.h" 20#include "rtc_base/logging.h" 21#include "rtc_base/thread.h" 22 23namespace { 24 25constexpr double kPreferredInputSampleRate = 48000.0; 26constexpr double kPreferredOutputSampleRate = 48000.0; 27 28// WebRTC processes audio in chunks of 10ms. Preferring 20ms audio chunks 29// is a compromize between performance and power consumption. 30constexpr NSTimeInterval kPeferredInputIOBufferDuration = 0.02; 31constexpr NSTimeInterval kPeferredOutputIOBufferDuration = 0.02; 32 33class AudioDeviceDelegateImpl final : public rtc::RefCountedNonVirtual<AudioDeviceDelegateImpl> { 34 public: 35 AudioDeviceDelegateImpl( 36 rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule> audio_device_module, 37 rtc::Thread* thread) 38 : audio_device_module_(audio_device_module), thread_(thread) { 39 RTC_DCHECK(audio_device_module_); 40 RTC_DCHECK(thread_); 41 } 42 43 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module() const { 44 return audio_device_module_.get(); 45 } 46 47 rtc::Thread* thread() const { return thread_; } 48 49 void reset_audio_device_module() { audio_device_module_ = nullptr; } 50 51 private: 52 rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule> audio_device_module_; 53 rtc::Thread* thread_; 54}; 55 56} // namespace 57 58@implementation ObjCAudioDeviceDelegate { 59 rtc::scoped_refptr<AudioDeviceDelegateImpl> impl_; 60} 61 62@synthesize getPlayoutData = getPlayoutData_; 63 64@synthesize deliverRecordedData = deliverRecordedData_; 65 66@synthesize preferredInputSampleRate = preferredInputSampleRate_; 67 68@synthesize preferredInputIOBufferDuration = preferredInputIOBufferDuration_; 69 70@synthesize preferredOutputSampleRate = preferredOutputSampleRate_; 71 72@synthesize preferredOutputIOBufferDuration = preferredOutputIOBufferDuration_; 73 74- (instancetype)initWithAudioDeviceModule: 75 (rtc::scoped_refptr<webrtc::objc_adm::ObjCAudioDeviceModule>)audioDeviceModule 76 audioDeviceThread:(rtc::Thread*)thread { 77 RTC_DCHECK_RUN_ON(thread); 78 if (self = [super init]) { 79 impl_ = rtc::make_ref_counted<AudioDeviceDelegateImpl>(audioDeviceModule, thread); 80 preferredInputSampleRate_ = kPreferredInputSampleRate; 81 preferredInputIOBufferDuration_ = kPeferredInputIOBufferDuration; 82 preferredOutputSampleRate_ = kPreferredOutputSampleRate; 83 preferredOutputIOBufferDuration_ = kPeferredOutputIOBufferDuration; 84 85 rtc::scoped_refptr<AudioDeviceDelegateImpl> playout_delegate = impl_; 86 getPlayoutData_ = ^OSStatus(AudioUnitRenderActionFlags* _Nonnull actionFlags, 87 const AudioTimeStamp* _Nonnull timestamp, 88 NSInteger inputBusNumber, 89 UInt32 frameCount, 90 AudioBufferList* _Nonnull outputData) { 91 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device = 92 playout_delegate->audio_device_module(); 93 if (audio_device) { 94 return audio_device->OnGetPlayoutData( 95 actionFlags, timestamp, inputBusNumber, frameCount, outputData); 96 } else { 97 *actionFlags |= kAudioUnitRenderAction_OutputIsSilence; 98 RTC_LOG(LS_VERBOSE) << "No alive audio device"; 99 return noErr; 100 } 101 }; 102 103 rtc::scoped_refptr<AudioDeviceDelegateImpl> record_delegate = impl_; 104 deliverRecordedData_ = 105 ^OSStatus(AudioUnitRenderActionFlags* _Nonnull actionFlags, 106 const AudioTimeStamp* _Nonnull timestamp, 107 NSInteger inputBusNumber, 108 UInt32 frameCount, 109 const AudioBufferList* _Nullable inputData, 110 void* renderContext, 111 RTC_OBJC_TYPE(RTCAudioDeviceRenderRecordedDataBlock) _Nullable renderBlock) { 112 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device = 113 record_delegate->audio_device_module(); 114 if (audio_device) { 115 return audio_device->OnDeliverRecordedData(actionFlags, 116 timestamp, 117 inputBusNumber, 118 frameCount, 119 inputData, 120 renderContext, 121 renderBlock); 122 } else { 123 RTC_LOG(LS_VERBOSE) << "No alive audio device"; 124 return noErr; 125 } 126 }; 127 } 128 return self; 129} 130 131- (void)notifyAudioInputParametersChange { 132 RTC_DCHECK_RUN_ON(impl_->thread()); 133 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); 134 if (audio_device_module) { 135 audio_device_module->HandleAudioInputParametersChange(); 136 } 137} 138 139- (void)notifyAudioOutputParametersChange { 140 RTC_DCHECK_RUN_ON(impl_->thread()); 141 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); 142 if (audio_device_module) { 143 audio_device_module->HandleAudioOutputParametersChange(); 144 } 145} 146 147- (void)notifyAudioInputInterrupted { 148 RTC_DCHECK_RUN_ON(impl_->thread()); 149 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); 150 if (audio_device_module) { 151 audio_device_module->HandleAudioInputInterrupted(); 152 } 153} 154 155- (void)notifyAudioOutputInterrupted { 156 RTC_DCHECK_RUN_ON(impl_->thread()); 157 webrtc::objc_adm::ObjCAudioDeviceModule* audio_device_module = impl_->audio_device_module(); 158 if (audio_device_module) { 159 audio_device_module->HandleAudioOutputInterrupted(); 160 } 161} 162 163- (void)dispatchAsync:(dispatch_block_t)block { 164 rtc::Thread* thread = impl_->thread(); 165 RTC_DCHECK(thread); 166 thread->PostTask([block] { 167 @autoreleasepool { 168 block(); 169 } 170 }); 171} 172 173- (void)dispatchSync:(dispatch_block_t)block { 174 rtc::Thread* thread = impl_->thread(); 175 RTC_DCHECK(thread); 176 if (thread->IsCurrent()) { 177 @autoreleasepool { 178 block(); 179 } 180 } else { 181 thread->BlockingCall([block] { 182 @autoreleasepool { 183 block(); 184 } 185 }); 186 } 187} 188 189- (void)resetAudioDeviceModule { 190 RTC_DCHECK_RUN_ON(impl_->thread()); 191 impl_->reset_audio_device_module(); 192} 193 194@end 195