Committed by
GitHub
Add some audio convenience functions (#408)
* Add some audio convenience functions * Fix tests
正在显示
7 个修改的文件
包含
108 行增加
和
9 行删除
| @@ -57,6 +57,7 @@ import kotlinx.serialization.Serializable | @@ -57,6 +57,7 @@ import kotlinx.serialization.Serializable | ||
| 57 | import livekit.LivekitModels | 57 | import livekit.LivekitModels |
| 58 | import livekit.LivekitRtc | 58 | import livekit.LivekitRtc |
| 59 | import livekit.org.webrtc.* | 59 | import livekit.org.webrtc.* |
| 60 | +import livekit.org.webrtc.audio.AudioDeviceModule | ||
| 60 | import javax.inject.Named | 61 | import javax.inject.Named |
| 61 | 62 | ||
| 62 | class Room | 63 | class Room |
| @@ -78,6 +79,7 @@ constructor( | @@ -78,6 +79,7 @@ constructor( | ||
| 78 | val audioProcessingController: AudioProcessingController, | 79 | val audioProcessingController: AudioProcessingController, |
| 79 | val lkObjects: LKObjects, | 80 | val lkObjects: LKObjects, |
| 80 | networkCallbackManagerFactory: NetworkCallbackManagerFactory, | 81 | networkCallbackManagerFactory: NetworkCallbackManagerFactory, |
| 82 | + private val audioDeviceModule: AudioDeviceModule, | ||
| 81 | ) : RTCEngine.Listener, ParticipantListener { | 83 | ) : RTCEngine.Listener, ParticipantListener { |
| 82 | 84 | ||
| 83 | private lateinit var coroutineScope: CoroutineScope | 85 | private lateinit var coroutineScope: CoroutineScope |
| @@ -778,6 +780,30 @@ constructor( | @@ -778,6 +780,30 @@ constructor( | ||
| 778 | fun create(context: Context): Room | 780 | fun create(context: Context): Room |
| 779 | } | 781 | } |
| 780 | 782 | ||
| 783 | + // ------------------------------------- General Utility functions -------------------------------------// | ||
| 784 | + | ||
| 785 | + /** | ||
| 786 | + * Control muting/unmuting all audio output. | ||
| 787 | + * | ||
| 788 | + * Note: using this mute will only mute local audio output, and will still receive the data from | ||
| 789 | + * remote audio tracks. Consider turning off [ConnectOptions.autoSubscribe] and manually unsubscribing | ||
| 790 | + * if you need more fine-grained control over the data usage. | ||
| 791 | + **/ | ||
| 792 | + fun setSpeakerMute(muted: Boolean) { | ||
| 793 | + audioDeviceModule.setSpeakerMute(muted) | ||
| 794 | + } | ||
| 795 | + | ||
| 796 | + /** | ||
| 797 | + * Control muting/unmuting the audio input. | ||
| 798 | + * | ||
| 799 | + * Note: using this mute will only zero the microphone audio data, and will still send data to | ||
| 800 | + * remote participants (although they will not hear anything). Consider using [TrackPublication.muted] | ||
| 801 | + * for more fine-grained control over the data usage. | ||
| 802 | + */ | ||
| 803 | + fun setMicrophoneMute(muted: Boolean) { | ||
| 804 | + audioDeviceModule.setMicrophoneMute(muted) | ||
| 805 | + } | ||
| 806 | + | ||
| 781 | // ------------------------------------- NetworkCallback -------------------------------------// | 807 | // ------------------------------------- NetworkCallback -------------------------------------// |
| 782 | private val networkCallbackManager = networkCallbackManagerFactory.invoke( | 808 | private val networkCallbackManager = networkCallbackManagerFactory.invoke( |
| 783 | object : NetworkCallback() { | 809 | object : NetworkCallback() { |
| @@ -26,6 +26,5 @@ abstract class AudioTrack( | @@ -26,6 +26,5 @@ abstract class AudioTrack( | ||
| 26 | /** | 26 | /** |
| 27 | * The underlying WebRTC audio track. | 27 | * The underlying WebRTC audio track. |
| 28 | */ | 28 | */ |
| 29 | - override val rtcTrack: AudioTrack | ||
| 30 | -) : | ||
| 31 | - Track(name, Kind.AUDIO, rtcTrack) | 29 | + override val rtcTrack: AudioTrack, |
| 30 | +) : Track(name, Kind.AUDIO, rtcTrack) |
| @@ -20,17 +20,18 @@ import android.Manifest | @@ -20,17 +20,18 @@ import android.Manifest | ||
| 20 | import android.content.Context | 20 | import android.content.Context |
| 21 | import android.content.pm.PackageManager | 21 | import android.content.pm.PackageManager |
| 22 | import androidx.core.content.ContextCompat | 22 | import androidx.core.content.ContextCompat |
| 23 | +import io.livekit.android.room.participant.LocalParticipant | ||
| 23 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread | 24 | import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread |
| 24 | import livekit.org.webrtc.MediaConstraints | 25 | import livekit.org.webrtc.MediaConstraints |
| 25 | import livekit.org.webrtc.PeerConnectionFactory | 26 | import livekit.org.webrtc.PeerConnectionFactory |
| 26 | import livekit.org.webrtc.RtpSender | 27 | import livekit.org.webrtc.RtpSender |
| 27 | import livekit.org.webrtc.RtpTransceiver | 28 | import livekit.org.webrtc.RtpTransceiver |
| 28 | -import java.util.* | 29 | +import java.util.UUID |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
| 31 | * Represents a local audio track (generally using the microphone as input). | 32 | * Represents a local audio track (generally using the microphone as input). |
| 32 | * | 33 | * |
| 33 | - * This class should not be constructed directly, but rather through [LocalParticipant] | 34 | + * This class should not be constructed directly, but rather through [LocalParticipant.createAudioTrack]. |
| 34 | */ | 35 | */ |
| 35 | class LocalAudioTrack( | 36 | class LocalAudioTrack( |
| 36 | name: String, | 37 | name: String, |
| @@ -49,4 +49,18 @@ class RemoteAudioTrack( | @@ -49,4 +49,18 @@ class RemoteAudioTrack( | ||
| 49 | rtcTrack.removeSink(sink) | 49 | rtcTrack.removeSink(sink) |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * Sets the volume. | ||
| 55 | + * | ||
| 56 | + * @param volume a gain value in the range 0 to 10. | ||
| 57 | + * * 0 will mute the track | ||
| 58 | + * * 1 will play the track normally | ||
| 59 | + * * values greater than 1 will boost the volume of the track. | ||
| 60 | + */ | ||
| 61 | + fun setVolume(volume: Double) { | ||
| 62 | + executeBlockingOnRTCThread { | ||
| 63 | + rtcTrack.setVolume(volume) | ||
| 64 | + } | ||
| 65 | + } | ||
| 52 | } | 66 | } |
| 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.test.mock | ||
| 18 | + | ||
| 19 | +import livekit.org.webrtc.audio.AudioDeviceModule | ||
| 20 | + | ||
| 21 | +class MockAudioDeviceModule : AudioDeviceModule { | ||
| 22 | + override fun getNativeAudioDeviceModulePointer(): Long { | ||
| 23 | + return 1 | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + override fun release() { | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + override fun setSpeakerMute(muted: Boolean) { | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + override fun setMicrophoneMute(muted: Boolean) { | ||
| 33 | + } | ||
| 34 | +} |
| @@ -23,9 +23,15 @@ import dagger.Provides | @@ -23,9 +23,15 @@ import dagger.Provides | ||
| 23 | import io.livekit.android.audio.AudioProcessingController | 23 | import io.livekit.android.audio.AudioProcessingController |
| 24 | import io.livekit.android.dagger.CapabilitiesGetter | 24 | import io.livekit.android.dagger.CapabilitiesGetter |
| 25 | import io.livekit.android.dagger.InjectionNames | 25 | import io.livekit.android.dagger.InjectionNames |
| 26 | +import io.livekit.android.test.mock.MockAudioDeviceModule | ||
| 26 | import io.livekit.android.test.mock.MockAudioProcessingController | 27 | import io.livekit.android.test.mock.MockAudioProcessingController |
| 27 | import io.livekit.android.test.mock.MockEglBase | 28 | import io.livekit.android.test.mock.MockEglBase |
| 28 | -import livekit.org.webrtc.* | 29 | +import livekit.org.webrtc.EglBase |
| 30 | +import livekit.org.webrtc.MediaStreamTrack | ||
| 31 | +import livekit.org.webrtc.MockPeerConnectionFactory | ||
| 32 | +import livekit.org.webrtc.PeerConnectionFactory | ||
| 33 | +import livekit.org.webrtc.WebRTCInitializer | ||
| 34 | +import livekit.org.webrtc.audio.AudioDeviceModule | ||
| 29 | import javax.inject.Named | 35 | import javax.inject.Named |
| 30 | import javax.inject.Singleton | 36 | import javax.inject.Singleton |
| 31 | 37 | ||
| @@ -48,6 +54,12 @@ object TestRTCModule { | @@ -48,6 +54,12 @@ object TestRTCModule { | ||
| 48 | 54 | ||
| 49 | @Provides | 55 | @Provides |
| 50 | @Singleton | 56 | @Singleton |
| 57 | + fun audioDeviceModule(): AudioDeviceModule { | ||
| 58 | + return MockAudioDeviceModule() | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + @Provides | ||
| 62 | + @Singleton | ||
| 51 | fun peerConnectionFactory( | 63 | fun peerConnectionFactory( |
| 52 | appContext: Context, | 64 | appContext: Context, |
| 53 | ): PeerConnectionFactory { | 65 | ): PeerConnectionFactory { |
| @@ -23,13 +23,17 @@ import androidx.test.core.app.ApplicationProvider | @@ -23,13 +23,17 @@ import androidx.test.core.app.ApplicationProvider | ||
| 23 | import io.livekit.android.audio.NoAudioHandler | 23 | import io.livekit.android.audio.NoAudioHandler |
| 24 | import io.livekit.android.audio.NoopCommunicationWorkaround | 24 | import io.livekit.android.audio.NoopCommunicationWorkaround |
| 25 | import io.livekit.android.e2ee.E2EEManager | 25 | import io.livekit.android.e2ee.E2EEManager |
| 26 | -import io.livekit.android.events.* | 26 | +import io.livekit.android.events.DisconnectReason |
| 27 | +import io.livekit.android.events.EventListenable | ||
| 28 | +import io.livekit.android.events.ParticipantEvent | ||
| 29 | +import io.livekit.android.events.RoomEvent | ||
| 27 | import io.livekit.android.memory.CloseableManager | 30 | import io.livekit.android.memory.CloseableManager |
| 28 | import io.livekit.android.room.network.NetworkCallbackManagerImpl | 31 | import io.livekit.android.room.network.NetworkCallbackManagerImpl |
| 29 | import io.livekit.android.room.participant.LocalParticipant | 32 | import io.livekit.android.room.participant.LocalParticipant |
| 30 | import io.livekit.android.test.assert.assertIsClassList | 33 | import io.livekit.android.test.assert.assertIsClassList |
| 31 | import io.livekit.android.test.coroutines.TestCoroutineRule | 34 | import io.livekit.android.test.coroutines.TestCoroutineRule |
| 32 | import io.livekit.android.test.events.EventCollector | 35 | import io.livekit.android.test.events.EventCollector |
| 36 | +import io.livekit.android.test.mock.MockAudioDeviceModule | ||
| 33 | import io.livekit.android.test.mock.MockAudioProcessingController | 37 | import io.livekit.android.test.mock.MockAudioProcessingController |
| 34 | import io.livekit.android.test.mock.MockEglBase | 38 | import io.livekit.android.test.mock.MockEglBase |
| 35 | import io.livekit.android.test.mock.MockLKObjects | 39 | import io.livekit.android.test.mock.MockLKObjects |
| @@ -43,7 +47,10 @@ import kotlinx.coroutines.test.advanceUntilIdle | @@ -43,7 +47,10 @@ import kotlinx.coroutines.test.advanceUntilIdle | ||
| 43 | import kotlinx.coroutines.test.runTest | 47 | import kotlinx.coroutines.test.runTest |
| 44 | import livekit.LivekitRtc.JoinResponse | 48 | import livekit.LivekitRtc.JoinResponse |
| 45 | import livekit.org.webrtc.EglBase | 49 | import livekit.org.webrtc.EglBase |
| 46 | -import org.junit.Assert.* | 50 | +import org.junit.Assert.assertEquals |
| 51 | +import org.junit.Assert.assertFalse | ||
| 52 | +import org.junit.Assert.assertNull | ||
| 53 | +import org.junit.Assert.assertTrue | ||
| 47 | import org.junit.Before | 54 | import org.junit.Before |
| 48 | import org.junit.Rule | 55 | import org.junit.Rule |
| 49 | import org.junit.Test | 56 | import org.junit.Test |
| @@ -51,7 +58,12 @@ import org.junit.runner.RunWith | @@ -51,7 +58,12 @@ import org.junit.runner.RunWith | ||
| 51 | import org.mockito.Mock | 58 | import org.mockito.Mock |
| 52 | import org.mockito.Mockito | 59 | import org.mockito.Mockito |
| 53 | import org.mockito.junit.MockitoJUnit | 60 | import org.mockito.junit.MockitoJUnit |
| 54 | -import org.mockito.kotlin.* | 61 | +import org.mockito.kotlin.any |
| 62 | +import org.mockito.kotlin.anyOrNull | ||
| 63 | +import org.mockito.kotlin.doReturn | ||
| 64 | +import org.mockito.kotlin.doSuspendableAnswer | ||
| 65 | +import org.mockito.kotlin.stub | ||
| 66 | +import org.mockito.kotlin.whenever | ||
| 55 | import org.robolectric.RobolectricTestRunner | 67 | import org.robolectric.RobolectricTestRunner |
| 56 | 68 | ||
| 57 | @ExperimentalCoroutinesApi | 69 | @ExperimentalCoroutinesApi |
| @@ -112,6 +124,7 @@ class RoomTest { | @@ -112,6 +124,7 @@ class RoomTest { | ||
| 112 | networkCallbackManagerFactory = { networkCallback: NetworkCallback -> | 124 | networkCallbackManagerFactory = { networkCallback: NetworkCallback -> |
| 113 | NetworkCallbackManagerImpl(networkCallback, networkCallbackRegistry) | 125 | NetworkCallbackManagerImpl(networkCallback, networkCallbackRegistry) |
| 114 | }, | 126 | }, |
| 127 | + audioDeviceModule = MockAudioDeviceModule(), | ||
| 115 | ) | 128 | ) |
| 116 | } | 129 | } |
| 117 | 130 |
-
请 注册 或 登录 后发表评论