Committed by
GitHub
Support authenticating audio processors (#390)
* Support authenticating audio processors * spotless * fix tests * spotless
正在显示
10 个修改的文件
包含
126 行增加
和
14 行删除
| @@ -40,3 +40,13 @@ interface AudioProcessingController { | @@ -40,3 +40,13 @@ interface AudioProcessingController { | ||
| 40 | */ | 40 | */ |
| 41 | fun setBypassForRenderPreProcessing(bypass: Boolean) | 41 | fun setBypassForRenderPreProcessing(bypass: Boolean) |
| 42 | } | 42 | } |
| 43 | + | ||
| 44 | +/** | ||
| 45 | + * @suppress | ||
| 46 | + */ | ||
| 47 | +interface AuthedAudioProcessingController : AudioProcessingController { | ||
| 48 | + /** | ||
| 49 | + * @suppress | ||
| 50 | + */ | ||
| 51 | + fun authenticate(url: String, token: String) | ||
| 52 | +} |
| @@ -34,16 +34,35 @@ interface AudioProcessorInterface { | @@ -34,16 +34,35 @@ interface AudioProcessorInterface { | ||
| 34 | 34 | ||
| 35 | /** | 35 | /** |
| 36 | * Initialize the audio processing. | 36 | * Initialize the audio processing. |
| 37 | + * | ||
| 38 | + * Note: audio processing methods will be called regardless of whether | ||
| 39 | + * [isEnabled] returns true or not. | ||
| 37 | */ | 40 | */ |
| 38 | fun initializeAudioProcessing(sampleRateHz: Int, numChannels: Int) | 41 | fun initializeAudioProcessing(sampleRateHz: Int, numChannels: Int) |
| 39 | 42 | ||
| 40 | /** | 43 | /** |
| 41 | * Called when the sample rate has changed. | 44 | * Called when the sample rate has changed. |
| 45 | + * | ||
| 46 | + * Note: audio processing methods will be called regardless of whether | ||
| 47 | + * [isEnabled] returns true or not. | ||
| 42 | */ | 48 | */ |
| 43 | fun resetAudioProcessing(newRate: Int) | 49 | fun resetAudioProcessing(newRate: Int) |
| 44 | 50 | ||
| 45 | /** | 51 | /** |
| 46 | * Process the audio frame (10ms). | 52 | * Process the audio frame (10ms). |
| 53 | + * | ||
| 54 | + * Note: audio processing methods will be called regardless of whether | ||
| 55 | + * [isEnabled] returns true or not. | ||
| 47 | */ | 56 | */ |
| 48 | fun processAudio(numBands: Int, numFrames: Int, buffer: ByteBuffer) | 57 | fun processAudio(numBands: Int, numFrames: Int, buffer: ByteBuffer) |
| 49 | } | 58 | } |
| 59 | + | ||
| 60 | +/** | ||
| 61 | + * @suppress | ||
| 62 | + */ | ||
| 63 | +interface AuthedAudioProcessorInterface : AudioProcessorInterface { | ||
| 64 | + /** | ||
| 65 | + * @suppress | ||
| 66 | + */ | ||
| 67 | + fun authenticate(url: String, token: String) | ||
| 68 | +} |
| @@ -38,3 +38,12 @@ data class AudioProcessorOptions( | @@ -38,3 +38,12 @@ data class AudioProcessorOptions( | ||
| 38 | */ | 38 | */ |
| 39 | val renderPreBypass: Boolean = false, | 39 | val renderPreBypass: Boolean = false, |
| 40 | ) | 40 | ) |
| 41 | + | ||
| 42 | +internal fun AudioProcessorOptions.authenticateProcessors(url: String, token: String) { | ||
| 43 | + if (capturePostProcessor is AuthedAudioProcessorInterface) { | ||
| 44 | + capturePostProcessor.authenticate(url, token) | ||
| 45 | + } | ||
| 46 | + if (renderPreProcessor is AuthedAudioProcessorInterface) { | ||
| 47 | + renderPreProcessor.authenticate(url, token) | ||
| 48 | + } | ||
| 49 | +} |
| @@ -287,7 +287,7 @@ internal object RTCModule { | @@ -287,7 +287,7 @@ internal object RTCModule { | ||
| 287 | @Named(InjectionNames.OVERRIDE_PEER_CONNECTION_FACTORY_OPTIONS) | 287 | @Named(InjectionNames.OVERRIDE_PEER_CONNECTION_FACTORY_OPTIONS) |
| 288 | peerConnectionFactoryOptions: PeerConnectionFactory.Options?, | 288 | peerConnectionFactoryOptions: PeerConnectionFactory.Options?, |
| 289 | memoryManager: CloseableManager, | 289 | memoryManager: CloseableManager, |
| 290 | - audioProcessingFactory: AudioProcessingFactory?, | 290 | + audioProcessingFactory: AudioProcessingFactory, |
| 291 | ): PeerConnectionFactory { | 291 | ): PeerConnectionFactory { |
| 292 | return PeerConnectionFactory.builder() | 292 | return PeerConnectionFactory.builder() |
| 293 | .setAudioDeviceModule(audioDeviceModule) | 293 | .setAudioDeviceModule(audioDeviceModule) |
| @@ -31,7 +31,7 @@ import io.livekit.android.RoomOptions | @@ -31,7 +31,7 @@ import io.livekit.android.RoomOptions | ||
| 31 | import io.livekit.android.Version | 31 | import io.livekit.android.Version |
| 32 | import io.livekit.android.audio.AudioHandler | 32 | import io.livekit.android.audio.AudioHandler |
| 33 | import io.livekit.android.audio.AudioProcessingController | 33 | import io.livekit.android.audio.AudioProcessingController |
| 34 | -import io.livekit.android.audio.AudioProcessorOptions | 34 | +import io.livekit.android.audio.AuthedAudioProcessingController |
| 35 | import io.livekit.android.audio.CommunicationWorkaround | 35 | import io.livekit.android.audio.CommunicationWorkaround |
| 36 | import io.livekit.android.dagger.InjectionNames | 36 | import io.livekit.android.dagger.InjectionNames |
| 37 | import io.livekit.android.e2ee.E2EEManager | 37 | import io.livekit.android.e2ee.E2EEManager |
| @@ -41,6 +41,7 @@ import io.livekit.android.memory.CloseableManager | @@ -41,6 +41,7 @@ import io.livekit.android.memory.CloseableManager | ||
| 41 | import io.livekit.android.renderer.TextureViewRenderer | 41 | import io.livekit.android.renderer.TextureViewRenderer |
| 42 | import io.livekit.android.room.network.NetworkCallbackManager | 42 | import io.livekit.android.room.network.NetworkCallbackManager |
| 43 | import io.livekit.android.room.participant.* | 43 | import io.livekit.android.room.participant.* |
| 44 | +import io.livekit.android.room.provisions.LKObjects | ||
| 44 | import io.livekit.android.room.track.* | 45 | import io.livekit.android.room.track.* |
| 45 | import io.livekit.android.util.FlowObservable | 46 | import io.livekit.android.util.FlowObservable |
| 46 | import io.livekit.android.util.LKLog | 47 | import io.livekit.android.util.LKLog |
| @@ -76,6 +77,7 @@ constructor( | @@ -76,6 +77,7 @@ constructor( | ||
| 76 | private val e2EEManagerFactory: E2EEManager.Factory, | 77 | private val e2EEManagerFactory: E2EEManager.Factory, |
| 77 | private val communicationWorkaround: CommunicationWorkaround, | 78 | private val communicationWorkaround: CommunicationWorkaround, |
| 78 | val audioProcessingController: AudioProcessingController, | 79 | val audioProcessingController: AudioProcessingController, |
| 80 | + val lkObjects: LKObjects, | ||
| 79 | ) : RTCEngine.Listener, ParticipantListener { | 81 | ) : RTCEngine.Listener, ParticipantListener { |
| 80 | 82 | ||
| 81 | private lateinit var coroutineScope: CoroutineScope | 83 | private lateinit var coroutineScope: CoroutineScope |
| @@ -211,11 +213,6 @@ constructor( | @@ -211,11 +213,6 @@ constructor( | ||
| 211 | var e2eeOptions: E2EEOptions? = null | 213 | var e2eeOptions: E2EEOptions? = null |
| 212 | 214 | ||
| 213 | /** | 215 | /** |
| 214 | - * @see external audio processing options | ||
| 215 | - */ | ||
| 216 | - var audioProcessorOptions: AudioProcessorOptions? = null | ||
| 217 | - | ||
| 218 | - /** | ||
| 219 | * Default options to use when creating an audio track. | 216 | * Default options to use when creating an audio track. |
| 220 | */ | 217 | */ |
| 221 | var audioTrackCaptureDefaults: LocalAudioTrackOptions by defaultsManager::audioTrackCaptureDefaults | 218 | var audioTrackCaptureDefaults: LocalAudioTrackOptions by defaultsManager::audioTrackCaptureDefaults |
| @@ -370,10 +367,12 @@ constructor( | @@ -370,10 +367,12 @@ constructor( | ||
| 370 | val connectJob = coroutineScope.launch( | 367 | val connectJob = coroutineScope.launch( |
| 371 | ioDispatcher + emptyCoroutineExceptionHandler, | 368 | ioDispatcher + emptyCoroutineExceptionHandler, |
| 372 | ) { | 369 | ) { |
| 370 | + if (audioProcessingController is AuthedAudioProcessingController) { | ||
| 371 | + audioProcessingController.authenticate(url, token) | ||
| 372 | + } | ||
| 373 | engine.join(url, token, options, roomOptions) | 373 | engine.join(url, token, options, roomOptions) |
| 374 | - networkCallbackManager.registerCallback() | ||
| 375 | - | ||
| 376 | ensureActive() | 374 | ensureActive() |
| 375 | + networkCallbackManager.registerCallback() | ||
| 377 | if (options.audio) { | 376 | if (options.audio) { |
| 378 | val audioTrack = localParticipant.createAudioTrack() | 377 | val audioTrack = localParticipant.createAudioTrack() |
| 379 | localParticipant.publishAudioTrack(audioTrack) | 378 | localParticipant.publishAudioTrack(audioTrack) |
| 1 | +/* | ||
| 2 | + * Copyright 2024 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package io.livekit.android.room.provisions | ||
| 18 | + | ||
| 19 | +import livekit.org.webrtc.EglBase | ||
| 20 | +import javax.inject.Inject | ||
| 21 | +import javax.inject.Provider | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * Provides access to objects used internally. | ||
| 25 | + */ | ||
| 26 | +// Note, to avoid accidentally instantiating an unneeded object, | ||
| 27 | +// only store Providers here. | ||
| 28 | +// | ||
| 29 | +// Additionally, the provided objects should only be singletons. | ||
| 30 | +// Otherwise the created objects may not be the one used internally. | ||
| 31 | +class LKObjects | ||
| 32 | +@Inject | ||
| 33 | +constructor( | ||
| 34 | + private val eglBaseProvider: Provider<EglBase>, | ||
| 35 | +) { | ||
| 36 | + val eglBase: EglBase | ||
| 37 | + get() = eglBaseProvider.get() | ||
| 38 | +} |
| @@ -16,21 +16,21 @@ | @@ -16,21 +16,21 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.webrtc | 17 | package io.livekit.android.webrtc |
| 18 | 18 | ||
| 19 | -import io.livekit.android.audio.AudioProcessingController | ||
| 20 | import io.livekit.android.audio.AudioProcessorInterface | 19 | import io.livekit.android.audio.AudioProcessorInterface |
| 21 | import io.livekit.android.audio.AudioProcessorOptions | 20 | import io.livekit.android.audio.AudioProcessorOptions |
| 21 | +import io.livekit.android.audio.AuthedAudioProcessingController | ||
| 22 | +import io.livekit.android.audio.authenticateProcessors | ||
| 22 | import livekit.org.webrtc.AudioProcessingFactory | 23 | import livekit.org.webrtc.AudioProcessingFactory |
| 23 | import livekit.org.webrtc.ExternalAudioProcessingFactory | 24 | import livekit.org.webrtc.ExternalAudioProcessingFactory |
| 24 | import java.nio.ByteBuffer | 25 | import java.nio.ByteBuffer |
| 25 | 26 | ||
| 26 | -class CustomAudioProcessingFactory(private val audioProcessorOptions: AudioProcessorOptions) : AudioProcessingController { | 27 | +class CustomAudioProcessingFactory(private var audioProcessorOptions: AudioProcessorOptions) : AuthedAudioProcessingController { |
| 27 | 28 | ||
| 28 | private val externalAudioProcessor = ExternalAudioProcessingFactory() | 29 | private val externalAudioProcessor = ExternalAudioProcessingFactory() |
| 29 | 30 | ||
| 30 | init { | 31 | init { |
| 31 | if (audioProcessorOptions.capturePostProcessor != null) { | 32 | if (audioProcessorOptions.capturePostProcessor != null) { |
| 32 | setCapturePostProcessing(audioProcessorOptions.capturePostProcessor) | 33 | setCapturePostProcessing(audioProcessorOptions.capturePostProcessor) |
| 33 | - setBypassForCapturePostProcessing(audioProcessorOptions.capturePostBypass) | ||
| 34 | } else { | 34 | } else { |
| 35 | setCapturePostProcessing(null) | 35 | setCapturePostProcessing(null) |
| 36 | setBypassForCapturePostProcessing(false) | 36 | setBypassForCapturePostProcessing(false) |
| @@ -48,23 +48,31 @@ class CustomAudioProcessingFactory(private val audioProcessorOptions: AudioProce | @@ -48,23 +48,31 @@ class CustomAudioProcessingFactory(private val audioProcessorOptions: AudioProce | ||
| 48 | return externalAudioProcessor | 48 | return externalAudioProcessor |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | + override fun authenticate(url: String, token: String) { | ||
| 52 | + audioProcessorOptions.authenticateProcessors(url, token) | ||
| 53 | + } | ||
| 54 | + | ||
| 51 | override fun setCapturePostProcessing(processing: AudioProcessorInterface?) { | 55 | override fun setCapturePostProcessing(processing: AudioProcessorInterface?) { |
| 56 | + audioProcessorOptions = audioProcessorOptions.copy(capturePostProcessor = processing) | ||
| 52 | externalAudioProcessor.setCapturePostProcessing( | 57 | externalAudioProcessor.setCapturePostProcessing( |
| 53 | processing.toAudioProcessing(), | 58 | processing.toAudioProcessing(), |
| 54 | ) | 59 | ) |
| 55 | } | 60 | } |
| 56 | 61 | ||
| 57 | override fun setBypassForCapturePostProcessing(bypass: Boolean) { | 62 | override fun setBypassForCapturePostProcessing(bypass: Boolean) { |
| 63 | + audioProcessorOptions = audioProcessorOptions.copy(capturePostBypass = bypass) | ||
| 58 | externalAudioProcessor.setBypassFlagForCapturePost(bypass) | 64 | externalAudioProcessor.setBypassFlagForCapturePost(bypass) |
| 59 | } | 65 | } |
| 60 | 66 | ||
| 61 | override fun setRenderPreProcessing(processing: AudioProcessorInterface?) { | 67 | override fun setRenderPreProcessing(processing: AudioProcessorInterface?) { |
| 68 | + audioProcessorOptions = audioProcessorOptions.copy(renderPreProcessor = processing) | ||
| 62 | externalAudioProcessor.setRenderPreProcessing( | 69 | externalAudioProcessor.setRenderPreProcessing( |
| 63 | processing.toAudioProcessing(), | 70 | processing.toAudioProcessing(), |
| 64 | ) | 71 | ) |
| 65 | } | 72 | } |
| 66 | 73 | ||
| 67 | override fun setBypassForRenderPreProcessing(bypass: Boolean) { | 74 | override fun setBypassForRenderPreProcessing(bypass: Boolean) { |
| 75 | + audioProcessorOptions = audioProcessorOptions.copy(renderPreBypass = bypass) | ||
| 68 | externalAudioProcessor.setBypassFlagForRenderPre(bypass) | 76 | externalAudioProcessor.setBypassFlagForRenderPre(bypass) |
| 69 | } | 77 | } |
| 70 | 78 | ||
| @@ -76,7 +84,9 @@ class CustomAudioProcessingFactory(private val audioProcessorOptions: AudioProce | @@ -76,7 +84,9 @@ class CustomAudioProcessingFactory(private val audioProcessorOptions: AudioProce | ||
| 76 | } | 84 | } |
| 77 | 85 | ||
| 78 | override fun reset(newRate: Int) { | 86 | override fun reset(newRate: Int) { |
| 79 | - audioProcessing?.resetAudioProcessing(newRate) | 87 | + // bug in webrtc lib causes newRate to be off by a factor of 10 |
| 88 | + // TODO: remove divide by 10 when updating libwebrtc | ||
| 89 | + audioProcessing?.resetAudioProcessing(newRate / 10) | ||
| 80 | } | 90 | } |
| 81 | 91 | ||
| 82 | override fun process(numBands: Int, numFrames: Int, buffer: ByteBuffer?) { | 92 | override fun process(numBands: Int, numFrames: Int, buffer: ByteBuffer?) { |
| 1 | +/* | ||
| 2 | + * Copyright 2024 LiveKit, Inc. | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package io.livekit.android.mock | ||
| 18 | + | ||
| 19 | +import io.livekit.android.room.provisions.LKObjects | ||
| 20 | + | ||
| 21 | +object MockLKObjects { | ||
| 22 | + fun get(): LKObjects { | ||
| 23 | + return LKObjects( | ||
| 24 | + eglBaseProvider = { MockEglBase() }, | ||
| 25 | + ) | ||
| 26 | + } | ||
| 27 | +} |
| @@ -102,6 +102,7 @@ class RoomTest { | @@ -102,6 +102,7 @@ class RoomTest { | ||
| 102 | e2EEManagerFactory = e2EEManagerFactory, | 102 | e2EEManagerFactory = e2EEManagerFactory, |
| 103 | communicationWorkaround = NoopCommunicationWorkaround(), | 103 | communicationWorkaround = NoopCommunicationWorkaround(), |
| 104 | audioProcessingController = MockAudioProcessingController(), | 104 | audioProcessingController = MockAudioProcessingController(), |
| 105 | + lkObjects = MockLKObjects.get(), | ||
| 105 | ) | 106 | ) |
| 106 | } | 107 | } |
| 107 | 108 |
| @@ -227,7 +227,6 @@ class CallViewModel( | @@ -227,7 +227,6 @@ class CallViewModel( | ||
| 227 | private suspend fun connectToRoom() { | 227 | private suspend fun connectToRoom() { |
| 228 | try { | 228 | try { |
| 229 | room.e2eeOptions = getE2EEOptions() | 229 | room.e2eeOptions = getE2EEOptions() |
| 230 | - room.audioProcessorOptions = audioProcessorOptions | ||
| 231 | room.connect( | 230 | room.connect( |
| 232 | url = url, | 231 | url = url, |
| 233 | token = token, | 232 | token = token, |
-
请 注册 或 登录 后发表评论