react-native-altibbi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +190 -0
  3. package/android/build.gradle +104 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +3 -0
  6. package/android/src/main/AndroidManifestNew.xml +2 -0
  7. package/android/src/main/java/com/altibbi/AltibbiModule.kt +18 -0
  8. package/android/src/main/java/com/altibbi/AltibbiPackage.kt +18 -0
  9. package/android/src/main/java/com/altibbi/OTCustomAudioDevice.java +1146 -0
  10. package/android/src/main/java/com/altibbi/OTPublisherLayout.java +61 -0
  11. package/android/src/main/java/com/altibbi/OTPublisherViewManager.java +30 -0
  12. package/android/src/main/java/com/altibbi/OTRN.java +101 -0
  13. package/android/src/main/java/com/altibbi/OTScreenCapturer.java +120 -0
  14. package/android/src/main/java/com/altibbi/OTSessionManager.java +1281 -0
  15. package/android/src/main/java/com/altibbi/OTSubscriberLayout.java +68 -0
  16. package/android/src/main/java/com/altibbi/OTSubscriberViewManager.java +30 -0
  17. package/android/src/main/java/com/altibbi/Socket.kt +294 -0
  18. package/android/src/main/java/com/altibbi/SocketEventEmitter.kt +25 -0
  19. package/android/src/main/java/com/altibbi/utils/EventUtils.java +189 -0
  20. package/android/src/main/java/com/altibbi/utils/Utils.java +135 -0
  21. package/ios/Altibbi-Bridging-Header.h +6 -0
  22. package/ios/Altibbi.mm +10 -0
  23. package/ios/Altibbi.swift +4 -0
  24. package/ios/OTCustomAudioDriver.swift +696 -0
  25. package/ios/OTPublisher.m +16 -0
  26. package/ios/OTPublisherManager.swift +21 -0
  27. package/ios/OTPublisherView.swift +28 -0
  28. package/ios/OTRN.swift +27 -0
  29. package/ios/OTScreenCapture.h +27 -0
  30. package/ios/OTScreenCapture.m +171 -0
  31. package/ios/OTSessionManager.m +127 -0
  32. package/ios/OTSessionManager.swift +866 -0
  33. package/ios/OTSubscriber.m +15 -0
  34. package/ios/OTSubscriberManager.swift +21 -0
  35. package/ios/OTSubscriberView.swift +29 -0
  36. package/ios/OpenTokReactNative.h +13 -0
  37. package/ios/OpenTokReactNative.m +13 -0
  38. package/ios/SocketReactNative.m +38 -0
  39. package/ios/SocketReactNative.swift +276 -0
  40. package/ios/Utils/EventUtils.swift +143 -0
  41. package/ios/Utils/Utils.swift +126 -0
  42. package/lib/commonjs/connection.js +200 -0
  43. package/lib/commonjs/connection.js.map +1 -0
  44. package/lib/commonjs/data.js +12 -0
  45. package/lib/commonjs/data.js.map +1 -0
  46. package/lib/commonjs/index.js +275 -0
  47. package/lib/commonjs/index.js.map +1 -0
  48. package/lib/commonjs/scoket.js +245 -0
  49. package/lib/commonjs/scoket.js.map +1 -0
  50. package/lib/commonjs/service.js +21 -0
  51. package/lib/commonjs/service.js.map +1 -0
  52. package/lib/commonjs/types.js +2 -0
  53. package/lib/commonjs/types.js.map +1 -0
  54. package/lib/commonjs/video/OT.js +57 -0
  55. package/lib/commonjs/video/OT.js.map +1 -0
  56. package/lib/commonjs/video/OTError.js +17 -0
  57. package/lib/commonjs/video/OTError.js.map +1 -0
  58. package/lib/commonjs/video/OTPublisher.js +171 -0
  59. package/lib/commonjs/video/OTPublisher.js.map +1 -0
  60. package/lib/commonjs/video/OTSession.js +205 -0
  61. package/lib/commonjs/video/OTSession.js.map +1 -0
  62. package/lib/commonjs/video/OTSubscriber.js +185 -0
  63. package/lib/commonjs/video/OTSubscriber.js.map +1 -0
  64. package/lib/commonjs/video/contexts/OTContext.js +11 -0
  65. package/lib/commonjs/video/contexts/OTContext.js.map +1 -0
  66. package/lib/commonjs/video/helpers/OTHelper.js +92 -0
  67. package/lib/commonjs/video/helpers/OTHelper.js.map +1 -0
  68. package/lib/commonjs/video/helpers/OTPublisherHelper.js +117 -0
  69. package/lib/commonjs/video/helpers/OTPublisherHelper.js.map +1 -0
  70. package/lib/commonjs/video/helpers/OTSessionHelper.js +206 -0
  71. package/lib/commonjs/video/helpers/OTSessionHelper.js.map +1 -0
  72. package/lib/commonjs/video/helpers/OTSubscriberHelper.js +121 -0
  73. package/lib/commonjs/video/helpers/OTSubscriberHelper.js.map +1 -0
  74. package/lib/commonjs/video/index.js +42 -0
  75. package/lib/commonjs/video/index.js.map +1 -0
  76. package/lib/commonjs/video/views/OTPublisherView.js +26 -0
  77. package/lib/commonjs/video/views/OTPublisherView.js.map +1 -0
  78. package/lib/commonjs/video/views/OTSubscriberView.js +25 -0
  79. package/lib/commonjs/video/views/OTSubscriberView.js.map +1 -0
  80. package/lib/module/connection.js +180 -0
  81. package/lib/module/connection.js.map +1 -0
  82. package/lib/module/data.js +6 -0
  83. package/lib/module/data.js.map +1 -0
  84. package/lib/module/index.js +12 -0
  85. package/lib/module/index.js.map +1 -0
  86. package/lib/module/scoket.js +235 -0
  87. package/lib/module/scoket.js.map +1 -0
  88. package/lib/module/service.js +14 -0
  89. package/lib/module/service.js.map +1 -0
  90. package/lib/module/types.js +2 -0
  91. package/lib/module/types.js.map +1 -0
  92. package/lib/module/video/OT.js +49 -0
  93. package/lib/module/video/OT.js.map +1 -0
  94. package/lib/module/video/OTError.js +10 -0
  95. package/lib/module/video/OTError.js.map +1 -0
  96. package/lib/module/video/OTPublisher.js +162 -0
  97. package/lib/module/video/OTPublisher.js.map +1 -0
  98. package/lib/module/video/OTSession.js +195 -0
  99. package/lib/module/video/OTSession.js.map +1 -0
  100. package/lib/module/video/OTSubscriber.js +175 -0
  101. package/lib/module/video/OTSubscriber.js.map +1 -0
  102. package/lib/module/video/contexts/OTContext.js +4 -0
  103. package/lib/module/video/contexts/OTContext.js.map +1 -0
  104. package/lib/module/video/helpers/OTHelper.js +82 -0
  105. package/lib/module/video/helpers/OTHelper.js.map +1 -0
  106. package/lib/module/video/helpers/OTPublisherHelper.js +110 -0
  107. package/lib/module/video/helpers/OTPublisherHelper.js.map +1 -0
  108. package/lib/module/video/helpers/OTSessionHelper.js +195 -0
  109. package/lib/module/video/helpers/OTSessionHelper.js.map +1 -0
  110. package/lib/module/video/helpers/OTSubscriberHelper.js +112 -0
  111. package/lib/module/video/helpers/OTSubscriberHelper.js.map +1 -0
  112. package/lib/module/video/index.js +7 -0
  113. package/lib/module/video/index.js.map +1 -0
  114. package/lib/module/video/views/OTPublisherView.js +18 -0
  115. package/lib/module/video/views/OTPublisherView.js.map +1 -0
  116. package/lib/module/video/views/OTSubscriberView.js +17 -0
  117. package/lib/module/video/views/OTSubscriberView.js.map +1 -0
  118. package/lib/typescript/src/connection.d.ts +40 -0
  119. package/lib/typescript/src/connection.d.ts.map +1 -0
  120. package/lib/typescript/src/data.d.ts +7 -0
  121. package/lib/typescript/src/data.d.ts.map +1 -0
  122. package/lib/typescript/src/index.d.ts +12 -0
  123. package/lib/typescript/src/index.d.ts.map +1 -0
  124. package/lib/typescript/src/scoket.d.ts +100 -0
  125. package/lib/typescript/src/scoket.d.ts.map +1 -0
  126. package/lib/typescript/src/service.d.ts +9 -0
  127. package/lib/typescript/src/service.d.ts.map +1 -0
  128. package/lib/typescript/src/types.d.ts +22 -0
  129. package/lib/typescript/src/types.d.ts.map +1 -0
  130. package/package.json +178 -0
  131. package/react-native-altibbi.podspec +46 -0
  132. package/src/connection.ts +255 -0
  133. package/src/data.ts +21 -0
  134. package/src/index.tsx +80 -0
  135. package/src/scoket.ts +365 -0
  136. package/src/service.ts +20 -0
  137. package/src/types.ts +22 -0
  138. package/src/video/OT.js +65 -0
  139. package/src/video/OTError.js +14 -0
  140. package/src/video/OTPublisher.js +193 -0
  141. package/src/video/OTSession.js +168 -0
  142. package/src/video/OTSubscriber.js +148 -0
  143. package/src/video/contexts/OTContext.js +5 -0
  144. package/src/video/helpers/OTHelper.js +91 -0
  145. package/src/video/helpers/OTPublisherHelper.js +122 -0
  146. package/src/video/helpers/OTSessionHelper.js +233 -0
  147. package/src/video/helpers/OTSubscriberHelper.js +125 -0
  148. package/src/video/index.js +13 -0
  149. package/src/video/views/OTPublisherView.js +19 -0
  150. package/src/video/views/OTSubscriberView.js +18 -0
