Skip to content
This repository was archived by the owner on Nov 6, 2025. It is now read-only.

Commit 6fcc170

Browse files
committed
Allow to use audio units other than Voice-Processing I/O unit
1 parent 79c60ca commit 6fcc170

File tree

10 files changed

+186
-53
lines changed

10 files changed

+186
-53
lines changed

README.pixiv.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ This is a fork of WebRTC made by [pixiv Inc](https://www.pixiv.co.jp/).
1313
to retrieve the last frame at an arbitrary time is introduced.
1414
- APIs necessary to deliver recorded audio data on iOS's broadcast extension is
1515
added.
16+
- An audio unit component other than the Voice-Processing I/O unit can be used
17+
to record audio on iOS.
1618

1719
# Delivering audio data on iOS's broadcast extension
1820

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2019 pixiv Inc. All Rights Reserved.
3+
*
4+
* Use of this source code is governed by a license that can be
5+
* found in the LICENSE.pixiv file in the root of the source tree.
6+
*/
7+
8+
#import "RTCAudioDeviceModule.h"
9+
10+
#if defined(WEBRTC_IOS)
11+
#include "sdk/objc/native/src/audio/audio_device_module_ios.h"
12+
13+
NS_ASSUME_NONNULL_BEGIN
14+
15+
@interface RTCAudioDeviceModule ()
16+
17+
@property(nonatomic, readonly) rtc::scoped_refptr<webrtc::ios_adm::AudioDeviceModuleIOS>
18+
nativeModule;
19+
20+
@end
21+
22+
NS_ASSUME_NONNULL_END
23+
#endif
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2019 pixiv Inc. All Rights Reserved.
3+
*
4+
* Use of this source code is governed by a license that can be
5+
* found in the LICENSE.pixiv file in the root of the source tree.
6+
*/
7+
8+
#import <CoreMedia/CoreMedia.h>
9+
#import <Foundation/Foundation.h>
10+
11+
#import "RTCMacros.h"
12+
13+
NS_ASSUME_NONNULL_BEGIN
14+
15+
RTC_OBJC_EXPORT
16+
17+
@interface RTCAudioDeviceModule : NSObject
18+
19+
- (void)deliverRecordedData:(CMSampleBufferRef)sampleBuffer;
20+
@property(nonatomic, assign) OSType audioUnitSubType;
21+
22+
@end
23+
24+
NS_ASSUME_NONNULL_END
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2019 pixiv Inc. All Rights Reserved.
3+
*
4+
* Use of this source code is governed by a license that can be
5+
* found in the LICENSE.pixiv file in the root of the source tree.
6+
*/
7+
8+
#include <AudioUnit/AudioUnit.h>
9+
10+
#import "RTCAudioDeviceModule+Private.h"
11+
#include "rtc_base/ref_counted_object.h"
12+
13+
@implementation RTCAudioDeviceModule {
14+
#if defined(WEBRTC_IOS)
15+
rtc::scoped_refptr<webrtc::ios_adm::AudioDeviceModuleIOS> _nativeModule;
16+
#endif
17+
}
18+
19+
- (instancetype)init {
20+
#if defined(WEBRTC_IOS)
21+
self = [super init];
22+
_nativeModule = new rtc::RefCountedObject<webrtc::ios_adm::AudioDeviceModuleIOS>();
23+
return self;
24+
#else
25+
return nullptr;
26+
#endif
27+
}
28+
29+
- (void)deliverRecordedData:(CMSampleBufferRef)sampleBuffer {
30+
#if defined(WEBRTC_IOS)
31+
_nativeModule->OnDeliverRecordedExternalData(sampleBuffer);
32+
#endif
33+
}
34+
35+
- (void)setAudioUnitSubType:(OSType)audioUnitSubType {
36+
#if defined(WEBRTC_IOS)
37+
_nativeModule->SetAudioUnitSubType(audioUnitSubType);
38+
#endif
39+
}
40+
41+
- (OSType)audioUnitSubType {
42+
#if defined(WEBRTC_IOS)
43+
return _nativeModule->GetAudioUnitSubType();
44+
#else
45+
return kAudioUnitSubType_VoiceProcessingIO;
46+
#endif
47+
}
48+
49+
#pragma mark - Private
50+
51+
#if defined(WEBRTC_IOS)
52+
- (rtc::scoped_refptr<webrtc::ios_adm::AudioDeviceModuleIOS>)nativeModule {
53+
return _nativeModule;
54+
}
55+
#endif
56+
57+
@end

sdk/objc/native/src/audio/audio_device_ios.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ class AudioDeviceIOS : public AudioDeviceGeneric,
162162

163163
bool IsInterrupted();
164164

165+
OSType audio_unit_sub_type;
166+
165167
private:
166168
// Called by the relevant AudioSessionObserver methods on |thread_|.
167169
void HandleInterruptionBegin();

sdk/objc/native/src/audio/audio_device_ios.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ static void LogDeviceInfo() {
781781
RTC_DCHECK(!audio_unit_);
782782

783783
audio_unit_.reset(new VoiceProcessingAudioUnit(this));
784-
if (!audio_unit_->Init()) {
784+
if (!audio_unit_->Init(audio_unit_sub_type)) {
785785
audio_unit_.reset();
786786
return false;
787787
}

sdk/objc/native/src/audio/audio_device_module_ios.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class AudioDeviceModuleIOS : public AudioDeviceModule {
133133

134134
#if defined(WEBRTC_IOS)
135135
void OnDeliverRecordedExternalData(CMSampleBufferRef sample_buffer);
136+
OSType GetAudioUnitSubType() const;
137+
void SetAudioUnitSubType(OSType sub_type);
136138
int GetPlayoutAudioParameters(AudioParameters* params) const override;
137139
int GetRecordAudioParameters(AudioParameters* params) const override;
138140
#endif // WEBRTC_IOS
@@ -141,6 +143,10 @@ class AudioDeviceModuleIOS : public AudioDeviceModule {
141143
const std::unique_ptr<TaskQueueFactory> task_queue_factory_;
142144
std::unique_ptr<AudioDeviceIOS> audio_device_;
143145
std::unique_ptr<AudioDeviceBuffer> audio_device_buffer_;
146+
147+
#if defined(WEBRTC_IOS)
148+
OSType audio_unit_sub_type_ = kAudioUnitSubType_VoiceProcessingIO;
149+
#endif // WEBRTC_IOS
144150
};
145151
} // namespace ios_adm
146152
} // namespace webrtc

sdk/objc/native/src/audio/audio_device_module_ios.mm

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
audio_device_.reset(new ios_adm::AudioDeviceIOS());
7676
RTC_CHECK(audio_device_);
7777

78+
audio_device_->audio_unit_sub_type = audio_unit_sub_type_;
79+
7880
this->AttachAudioBuffer();
7981

8082
AudioDeviceGeneric::InitStatus status = audio_device_->Init();
@@ -655,6 +657,18 @@
655657
audio_device_->OnDeliverRecordedExternalData(sample_buffer);
656658
}
657659

660+
OSType AudioDeviceModuleIOS::GetAudioUnitSubType() const {
661+
return audio_unit_sub_type_;
662+
}
663+
664+
void AudioDeviceModuleIOS::SetAudioUnitSubType(OSType sub_type) {
665+
audio_unit_sub_type_ = sub_type;
666+
667+
if (audio_device_) {
668+
audio_device_->audio_unit_sub_type = sub_type;
669+
}
670+
}
671+
658672
int AudioDeviceModuleIOS::GetPlayoutAudioParameters(
659673
AudioParameters* params) const {
660674
RTC_LOG(INFO) << __FUNCTION__;

sdk/objc/native/src/audio/voice_processing_audio_unit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class VoiceProcessingAudioUnit {
6969
// audio. The selected stream format is selected to avoid internal resampling
7070
// and to match the 10ms callback rate for WebRTC as well as possible.
7171
// Does not intialize the audio unit.
72-
bool Init();
72+
bool Init(OSType audio_unit_sub_type);
7373

7474
VoiceProcessingAudioUnit::State GetState() const;
7575

@@ -132,6 +132,7 @@ class VoiceProcessingAudioUnit {
132132
VoiceProcessingAudioUnitObserver* observer_;
133133
AudioUnit vpio_unit_;
134134
VoiceProcessingAudioUnit::State state_;
135+
OSType audio_unit_sub_type_;
135136
};
136137
} // namespace ios_adm
137138
} // namespace webrtc

sdk/objc/native/src/audio/voice_processing_audio_unit.mm

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,16 @@ static OSStatus GetAGCState(AudioUnit audio_unit, UInt32* enabled) {
8484

8585
const UInt32 VoiceProcessingAudioUnit::kBytesPerSample = 2;
8686

87-
bool VoiceProcessingAudioUnit::Init() {
87+
bool VoiceProcessingAudioUnit::Init(OSType audio_unit_sub_type) {
8888
RTC_DCHECK_EQ(state_, kInitRequired);
8989

90+
audio_unit_sub_type_ = audio_unit_sub_type;
91+
9092
// Create an audio component description to identify the Voice Processing
9193
// I/O audio unit.
9294
AudioComponentDescription vpio_unit_description;
9395
vpio_unit_description.componentType = kAudioUnitType_Output;
94-
vpio_unit_description.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
96+
vpio_unit_description.componentSubType = audio_unit_sub_type;
9597
vpio_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple;
9698
vpio_unit_description.componentFlags = 0;
9799
vpio_unit_description.componentFlagsMask = 0;
@@ -250,62 +252,64 @@ static OSStatus GetAGCState(AudioUnit audio_unit, UInt32* enabled) {
250252
RTCLog(@"Voice Processing I/O unit is now initialized.");
251253
}
252254

253-
// AGC should be enabled by default for Voice Processing I/O units but it is
254-
// checked below and enabled explicitly if needed. This scheme is used
255-
// to be absolutely sure that the AGC is enabled since we have seen cases
256-
// where only zeros are recorded and a disabled AGC could be one of the
257-
// reasons why it happens.
258-
int agc_was_enabled_by_default = 0;
259-
UInt32 agc_is_enabled = 0;
260-
result = GetAGCState(vpio_unit_, &agc_is_enabled);
261-
if (result != noErr) {
262-
RTCLogError(@"Failed to get AGC state (1st attempt). "
263-
"Error=%ld.",
264-
(long)result);
265-
// Example of error code: kAudioUnitErr_NoConnection (-10876).
266-
// All error codes related to audio units are negative and are therefore
267-
// converted into a postive value to match the UMA APIs.
268-
RTC_HISTOGRAM_COUNTS_SPARSE_100000(
269-
"WebRTC.Audio.GetAGCStateErrorCode1", (-1) * result);
270-
} else if (agc_is_enabled) {
271-
// Remember that the AGC was enabled by default. Will be used in UMA.
272-
agc_was_enabled_by_default = 1;
273-
} else {
274-
// AGC was initially disabled => try to enable it explicitly.
275-
UInt32 enable_agc = 1;
276-
result =
277-
AudioUnitSetProperty(vpio_unit_,
278-
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
279-
kAudioUnitScope_Global, kInputBus, &enable_agc,
280-
sizeof(enable_agc));
281-
if (result != noErr) {
282-
RTCLogError(@"Failed to enable the built-in AGC. "
283-
"Error=%ld.",
284-
(long)result);
285-
RTC_HISTOGRAM_COUNTS_SPARSE_100000(
286-
"WebRTC.Audio.SetAGCStateErrorCode", (-1) * result);
287-
}
255+
if (audio_unit_sub_type_ == kAudioUnitSubType_VoiceProcessingIO) {
256+
// AGC should be enabled by default for Voice Processing I/O units but it is
257+
// checked below and enabled explicitly if needed. This scheme is used
258+
// to be absolutely sure that the AGC is enabled since we have seen cases
259+
// where only zeros are recorded and a disabled AGC could be one of the
260+
// reasons why it happens.
261+
int agc_was_enabled_by_default = 0;
262+
UInt32 agc_is_enabled = 0;
288263
result = GetAGCState(vpio_unit_, &agc_is_enabled);
289264
if (result != noErr) {
290-
RTCLogError(@"Failed to get AGC state (2nd attempt). "
265+
RTCLogError(@"Failed to get AGC state (1st attempt). "
291266
"Error=%ld.",
292267
(long)result);
268+
// Example of error code: kAudioUnitErr_NoConnection (-10876).
269+
// All error codes related to audio units are negative and are therefore
270+
// converted into a postive value to match the UMA APIs.
293271
RTC_HISTOGRAM_COUNTS_SPARSE_100000(
294-
"WebRTC.Audio.GetAGCStateErrorCode2", (-1) * result);
272+
"WebRTC.Audio.GetAGCStateErrorCode1", (-1) * result);
273+
} else if (agc_is_enabled) {
274+
// Remember that the AGC was enabled by default. Will be used in UMA.
275+
agc_was_enabled_by_default = 1;
276+
} else {
277+
// AGC was initially disabled => try to enable it explicitly.
278+
UInt32 enable_agc = 1;
279+
result =
280+
AudioUnitSetProperty(vpio_unit_,
281+
kAUVoiceIOProperty_VoiceProcessingEnableAGC,
282+
kAudioUnitScope_Global, kInputBus, &enable_agc,
283+
sizeof(enable_agc));
284+
if (result != noErr) {
285+
RTCLogError(@"Failed to enable the built-in AGC. "
286+
"Error=%ld.",
287+
(long)result);
288+
RTC_HISTOGRAM_COUNTS_SPARSE_100000(
289+
"WebRTC.Audio.SetAGCStateErrorCode", (-1) * result);
290+
}
291+
result = GetAGCState(vpio_unit_, &agc_is_enabled);
292+
if (result != noErr) {
293+
RTCLogError(@"Failed to get AGC state (2nd attempt). "
294+
"Error=%ld.",
295+
(long)result);
296+
RTC_HISTOGRAM_COUNTS_SPARSE_100000(
297+
"WebRTC.Audio.GetAGCStateErrorCode2", (-1) * result);
298+
}
295299
}
296-
}
297300

298-
// Track if the built-in AGC was enabled by default (as it should) or not.
299-
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.BuiltInAGCWasEnabledByDefault",
300-
agc_was_enabled_by_default);
301-
RTCLog(@"WebRTC.Audio.BuiltInAGCWasEnabledByDefault: %d",
302-
agc_was_enabled_by_default);
303-
// As a final step, add an UMA histogram for tracking the AGC state.
304-
// At this stage, the AGC should be enabled, and if it is not, more work is
305-
// needed to find out the root cause.
306-
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.BuiltInAGCIsEnabled", agc_is_enabled);
307-
RTCLog(@"WebRTC.Audio.BuiltInAGCIsEnabled: %u",
308-
static_cast<unsigned int>(agc_is_enabled));
301+
// Track if the built-in AGC was enabled by default (as it should) or not.
302+
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.BuiltInAGCWasEnabledByDefault",
303+
agc_was_enabled_by_default);
304+
RTCLog(@"WebRTC.Audio.BuiltInAGCWasEnabledByDefault: %d",
305+
agc_was_enabled_by_default);
306+
// As a final step, add an UMA histogram for tracking the AGC state.
307+
// At this stage, the AGC should be enabled, and if it is not, more work is
308+
// needed to find out the root cause.
309+
RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.BuiltInAGCIsEnabled", agc_is_enabled);
310+
RTCLog(@"WebRTC.Audio.BuiltInAGCIsEnabled: %u",
311+
static_cast<unsigned int>(agc_is_enabled));
312+
}
309313

310314
state_ = kInitialized;
311315
return true;

0 commit comments

Comments
 (0)