Skip to content

Commit 0d47901

Browse files
hiroshihoriesanthoshvai
authored andcommitted
Expose audio engine APIs (livekit#62)
1 parent c40fee0 commit 0d47901

6 files changed

Lines changed: 586 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#import "WebRTCModule.h"
2+
3+
@interface WebRTCModule (RTCAudioDeviceModule)
4+
5+
@end
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#import <objc/runtime.h>
2+
3+
#import <React/RCTBridge.h>
4+
#import <React/RCTBridgeModule.h>
5+
6+
#import "AudioDeviceModuleObserver.h"
7+
#import "WebRTCModule.h"
8+
9+
// The underlying `RTCAudioDeviceModule` is owned by the `RTCPeerConnectionFactory`.
10+
// `WebRTCModule.audioDeviceModule` is a Swift wrapper around it, so we reach for the
11+
// raw device module here when we need to call APIs that are only defined on
12+
// `RTCAudioDeviceModule`.
13+
#define RAW_ADM (self.peerConnectionFactory.audioDeviceModule)
14+
15+
@implementation WebRTCModule (RTCAudioDeviceModule)
16+
17+
#pragma mark - Recording & Playback Control
18+
19+
RCT_EXPORT_METHOD(audioDeviceModuleStartPlayout
20+
: (RCTPromiseResolveBlock)resolve rejecter
21+
: (RCTPromiseRejectBlock)reject) {
22+
NSInteger result = [RAW_ADM startPlayout];
23+
if (result == 0) {
24+
resolve(nil);
25+
} else {
26+
reject(@"playout_error", [NSString stringWithFormat:@"Failed to start playout: %ld", (long)result], nil);
27+
}
28+
}
29+
30+
RCT_EXPORT_METHOD(audioDeviceModuleStopPlayout
31+
: (RCTPromiseResolveBlock)resolve rejecter
32+
: (RCTPromiseRejectBlock)reject) {
33+
NSInteger result = [RAW_ADM stopPlayout];
34+
if (result == 0) {
35+
resolve(nil);
36+
} else {
37+
reject(@"playout_error", [NSString stringWithFormat:@"Failed to stop playout: %ld", (long)result], nil);
38+
}
39+
}
40+
41+
RCT_EXPORT_METHOD(audioDeviceModuleStartRecording
42+
: (RCTPromiseResolveBlock)resolve rejecter
43+
: (RCTPromiseRejectBlock)reject) {
44+
NSInteger result = [RAW_ADM startRecording];
45+
if (result == 0) {
46+
resolve(nil);
47+
} else {
48+
reject(@"recording_error", [NSString stringWithFormat:@"Failed to start recording: %ld", (long)result], nil);
49+
}
50+
}
51+
52+
RCT_EXPORT_METHOD(audioDeviceModuleStopRecording
53+
: (RCTPromiseResolveBlock)resolve rejecter
54+
: (RCTPromiseRejectBlock)reject) {
55+
NSInteger result = [RAW_ADM stopRecording];
56+
if (result == 0) {
57+
resolve(nil);
58+
} else {
59+
reject(@"recording_error", [NSString stringWithFormat:@"Failed to stop recording: %ld", (long)result], nil);
60+
}
61+
}
62+
63+
RCT_EXPORT_METHOD(audioDeviceModuleStartLocalRecording
64+
: (RCTPromiseResolveBlock)resolve rejecter
65+
: (RCTPromiseRejectBlock)reject) {
66+
NSInteger result = [RAW_ADM initAndStartRecording];
67+
if (result == 0) {
68+
resolve(nil);
69+
} else {
70+
reject(
71+
@"recording_error", [NSString stringWithFormat:@"Failed to start local recording: %ld", (long)result], nil);
72+
}
73+
}
74+
75+
RCT_EXPORT_METHOD(audioDeviceModuleStopLocalRecording
76+
: (RCTPromiseResolveBlock)resolve rejecter
77+
: (RCTPromiseRejectBlock)reject) {
78+
NSInteger result = [RAW_ADM stopRecording];
79+
if (result == 0) {
80+
resolve(nil);
81+
} else {
82+
reject(
83+
@"recording_error", [NSString stringWithFormat:@"Failed to stop local recording: %ld", (long)result], nil);
84+
}
85+
}
86+
87+
#pragma mark - Microphone Control
88+
89+
RCT_EXPORT_METHOD(audioDeviceModuleSetMicrophoneMuted
90+
: (BOOL)muted resolver
91+
: (RCTPromiseResolveBlock)resolve rejecter
92+
: (RCTPromiseRejectBlock)reject) {
93+
NSInteger result = [RAW_ADM setMicrophoneMuted:muted];
94+
if (result == 0) {
95+
resolve(nil);
96+
} else {
97+
reject(@"mute_error", [NSString stringWithFormat:@"Failed to set microphone mute: %ld", (long)result], nil);
98+
}
99+
}
100+
101+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsMicrophoneMuted) {
102+
return @(RAW_ADM.isMicrophoneMuted);
103+
}
104+
105+
#pragma mark - Voice Processing
106+
107+
RCT_EXPORT_METHOD(audioDeviceModuleSetVoiceProcessingEnabled
108+
: (BOOL)enabled resolver
109+
: (RCTPromiseResolveBlock)resolve rejecter
110+
: (RCTPromiseRejectBlock)reject) {
111+
NSInteger result = [RAW_ADM setVoiceProcessingEnabled:enabled];
112+
if (result == 0) {
113+
resolve(nil);
114+
} else {
115+
reject(@"voice_processing_error",
116+
[NSString stringWithFormat:@"Failed to set voice processing: %ld", (long)result],
117+
nil);
118+
}
119+
}
120+
121+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsVoiceProcessingEnabled) {
122+
return @(RAW_ADM.isVoiceProcessingEnabled);
123+
}
124+
125+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleSetVoiceProcessingBypassed : (BOOL)bypassed) {
126+
RAW_ADM.voiceProcessingBypassed = bypassed;
127+
return nil;
128+
}
129+
130+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsVoiceProcessingBypassed) {
131+
return @(RAW_ADM.isVoiceProcessingBypassed);
132+
}
133+
134+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleSetVoiceProcessingAGCEnabled : (BOOL)enabled) {
135+
RAW_ADM.voiceProcessingAGCEnabled = enabled;
136+
return nil;
137+
}
138+
139+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsVoiceProcessingAGCEnabled) {
140+
return @(RAW_ADM.isVoiceProcessingAGCEnabled);
141+
}
142+
143+
#pragma mark - Status
144+
145+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsPlaying) {
146+
return @(RAW_ADM.isPlaying);
147+
}
148+
149+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsRecording) {
150+
return @(RAW_ADM.isRecording);
151+
}
152+
153+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsEngineRunning) {
154+
return @(RAW_ADM.isEngineRunning);
155+
}
156+
157+
#pragma mark - Advanced Features
158+
159+
RCT_EXPORT_METHOD(audioDeviceModuleSetMuteMode
160+
: (NSInteger)mode resolver
161+
: (RCTPromiseResolveBlock)resolve rejecter
162+
: (RCTPromiseRejectBlock)reject) {
163+
NSInteger result = [RAW_ADM setMuteMode:(RTCAudioEngineMuteMode)mode];
164+
if (result == 0) {
165+
resolve(nil);
166+
} else {
167+
reject(@"mute_mode_error", [NSString stringWithFormat:@"Failed to set mute mode: %ld", (long)result], nil);
168+
}
169+
}
170+
171+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleGetMuteMode) {
172+
return @(RAW_ADM.muteMode);
173+
}
174+
175+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleSetAdvancedDuckingEnabled : (BOOL)enabled) {
176+
RAW_ADM.advancedDuckingEnabled = enabled;
177+
return nil;
178+
}
179+
180+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsAdvancedDuckingEnabled) {
181+
return @(RAW_ADM.isAdvancedDuckingEnabled);
182+
}
183+
184+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleSetDuckingLevel : (NSInteger)level) {
185+
RAW_ADM.duckingLevel = level;
186+
return nil;
187+
}
188+
189+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleGetDuckingLevel) {
190+
return @(RAW_ADM.duckingLevel);
191+
}
192+
193+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleIsRecordingAlwaysPreparedMode) {
194+
return @(RAW_ADM.recordingAlwaysPreparedMode);
195+
}
196+
197+
RCT_EXPORT_METHOD(audioDeviceModuleSetRecordingAlwaysPreparedMode
198+
: (BOOL)enabled resolver
199+
: (RCTPromiseResolveBlock)resolve rejecter
200+
: (RCTPromiseRejectBlock)reject) {
201+
NSInteger result = [RAW_ADM setRecordingAlwaysPreparedMode:enabled];
202+
if (result == 0) {
203+
resolve(nil);
204+
} else {
205+
reject(@"recording_always_prepared_mode_error",
206+
[NSString stringWithFormat:@"Failed to set recording always prepared mode: %ld", (long)result],
207+
nil);
208+
}
209+
}
210+
211+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(audioDeviceModuleGetEngineAvailability) {
212+
RTCAudioEngineAvailability availability = RAW_ADM.engineAvailability;
213+
return @{
214+
@"isInputAvailable" : @(availability.isInputAvailable),
215+
@"isOutputAvailable" : @(availability.isOutputAvailable)
216+
};
217+
}
218+
219+
RCT_EXPORT_METHOD(audioDeviceModuleSetEngineAvailability
220+
: (NSDictionary *)availabilityDict resolver
221+
: (RCTPromiseResolveBlock)resolve rejecter
222+
: (RCTPromiseRejectBlock)reject) {
223+
RTCAudioEngineAvailability availability;
224+
availability.isInputAvailable = [availabilityDict[@"isInputAvailable"] boolValue];
225+
availability.isOutputAvailable = [availabilityDict[@"isOutputAvailable"] boolValue];
226+
NSInteger result = [RAW_ADM setEngineAvailability:availability];
227+
if (result == 0) {
228+
resolve(nil);
229+
} else {
230+
reject(@"engine_availability_error",
231+
[NSString stringWithFormat:@"Failed to set engine availability: %ld", (long)result],
232+
nil);
233+
}
234+
}
235+
236+
// TODO: Observer delegate "resolve" methods were skipped because our current
237+
// `AudioDeviceModuleObserver` does not expose async JS-driven resolution hooks;
238+
// the Swift `AudioDeviceModule` wrapper always returns success immediately.
239+
240+
@end
241+
242+
#undef RAW_ADM