@@ -0,0 +1,696 @@
1
+ import Foundation
2
+ import OpenTok
3
+
4
+ class OTCustomAudioDriver: NSObject {
5
+ #if targetEnvironment(simulator)
6
+ static let kSampleRate: UInt16 = 44100
7
+ #else
8
+ static let kSampleRate: UInt16 = 48000
9
+ #endif
10
+ static let kOutputBus = AudioUnitElement(0)
11
+ static let kInputBus = AudioUnitElement(1)
12
+ static let kAudioDeviceHeadset = "AudioSessionManagerDevice_Headset"
13
+ static let kAudioDeviceBluetooth = "AudioSessionManagerDevice_Bluetooth"
14
+ static let kAudioDeviceSpeaker = "AudioSessionManagerDevice_Speaker"
15
+
16
+ var inputAudioFormat = OTAudioFormat()
17
+ var outputAudioFormat = OTAudioFormat()
18
+ let safetyQueue = DispatchQueue(label: "ot-audio-driver")
19
+
20
+ var deviceAudioBus: OTAudioBus?
21
+
22
+ func setAudioBus(_ audioBus: OTAudioBus?) -> Bool {
23
+ deviceAudioBus = audioBus
24
+ outputAudioFormat = OTAudioFormat()
25
+ outputAudioFormat.sampleRate = OTCustomAudioDriver.kSampleRate
26
+ outputAudioFormat.numChannels = 2
27
+ inputAudioFormat = OTAudioFormat()
28
+ inputAudioFormat.sampleRate = OTCustomAudioDriver.kSampleRate
29
+ inputAudioFormat.numChannels = 1
30
+
31
+ return true
32
+ }
33
+
34
+ var bufferList: UnsafeMutablePointer<AudioBufferList>?
35
+ var bufferSize: UInt32 = 0
36
+ var bufferNumFrames: UInt32 = 0
37
+ var playoutAudioUnitPropertyLatency: Float64 = 0
38
+ var playoutDelayMeasurementCounter: UInt32 = 0
39
+ var recordingDelayMeasurementCounter: UInt32 = 0
40
+ var recordingDelayHWAndOS: UInt32 = 0
41
+ var recordingDelay: UInt32 = 0
42
+ var recordingAudioUnitPropertyLatency: Float64 = 0
43
+ var playoutDelay: UInt32 = 0
44
+ var playing = false
45
+ var playoutInitialized = false
46
+ var recording = false
47
+ var recordingInitialized = false
48
+ var interruptedPlayback = false
49
+ var isRecorderInterrupted = false
50
+ var isPlayerInterrupted = false
51
+ var isResetting = false
52
+ var restartRetryCount = 0
53
+ fileprivate var recordingVoiceUnit: AudioUnit?
54
+ fileprivate var playoutVoiceUnit: AudioUnit?
55
+
56
+ fileprivate var previousAVAudioSessionCategory: AVAudioSession.Category?
57
+ fileprivate var avAudioSessionMode: AVAudioSession.Mode?
58
+ fileprivate var avAudioSessionPreffSampleRate = Double(0)
59
+ fileprivate var avAudioSessionChannels = 0
60
+ fileprivate var isAudioSessionSetup = false
61
+
62
+ var areListenerBlocksSetup = false
63
+ var streamFormat = AudioStreamBasicDescription()
64
+
65
+ override init() {
66
+ inputAudioFormat.sampleRate = OTCustomAudioDriver.kSampleRate
67
+ inputAudioFormat.numChannels = 1
68
+ outputAudioFormat.sampleRate = OTCustomAudioDriver.kSampleRate
69
+ outputAudioFormat.numChannels = 2
70
+ }
71
+
72
+ deinit {
73
+ tearDownAudio()
74
+ removeObservers()
75
+ }
76
+
77
+
78
+ fileprivate func restartAudio() {
79
+ safetyQueue.async {
80
+ self.doRestartAudio(numberOfAttempts: 3)
81
+ }
82
+ }
83
+
84
+ fileprivate func restartAudioAfterInterruption() {
85
+ if isRecorderInterrupted {
86
+ if startCapture() {
87
+ isRecorderInterrupted = false
88
+ restartRetryCount = 0
89
+ } else {
90
+ restartRetryCount += 1
91
+ if restartRetryCount < 3 {
92
+ safetyQueue.asyncAfter(deadline: DispatchTime.now(), execute: { [unowned self] in
93
+ self.restartAudioAfterInterruption()
94
+ })
95
+ } else {
96
+ isRecorderInterrupted = false
97
+ isPlayerInterrupted = false
98
+ restartRetryCount = 0
99
+ print("ERROR[OpenTok]:Unable to acquire audio session")
100
+ }
101
+ }
102
+ }
103
+ if isPlayerInterrupted {
104
+ isPlayerInterrupted = false
105
+ let _ = startRendering()
106
+ }
107
+ }
108
+
109
+ fileprivate func doRestartAudio(numberOfAttempts: Int) {
110
+
111
+ if recording {
112
+ let _ = stopCapture()
113
+ disposeAudioUnit(audioUnit: &recordingVoiceUnit)
114
+ let _ = startCapture()
115
+ }
116
+
117
+ if playing {
118
+ let _ = self.stopRendering()
119
+ disposeAudioUnit(audioUnit: &playoutVoiceUnit)
120
+ let _ = self.startRendering()
121
+ }
122
+ isResetting = false
123
+ }
124
+
125
+ fileprivate func setupAudioUnit(withPlayout playout: Bool) -> Bool {
126
+ if !isAudioSessionSetup {
127
+ setupAudioSession()
128
+ isAudioSessionSetup = true
129
+ }
130
+
131
+ let bytesPerSample = UInt32(MemoryLayout<Int16>.size)
132
+ streamFormat.mFormatID = kAudioFormatLinearPCM
133
+ streamFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
134
+ if playout {
135
+ streamFormat.mBytesPerPacket = bytesPerSample*2
136
+ streamFormat.mBytesPerFrame = bytesPerSample*2
137
+ streamFormat.mChannelsPerFrame = 2
138
+ }
139
+ else {
140
+ streamFormat.mBytesPerPacket = bytesPerSample
141
+ streamFormat.mBytesPerFrame = bytesPerSample
142
+ streamFormat.mChannelsPerFrame = 1
143
+ }
144
+ streamFormat.mFramesPerPacket = 1
145
+ streamFormat.mBitsPerChannel = 8 * bytesPerSample
146
+ streamFormat.mSampleRate = Float64(OTCustomAudioDriver.kSampleRate)
147
+
148
+ var audioUnitDescription = AudioComponentDescription()
149
+ audioUnitDescription.componentType = kAudioUnitType_Output
150
+ if playout {
151
+ audioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO
152
+ }
153
+ else{
154
+ audioUnitDescription.componentSubType = kAudioUnitSubType_VoiceProcessingIO
155
+ }
156
+ audioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO
157
+ audioUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple
158
+ audioUnitDescription.componentFlags = 0
159
+ audioUnitDescription.componentFlagsMask = 0
160
+
161
+ let foundVpioUnitRef = AudioComponentFindNext(nil, &audioUnitDescription)
162
+ let result: OSStatus = {
163
+ if playout {
164
+ return AudioComponentInstanceNew(foundVpioUnitRef!, &playoutVoiceUnit)
165
+ } else {
166
+ return AudioComponentInstanceNew(foundVpioUnitRef!, &recordingVoiceUnit)
167
+ }
168
+ }()
169
+
170
+ if result != noErr {
171
+ print("Error seting up audio unit")
172
+ return false
173
+ }
174
+
175
+ var value: UInt32 = 1
176
+ if playout {
177
+ AudioUnitSetProperty(playoutVoiceUnit!, kAudioOutputUnitProperty_EnableIO,
178
+ kAudioUnitScope_Output, OTCustomAudioDriver.kOutputBus, &value,
179
+ UInt32(MemoryLayout<UInt32>.size))
180
+
181
+ AudioUnitSetProperty(playoutVoiceUnit!, kAudioUnitProperty_StreamFormat,
182
+ kAudioUnitScope_Input, OTCustomAudioDriver.kOutputBus, &streamFormat,
183
+ UInt32(MemoryLayout<AudioStreamBasicDescription>.size))
184
+ // Disable Input on playout
185
+ var enableInput = 0
186
+ AudioUnitSetProperty(playoutVoiceUnit!, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,
187
+ OTCustomAudioDriver.kInputBus, &enableInput, UInt32(MemoryLayout<UInt32>.size))
188
+ } else {
189
+ AudioUnitSetProperty(recordingVoiceUnit!, kAudioOutputUnitProperty_EnableIO,
190
+ kAudioUnitScope_Input, OTCustomAudioDriver.kInputBus, &value,
191
+ UInt32(MemoryLayout<UInt32>.size))
192
+ AudioUnitSetProperty(recordingVoiceUnit!, kAudioUnitProperty_StreamFormat,
193
+ kAudioUnitScope_Output, OTCustomAudioDriver.kInputBus, &streamFormat,
194
+ UInt32(MemoryLayout<AudioStreamBasicDescription>.size))
195
+ // Disable Output on record
196
+ var enableOutput = 0
197
+ AudioUnitSetProperty(recordingVoiceUnit!, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,
198
+ OTCustomAudioDriver.kOutputBus, &enableOutput, UInt32(MemoryLayout<UInt32>.size))
199
+ }
200
+
201
+ if playout {
202
+ setupPlayoutCallback()
203
+ } else {
204
+ setupRecordingCallback()
205
+ }
206
+
207
+ setBluetoothAsPreferredInputDevice()
208
+
209
+ return true
210
+ }
211
+
212
+ fileprivate func setupPlayoutCallback() {
213
+ let selfPointer = Unmanaged.passUnretained(self).toOpaque()
214
+ var renderCallback = AURenderCallbackStruct(inputProc: renderCb, inputProcRefCon: selfPointer)
215
+ AudioUnitSetProperty(playoutVoiceUnit!,
216
+ kAudioUnitProperty_SetRenderCallback,
217
+ kAudioUnitScope_Input,
218
+ OTCustomAudioDriver.kOutputBus,
219
+ &renderCallback,
220
+ UInt32(MemoryLayout<AURenderCallbackStruct>.size))
221
+
222
+ }
223
+
224
+ fileprivate func setupRecordingCallback() {
225
+ let selfPointer = Unmanaged.passUnretained(self).toOpaque()
226
+ var inputCallback = AURenderCallbackStruct(inputProc: recordCb, inputProcRefCon: selfPointer)
227
+ AudioUnitSetProperty(recordingVoiceUnit!,
228
+ kAudioOutputUnitProperty_SetInputCallback,
229
+ kAudioUnitScope_Global,
230
+ OTCustomAudioDriver.kInputBus,
231
+ &inputCallback,
232
+ UInt32(MemoryLayout<AURenderCallbackStruct>.size))
233
+
234
+ var value = 0
235
+ AudioUnitSetProperty(recordingVoiceUnit!,
236
+ kAudioUnitProperty_ShouldAllocateBuffer,
237
+ kAudioUnitScope_Output,
238
+ OTCustomAudioDriver.kInputBus,
239
+ &value,
240
+ UInt32(MemoryLayout<UInt32>.size))
241
+ }
242
+
243
+ fileprivate func disposeAudioUnit(audioUnit: inout AudioUnit?) {
244
+ if let unit = audioUnit {
245
+ AudioUnitUninitialize(unit)
246
+ AudioComponentInstanceDispose(unit)
247
+ }
248
+ audioUnit = nil
249
+ }
250
+
251
+ fileprivate func tearDownAudio() {
252
+ print("Destoying audio units")
253
+ disposeAudioUnit(audioUnit: &playoutVoiceUnit)
254
+ disposeAudioUnit(audioUnit: &recordingVoiceUnit)
255
+ freeupAudioBuffers()
256
+
257
+ let session = AVAudioSession.sharedInstance()
258
+ do {
259
+ guard let previousAVAudioSessionCategory = previousAVAudioSessionCategory else { return }
260
+ if #available(iOS 10.0, *) {
261
+ try session.setCategory(previousAVAudioSessionCategory, mode: .default)
262
+ } else {
263
+ try session.setCategory(previousAVAudioSessionCategory)
264
+ }
265
+ guard let avAudioSessionMode = avAudioSessionMode else { return }
266
+ try session.setMode(avAudioSessionMode)
267
+ try session.setPreferredSampleRate(avAudioSessionPreffSampleRate)
268
+ try session.setPreferredInputNumberOfChannels(avAudioSessionChannels)
269
+
270
+ isAudioSessionSetup = false
271
+ } catch {
272
+ print("Error reseting AVAudioSession")
273
+ }
274
+ }
275
+
276
+ fileprivate func freeupAudioBuffers() {
277
+ if var data = bufferList?.pointee, data.mBuffers.mData != nil {
278
+ data.mBuffers.mData?.assumingMemoryBound(to: UInt16.self).deallocate()
279
+ data.mBuffers.mData = nil
280
+ }
281
+
282
+ if let list = bufferList {
283
+ list.deallocate();
284
+ }
285
+
286
+ bufferList = nil
287
+ bufferNumFrames = 0
288
+ }
289
+ }
290
+
291
+ // MARK: - Audio Device Implementation
292
+ extension OTCustomAudioDriver: OTAudioDevice {
293
+ func captureFormat() -> OTAudioFormat {
294
+ return inputAudioFormat
295
+ }
296
+ func renderFormat() -> OTAudioFormat {
297
+ return outputAudioFormat
298
+ }
299
+ func renderingIsAvailable() -> Bool {
300
+ return true
301
+ }
302
+ func renderingIsInitialized() -> Bool {
303
+ return playoutInitialized
304
+ }
305
+ func isRendering() -> Bool {
306
+ return playing
307
+ }
308
+ func isCapturing() -> Bool {
309
+ return recording
310
+ }
311
+ func estimatedRenderDelay() -> UInt16 {
312
+ return UInt16(playoutDelay)
313
+ }
314
+ func estimatedCaptureDelay() -> UInt16 {
315
+ return UInt16(recordingDelay)
316
+ }
317
+ func captureIsAvailable() -> Bool {
318
+ return true
319
+ }
320
+ func captureIsInitialized() -> Bool {
321
+ return recordingInitialized
322
+ }
323
+
324
+ func initializeRendering() -> Bool {
325
+ if playing { return false }
326
+
327
+ playoutInitialized = true
328
+ return playoutInitialized
329
+ }
330
+
331
+ func startRendering() -> Bool {
332
+ if playing { return true }
333
+ playing = true
334
+ if playoutVoiceUnit == nil {
335
+ playing = setupAudioUnit(withPlayout: true)
336
+ if !playing {
337
+ return false
338
+ }
339
+ }
340
+
341
+ let result = AudioOutputUnitStart(playoutVoiceUnit!)
342
+
343
+ if result != noErr {
344
+ print("Error creaing rendering unit")
345
+ playing = false
346
+ }
347
+ return playing
348
+ }
349
+
350
+ func stopRendering() -> Bool {
351
+ if !playing {
352
+ return true
353
+ }
354
+
355
+ playing = false
356
+
357
+ if playoutVoiceUnit != nil {
358
+ let result = AudioOutputUnitStop(playoutVoiceUnit!)
359
+ if result != noErr {
360
+ print("Error creaing playout unit")
361
+ return false
362
+ }
363
+ }
364
+
365
+ if !recording && !isPlayerInterrupted && !isResetting {
366
+ tearDownAudio()
367
+ }
368
+
369
+ return true
370
+ }
371
+
372
+
373
+ func initializeCapture() -> Bool {
374
+ if recording { return false }
375
+
376
+ recordingInitialized = true
377
+ return recordingInitialized
378
+ }
379
+
380
+ func startCapture() -> Bool {
381
+ if recording {
382
+ return true
383
+ }
384
+
385
+ recording = true
386
+
387
+ if recordingVoiceUnit == nil {
388
+ recording = setupAudioUnit(withPlayout: false)
389
+
390
+ if !recording {
391
+ return false
392
+ }
393
+ }
394
+
395
+ let result = AudioOutputUnitStart(recordingVoiceUnit!)
396
+ if result != noErr {
397
+ recording = false
398
+ }
399
+
400
+ return recording
401
+ }
402
+
403
+ func stopCapture() -> Bool {
404
+ if !recording {
405
+ return true
406
+ }
407
+
408
+ recording = false
409
+
410
+ let result = AudioOutputUnitStop(recordingVoiceUnit!)
411
+
412
+ if result != noErr {
413
+ return false
414
+ }
415
+
416
+ freeupAudioBuffers()
417
+
418
+ if !recording && !isRecorderInterrupted && !isResetting {
419
+ tearDownAudio()
420
+ }
421
+
422
+ return true
423
+ }
424
+
425
+ }
426
+
427
+ // MARK: - AVAudioSession
428
+ extension OTCustomAudioDriver {
429
+ @objc func onInterruptionEvent(notification: Notification) {
430
+ let type = notification.userInfo?[AVAudioSessionInterruptionTypeKey]
431
+ safetyQueue.async {
432
+ self.handleInterruptionEvent(type: type as? Int)
433
+ }
434
+ }
435
+
436
+ fileprivate func handleInterruptionEvent(type: Int?) {
437
+ guard let interruptionType = type else {
438
+ return
439
+ }
440
+
441
+ switch UInt(interruptionType) {
442
+ case AVAudioSession.InterruptionType.began.rawValue:
443
+ if recording {
444
+ isRecorderInterrupted = true
445
+ let _ = stopCapture()
446
+ }
447
+ if playing {
448
+ isPlayerInterrupted = true
449
+ let _ = stopRendering()
450
+ }
451
+ case AVAudioSession.InterruptionType.ended.rawValue:
452
+ configureAudioSessionWithDesiredAudioRoute(desiredAudioRoute: OTCustomAudioDriver.kAudioDeviceBluetooth)
453
+ restartAudioAfterInterruption()
454
+ default:
455
+ break
456
+ }
457
+ }
458
+
459
+ @objc func onRouteChangeEvent(notification: Notification) {
460
+ safetyQueue.async {
461
+ self.handleRouteChangeEvent(notification: notification)
462
+ }
463
+ }
464
+
465
+ @objc func appDidBecomeActive(notification: Notification) {
466
+ safetyQueue.async {
467
+ self.handleInterruptionEvent(type: Int(AVAudioSession.InterruptionType.ended.rawValue))
468
+ }
469
+ }
470
+
471
+ fileprivate func handleRouteChangeEvent(notification: Notification) {
472
+ guard let reason = notification.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt else {
473
+ return
474
+ }
475
+
476
+ if reason == AVAudioSession.RouteChangeReason.routeConfigurationChange.rawValue {
477
+ return
478
+ }
479
+
480
+ if reason == AVAudioSession.RouteChangeReason.override.rawValue ||
481
+ reason == AVAudioSession.RouteChangeReason.categoryChange.rawValue {
482
+
483
+ let oldRouteDesc = notification.userInfo?[AVAudioSessionRouteChangePreviousRouteKey] as! AVAudioSessionRouteDescription
484
+ let outputs = oldRouteDesc.outputs
485
+ var oldOutputDeviceName: String? = nil
486
+ var currentOutputDeviceName: String? = nil
487
+
488
+ if outputs.count > 0 {
489
+ let portDesc = outputs[0]
490
+ oldOutputDeviceName = portDesc.portName
491
+ }
492
+
493
+ if AVAudioSession.sharedInstance().currentRoute.outputs.count > 0 {
494
+ currentOutputDeviceName = AVAudioSession.sharedInstance().currentRoute.outputs[0].portName
495
+ }
496
+
497
+ if oldOutputDeviceName == currentOutputDeviceName || currentOutputDeviceName == nil || oldOutputDeviceName == nil {
498
+ return
499
+ }
500
+
501
+ restartAudio()
502
+ }
503
+ }
504
+
505
+ fileprivate func setupListenerBlocks() {
506
+ if areListenerBlocksSetup {
507
+ return
508
+ }
509
+
510
+ let notificationCenter = NotificationCenter.default
511
+
512
+ notificationCenter.addObserver(self, selector: #selector(OTCustomAudioDriver.onInterruptionEvent),
513
+ name: AVAudioSession.interruptionNotification, object: nil)
514
+ notificationCenter.addObserver(self, selector: #selector(OTCustomAudioDriver.onRouteChangeEvent(notification:)),
515
+ name: AVAudioSession.routeChangeNotification, object: nil)
516
+ notificationCenter.addObserver(self, selector: #selector(OTCustomAudioDriver.appDidBecomeActive(notification:)),
517
+ name: UIApplication.didBecomeActiveNotification, object: nil)
518
+
519
+ areListenerBlocksSetup = true
520
+ }
521
+
522
+ fileprivate func removeObservers() {
523
+ NotificationCenter.default.removeObserver(self)
524
+ areListenerBlocksSetup = false
525
+ }
526
+
527
+ fileprivate func setupAudioSession() {
528
+ let session = AVAudioSession.sharedInstance()
529
+
530
+ previousAVAudioSessionCategory = session.category
531
+ avAudioSessionMode = session.mode
532
+ avAudioSessionPreffSampleRate = session.preferredSampleRate
533
+ avAudioSessionChannels = session.inputNumberOfChannels
534
+
535
+ do {
536
+ try session.setPreferredOutputNumberOfChannels(2)
537
+ try session.setPreferredSampleRate(Double(OTCustomAudioDriver.kSampleRate))
538
+ try session.setPreferredIOBufferDuration(0.01)
539
+ let audioOptions = AVAudioSession.CategoryOptions.mixWithOthers.rawValue |
540
+ AVAudioSession.CategoryOptions.allowBluetooth.rawValue |
541
+ AVAudioSession.CategoryOptions.defaultToSpeaker.rawValue
542
+ if #available(iOS 10.0, *) {
543
+ try session.setCategory(.playAndRecord, mode: .videoChat, options: AVAudioSession.CategoryOptions(rawValue: audioOptions))
544
+ } else {
545
+ try session.setCategory(.playAndRecord, options: AVAudioSession.CategoryOptions(rawValue: audioOptions))
546
+ }
547
+ setupListenerBlocks()
548
+
549
+ try session.setActive(true)
550
+ try session.setPreferredOutputNumberOfChannels(2)
551
+ } catch let err as NSError {
552
+ print("Error setting up audio session \(err)")
553
+ } catch {
554
+ print("Error setting up audio session")
555
+ }
556
+ print("preferred output channels = \(session.preferredOutputNumberOfChannels)")
557
+ }
558
+ }
559
+
560
+ // MARK: - Audio Route functions
561
+ extension OTCustomAudioDriver {
562
+ fileprivate func setBluetoothAsPreferredInputDevice() {
563
+ let btRoutes = [AVAudioSession.Port.bluetoothA2DP, AVAudioSession.Port.bluetoothLE, AVAudioSession.Port.bluetoothHFP]
564
+ AVAudioSession.sharedInstance().availableInputs?.forEach({ el in
565
+ if btRoutes.contains(el.portType) {
566
+ do {
567
+ try AVAudioSession.sharedInstance().setPreferredInput(el)
568
+ } catch {
569
+ print("Error setting BT as preferred input device")
570
+ }
571
+ }
572
+ })
573
+ }
574
+
575
+ fileprivate func configureAudioSessionWithDesiredAudioRoute(desiredAudioRoute: String) {
576
+ let session = AVAudioSession.sharedInstance()
577
+
578
+ if desiredAudioRoute == OTCustomAudioDriver.kAudioDeviceBluetooth {
579
+ setBluetoothAsPreferredInputDevice()
580
+ }
581
+ do {
582
+ if desiredAudioRoute == OTCustomAudioDriver.kAudioDeviceSpeaker {
583
+ try session.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
584
+ } else {
585
+ try session.overrideOutputAudioPort(AVAudioSession.PortOverride.none)
586
+ }
587
+ } catch let err as NSError {
588
+ print("Error setting audio route: \(err)")
589
+ }
590
+ }
591
+ }
592
+
593
+ // MARK: - Render and Record C Callbacks
594
+ func renderCb(inRefCon:UnsafeMutableRawPointer,
595
+ ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
596
+ inTimeStamp:UnsafePointer<AudioTimeStamp>,
597
+ inBusNumber:UInt32,
598
+ inNumberFrames:UInt32,
599
+ ioData:UnsafeMutablePointer<AudioBufferList>?) -> OSStatus
600
+ {
601
+ let audioDevice: OTCustomAudioDriver = Unmanaged.fromOpaque(inRefCon).takeUnretainedValue()
602
+ if !audioDevice.playing { return 0 }
603
+
604
+ let _ = audioDevice.deviceAudioBus!.readRenderData((ioData?.pointee.mBuffers.mData)!, numberOfSamples: inNumberFrames)
605
+ updatePlayoutDelay(withAudioDevice: audioDevice)
606
+
607
+ return noErr
608
+ }
609
+
610
+ func recordCb(inRefCon:UnsafeMutableRawPointer,
611
+ ioActionFlags:UnsafeMutablePointer<AudioUnitRenderActionFlags>,
612
+ inTimeStamp:UnsafePointer<AudioTimeStamp>,
613
+ inBusNumber:UInt32,
614
+ inNumberFrames:UInt32,
615
+ ioData:UnsafeMutablePointer<AudioBufferList>?) -> OSStatus
616
+ {
617
+ let audioDevice: OTCustomAudioDriver = Unmanaged.fromOpaque(inRefCon).takeUnretainedValue()
618
+ if audioDevice.bufferList == nil || inNumberFrames > audioDevice.bufferNumFrames {
619
+ if audioDevice.bufferList != nil {
620
+ audioDevice.bufferList!.pointee.mBuffers.mData?
621
+ .assumingMemoryBound(to: UInt16.self).deallocate()
622
+ audioDevice.bufferList?.deallocate()
623
+ }
624
+
625
+ audioDevice.bufferList = UnsafeMutablePointer<AudioBufferList>.allocate(capacity: 1)
626
+ audioDevice.bufferList?.pointee.mNumberBuffers = 1
627
+ audioDevice.bufferList?.pointee.mBuffers.mNumberChannels = 1
628
+
629
+ audioDevice.bufferList?.pointee.mBuffers.mDataByteSize = inNumberFrames * UInt32(MemoryLayout<UInt16>.size)
630
+ audioDevice.bufferList?.pointee.mBuffers.mData = UnsafeMutableRawPointer(UnsafeMutablePointer<UInt16>.allocate(capacity: Int(inNumberFrames)))
631
+ audioDevice.bufferNumFrames = inNumberFrames
632
+ audioDevice.bufferSize = (audioDevice.bufferList?.pointee.mBuffers.mDataByteSize)!
633
+ }
634
+
635
+ AudioUnitRender(audioDevice.recordingVoiceUnit!,
636
+ ioActionFlags,
637
+ inTimeStamp,
638
+ 1,
639
+ inNumberFrames,
640
+ audioDevice.bufferList!)
641
+
642
+ if audioDevice.recording {
643
+ audioDevice.deviceAudioBus!.writeCaptureData((audioDevice.bufferList?.pointee.mBuffers.mData)!, numberOfSamples: inNumberFrames)
644
+ }
645
+
646
+ if audioDevice.bufferSize != audioDevice.bufferList?.pointee.mBuffers.mDataByteSize {
647
+ audioDevice.bufferList?.pointee.mBuffers.mDataByteSize = audioDevice.bufferSize
648
+ }
649
+
650
+ updateRecordingDelay(withAudioDevice: audioDevice)
651
+
652
+ return noErr
653
+ }
654
+
655
+ func updatePlayoutDelay(withAudioDevice audioDevice: OTCustomAudioDriver) {
656
+ audioDevice.playoutDelayMeasurementCounter += 1
657
+ if audioDevice.playoutDelayMeasurementCounter >= 100 {
658
+ // Update HW and OS delay every second, unlikely to change
659
+ audioDevice.playoutDelay = 0
660
+ let session = AVAudioSession.sharedInstance()
661
+
662
+ // HW output latency
663
+ let interval = session.outputLatency
664
+ audioDevice.playoutDelay += UInt32(interval * 1000000)
665
+ // HW buffer duration
666
+ let ioInterval = session.ioBufferDuration
667
+ audioDevice.playoutDelay += UInt32(ioInterval * 1000000)
668
+ audioDevice.playoutDelay += UInt32(audioDevice.playoutAudioUnitPropertyLatency * 1000000)
669
+ // To ms
670
+ audioDevice.playoutDelay = (audioDevice.playoutDelay - 500) / 1000
671
+
672
+ audioDevice.playoutDelayMeasurementCounter = 0
673
+ }
674
+ }
675
+
676
+ func updateRecordingDelay(withAudioDevice audioDevice: OTCustomAudioDriver) {
677
+ audioDevice.recordingDelayMeasurementCounter += 1
678
+
679
+ if audioDevice.recordingDelayMeasurementCounter >= 100 {
680
+ audioDevice.recordingDelayHWAndOS = 0
681
+ let session = AVAudioSession.sharedInstance()
682
+ let interval = session.inputLatency
683
+
684
+ audioDevice.recordingDelayHWAndOS += UInt32(interval * 1000000)
685
+ let ioInterval = session.ioBufferDuration
686
+
687
+ audioDevice.recordingDelayHWAndOS += UInt32(ioInterval * 1000000)
688
+ audioDevice.recordingDelayHWAndOS += UInt32(audioDevice.recordingAudioUnitPropertyLatency * 1000000)
689
+
690
+ audioDevice.recordingDelayHWAndOS = audioDevice.recordingDelayHWAndOS.advanced(by: -500) / 1000
691
+
692
+ audioDevice.recordingDelayMeasurementCounter = 0
693
+ }
694
+
695
+ audioDevice.recordingDelay = audioDevice.recordingDelayHWAndOS
696
+ }