davidliu
Committed by GitHub

Add some audio convenience functions (#408)

* Add some audio convenience functions

* Fix tests
... ... @@ -57,6 +57,7 @@ import kotlinx.serialization.Serializable
import livekit.LivekitModels
import livekit.LivekitRtc
import livekit.org.webrtc.*
import livekit.org.webrtc.audio.AudioDeviceModule
import javax.inject.Named
class Room
... ... @@ -78,6 +79,7 @@ constructor(
val audioProcessingController: AudioProcessingController,
val lkObjects: LKObjects,
networkCallbackManagerFactory: NetworkCallbackManagerFactory,
private val audioDeviceModule: AudioDeviceModule,
) : RTCEngine.Listener, ParticipantListener {
private lateinit var coroutineScope: CoroutineScope
... ... @@ -778,6 +780,30 @@ constructor(
fun create(context: Context): Room
}
// ------------------------------------- General Utility functions -------------------------------------//
/**
* Control muting/unmuting all audio output.
*
* Note: using this mute will only mute local audio output, and will still receive the data from
* remote audio tracks. Consider turning off [ConnectOptions.autoSubscribe] and manually unsubscribing
* if you need more fine-grained control over the data usage.
**/
fun setSpeakerMute(muted: Boolean) {
audioDeviceModule.setSpeakerMute(muted)
}
/**
* Control muting/unmuting the audio input.
*
* Note: using this mute will only zero the microphone audio data, and will still send data to
* remote participants (although they will not hear anything). Consider using [TrackPublication.muted]
* for more fine-grained control over the data usage.
*/
fun setMicrophoneMute(muted: Boolean) {
audioDeviceModule.setMicrophoneMute(muted)
}
// ------------------------------------- NetworkCallback -------------------------------------//
private val networkCallbackManager = networkCallbackManagerFactory.invoke(
object : NetworkCallback() {
... ...
... ... @@ -26,6 +26,5 @@ abstract class AudioTrack(
/**
* The underlying WebRTC audio track.
*/
override val rtcTrack: AudioTrack
) :
Track(name, Kind.AUDIO, rtcTrack)
override val rtcTrack: AudioTrack,
) : Track(name, Kind.AUDIO, rtcTrack)
... ...
... ... @@ -20,17 +20,18 @@ import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.content.ContextCompat
import io.livekit.android.room.participant.LocalParticipant
import io.livekit.android.webrtc.peerconnection.executeBlockingOnRTCThread
import livekit.org.webrtc.MediaConstraints
import livekit.org.webrtc.PeerConnectionFactory
import livekit.org.webrtc.RtpSender
import livekit.org.webrtc.RtpTransceiver
import java.util.*
import java.util.UUID
/**
* Represents a local audio track (generally using the microphone as input).
*
* This class should not be constructed directly, but rather through [LocalParticipant]
* This class should not be constructed directly, but rather through [LocalParticipant.createAudioTrack].
*/
class LocalAudioTrack(
name: String,
... ...
... ... @@ -49,4 +49,18 @@ class RemoteAudioTrack(
rtcTrack.removeSink(sink)
}
}
/**
* Sets the volume.
*
* @param volume a gain value in the range 0 to 10.
* * 0 will mute the track
* * 1 will play the track normally
* * values greater than 1 will boost the volume of the track.
*/
fun setVolume(volume: Double) {
executeBlockingOnRTCThread {
rtcTrack.setVolume(volume)
}
}
}
... ...
/*
* Copyright 2024 LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.livekit.android.test.mock
import livekit.org.webrtc.audio.AudioDeviceModule
class MockAudioDeviceModule : AudioDeviceModule {
override fun getNativeAudioDeviceModulePointer(): Long {
return 1
}
override fun release() {
}
override fun setSpeakerMute(muted: Boolean) {
}
override fun setMicrophoneMute(muted: Boolean) {
}
}
... ...
... ... @@ -23,9 +23,15 @@ import dagger.Provides
import io.livekit.android.audio.AudioProcessingController
import io.livekit.android.dagger.CapabilitiesGetter
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.test.mock.MockAudioDeviceModule
import io.livekit.android.test.mock.MockAudioProcessingController
import io.livekit.android.test.mock.MockEglBase
import livekit.org.webrtc.*
import livekit.org.webrtc.EglBase
import livekit.org.webrtc.MediaStreamTrack
import livekit.org.webrtc.MockPeerConnectionFactory
import livekit.org.webrtc.PeerConnectionFactory
import livekit.org.webrtc.WebRTCInitializer
import livekit.org.webrtc.audio.AudioDeviceModule
import javax.inject.Named
import javax.inject.Singleton
... ... @@ -48,6 +54,12 @@ object TestRTCModule {
@Provides
@Singleton
fun audioDeviceModule(): AudioDeviceModule {
return MockAudioDeviceModule()
}
@Provides
@Singleton
fun peerConnectionFactory(
appContext: Context,
): PeerConnectionFactory {
... ...
... ... @@ -23,13 +23,17 @@ import androidx.test.core.app.ApplicationProvider
import io.livekit.android.audio.NoAudioHandler
import io.livekit.android.audio.NoopCommunicationWorkaround
import io.livekit.android.e2ee.E2EEManager
import io.livekit.android.events.*
import io.livekit.android.events.DisconnectReason
import io.livekit.android.events.EventListenable
import io.livekit.android.events.ParticipantEvent
import io.livekit.android.events.RoomEvent
import io.livekit.android.memory.CloseableManager
import io.livekit.android.room.network.NetworkCallbackManagerImpl
import io.livekit.android.room.participant.LocalParticipant
import io.livekit.android.test.assert.assertIsClassList
import io.livekit.android.test.coroutines.TestCoroutineRule
import io.livekit.android.test.events.EventCollector
import io.livekit.android.test.mock.MockAudioDeviceModule
import io.livekit.android.test.mock.MockAudioProcessingController
import io.livekit.android.test.mock.MockEglBase
import io.livekit.android.test.mock.MockLKObjects
... ... @@ -43,7 +47,10 @@ import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import livekit.LivekitRtc.JoinResponse
import livekit.org.webrtc.EglBase
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
... ... @@ -51,7 +58,12 @@ import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.*
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doSuspendableAnswer
import org.mockito.kotlin.stub
import org.mockito.kotlin.whenever
import org.robolectric.RobolectricTestRunner
@ExperimentalCoroutinesApi
... ... @@ -112,6 +124,7 @@ class RoomTest {
networkCallbackManagerFactory = { networkCallback: NetworkCallback ->
NetworkCallbackManagerImpl(networkCallback, networkCallbackRegistry)
},
audioDeviceModule = MockAudioDeviceModule(),
)
}
... ...