ios/RCTWebRTC/WebRTCModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ static NSString *const kEventAudioDeviceModuleAudioProcessingStateUpdated = @"au
4848
@property(nonatomic, strong) NSMutableDictionary<NSString *, RTCMediaStream *> *localStreams;
4949
@property(nonatomic, strong) NSMutableDictionary<NSString *, RTCMediaStreamTrack *> *localTracks;
5050

51+
// TODO: FrameCryption is not supported by this SDK yet. These containers are
52+
// retained so the native factory initialization keeps working unchanged.
5153
@property(nonatomic, strong) NSMutableDictionary<NSString *, RTCFrameCryptor *> *frameCryptors;
5254
@property(nonatomic, strong) NSMutableDictionary<NSString *, RTCFrameCryptorKeyProvider *> *keyProviders;
5355
@property(nonatomic, strong) NSMutableDictionary<NSString *, RTCDataPacketCryptor *> *dataPacketCryptors;

ios/RCTWebRTC/WebRTCModule.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ - (instancetype)init {
131131
_localStreams = [NSMutableDictionary new];
132132
_localTracks = [NSMutableDictionary new];
133133

134+
// TODO: FrameCryption is not supported yet; dictionaries left empty.
134135
_frameCryptors = [NSMutableDictionary new];
135136
_keyProviders = [NSMutableDictionary new];
136137
_dataPacketCryptors = [NSMutableDictionary new];

0 commit comments

Comments
 (0)