Committed by
GitHub
Implement LocalAudioTrack.addSink (#516)
* Implement LocalAudioTrack.addSink * test fix * fix tests * fix tests
正在显示
15 个修改的文件
包含
223 行增加
和
113 行删除
.changeset/bright-pillows-pay.md
0 → 100644
livekit-android-sdk/src/main/java/io/livekit/android/audio/AudioRecordSamplesDispatcher.kt
0 → 100644
| 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.audio | ||
| 18 | + | ||
| 19 | +import android.media.AudioFormat | ||
| 20 | +import android.os.SystemClock | ||
| 21 | +import livekit.org.webrtc.AudioTrackSink | ||
| 22 | +import livekit.org.webrtc.audio.JavaAudioDeviceModule | ||
| 23 | +import livekit.org.webrtc.audio.JavaAudioDeviceModule.SamplesReadyCallback | ||
| 24 | +import java.nio.ByteBuffer | ||
| 25 | + | ||
| 26 | +class AudioRecordSamplesDispatcher : SamplesReadyCallback { | ||
| 27 | + | ||
| 28 | + private val sinks = mutableSetOf<AudioTrackSink>() | ||
| 29 | + | ||
| 30 | + @Synchronized | ||
| 31 | + fun registerSink(sink: AudioTrackSink) { | ||
| 32 | + sinks.add(sink) | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Synchronized | ||
| 36 | + fun unregisterSink(sink: AudioTrackSink) { | ||
| 37 | + sinks.remove(sink) | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + // Reference from Android code, AudioFormat.getBytesPerSample. BitPerSample / 8 | ||
| 41 | + // Default audio data format is PCM 16 bits per sample. | ||
| 42 | + // Guaranteed to be supported by all devices | ||
| 43 | + private fun getBytesPerSample(audioFormat: Int): Int { | ||
| 44 | + return when (audioFormat) { | ||
| 45 | + AudioFormat.ENCODING_PCM_8BIT -> 1 | ||
| 46 | + AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_IEC61937, AudioFormat.ENCODING_DEFAULT -> 2 | ||
| 47 | + AudioFormat.ENCODING_PCM_FLOAT -> 4 | ||
| 48 | + AudioFormat.ENCODING_INVALID -> throw IllegalArgumentException("Bad audio format $audioFormat") | ||
| 49 | + else -> throw IllegalArgumentException("Bad audio format $audioFormat") | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + @Synchronized | ||
| 54 | + override fun onWebRtcAudioRecordSamplesReady(samples: JavaAudioDeviceModule.AudioSamples) { | ||
| 55 | + val bitsPerSample = getBytesPerSample(samples.audioFormat) * 8 | ||
| 56 | + val numFrames = samples.sampleRate / 100 // 10ms worth of samples. | ||
| 57 | + val timestamp = SystemClock.elapsedRealtime() | ||
| 58 | + for (sink in sinks) { | ||
| 59 | + val byteBuffer = ByteBuffer.wrap(samples.data) | ||
| 60 | + sink.onData( | ||
| 61 | + byteBuffer, | ||
| 62 | + bitsPerSample, | ||
| 63 | + samples.sampleRate, | ||
| 64 | + samples.channelCount, | ||
| 65 | + numFrames, | ||
| 66 | + timestamp, | ||
| 67 | + ) | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | +} |
| @@ -47,6 +47,8 @@ object InjectionNames { | @@ -47,6 +47,8 @@ object InjectionNames { | ||
| 47 | 47 | ||
| 48 | const val LIB_WEBRTC_INITIALIZATION = "lib_webrtc_initialization" | 48 | const val LIB_WEBRTC_INITIALIZATION = "lib_webrtc_initialization" |
| 49 | 49 | ||
| 50 | + const val LOCAL_AUDIO_RECORD_SAMPLES_DISPATCHER = "local_audio_record_samples_dispatcher" | ||
| 51 | + | ||
| 50 | // Overrides | 52 | // Overrides |
| 51 | const val OVERRIDE_OKHTTP = "override_okhttp" | 53 | const val OVERRIDE_OKHTTP = "override_okhttp" |
| 52 | const val OVERRIDE_AUDIO_DEVICE_MODULE = "override_audio_device_module" | 54 | const val OVERRIDE_AUDIO_DEVICE_MODULE = "override_audio_device_module" |
| @@ -27,6 +27,7 @@ import dagger.Provides | @@ -27,6 +27,7 @@ import dagger.Provides | ||
| 27 | import io.livekit.android.LiveKit | 27 | import io.livekit.android.LiveKit |
| 28 | import io.livekit.android.audio.AudioProcessingController | 28 | import io.livekit.android.audio.AudioProcessingController |
| 29 | import io.livekit.android.audio.AudioProcessorOptions | 29 | import io.livekit.android.audio.AudioProcessorOptions |
| 30 | +import io.livekit.android.audio.AudioRecordSamplesDispatcher | ||
| 30 | import io.livekit.android.audio.CommunicationWorkaround | 31 | import io.livekit.android.audio.CommunicationWorkaround |
| 31 | import io.livekit.android.memory.CloseableManager | 32 | import io.livekit.android.memory.CloseableManager |
| 32 | import io.livekit.android.util.LKLog | 33 | import io.livekit.android.util.LKLog |
| @@ -128,6 +129,13 @@ internal object RTCModule { | @@ -128,6 +129,13 @@ internal object RTCModule { | ||
| 128 | } | 129 | } |
| 129 | 130 | ||
| 130 | @Provides | 131 | @Provides |
| 132 | + @Named(InjectionNames.LOCAL_AUDIO_RECORD_SAMPLES_DISPATCHER) | ||
| 133 | + @Singleton | ||
| 134 | + fun localAudioSamplesDispatcher(): AudioRecordSamplesDispatcher { | ||
| 135 | + return AudioRecordSamplesDispatcher() | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + @Provides | ||
| 131 | @Singleton | 139 | @Singleton |
| 132 | @JvmSuppressWildcards | 140 | @JvmSuppressWildcards |
| 133 | fun audioModule( | 141 | fun audioModule( |
| @@ -141,6 +149,8 @@ internal object RTCModule { | @@ -141,6 +149,8 @@ internal object RTCModule { | ||
| 141 | appContext: Context, | 149 | appContext: Context, |
| 142 | closeableManager: CloseableManager, | 150 | closeableManager: CloseableManager, |
| 143 | communicationWorkaround: CommunicationWorkaround, | 151 | communicationWorkaround: CommunicationWorkaround, |
| 152 | + @Named(InjectionNames.LOCAL_AUDIO_RECORD_SAMPLES_DISPATCHER) | ||
| 153 | + audioRecordSamplesDispatcher: AudioRecordSamplesDispatcher, | ||
| 144 | ): AudioDeviceModule { | 154 | ): AudioDeviceModule { |
| 145 | if (audioDeviceModuleOverride != null) { | 155 | if (audioDeviceModuleOverride != null) { |
| 146 | return audioDeviceModuleOverride | 156 | return audioDeviceModuleOverride |
| @@ -215,6 +225,7 @@ internal object RTCModule { | @@ -215,6 +225,7 @@ internal object RTCModule { | ||
| 215 | .setAudioTrackErrorCallback(audioTrackErrorCallback) | 225 | .setAudioTrackErrorCallback(audioTrackErrorCallback) |
| 216 | .setAudioRecordStateCallback(audioRecordStateCallback) | 226 | .setAudioRecordStateCallback(audioRecordStateCallback) |
| 217 | .setAudioTrackStateCallback(audioTrackStateCallback) | 227 | .setAudioTrackStateCallback(audioTrackStateCallback) |
| 228 | + .setSamplesReadyCallback(audioRecordSamplesDispatcher) | ||
| 218 | // VOICE_COMMUNICATION needs to be used for echo cancelling. | 229 | // VOICE_COMMUNICATION needs to be used for echo cancelling. |
| 219 | .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) | 230 | .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) |
| 220 | .setAudioAttributes(audioOutputAttributes) | 231 | .setAudioAttributes(audioOutputAttributes) |
| @@ -17,6 +17,7 @@ | @@ -17,6 +17,7 @@ | ||
| 17 | package io.livekit.android.room.track | 17 | package io.livekit.android.room.track |
| 18 | 18 | ||
| 19 | import livekit.org.webrtc.AudioTrack | 19 | import livekit.org.webrtc.AudioTrack |
| 20 | +import livekit.org.webrtc.AudioTrackSink | ||
| 20 | 21 | ||
| 21 | /** | 22 | /** |
| 22 | * A class representing an audio track. | 23 | * A class representing an audio track. |
| @@ -27,4 +28,22 @@ abstract class AudioTrack( | @@ -27,4 +28,22 @@ abstract class AudioTrack( | ||
| 27 | * The underlying WebRTC audio track. | 28 | * The underlying WebRTC audio track. |
| 28 | */ | 29 | */ |
| 29 | override val rtcTrack: AudioTrack, | 30 | override val rtcTrack: AudioTrack, |
| 30 | -) : Track(name, Kind.AUDIO, rtcTrack) | 31 | +) : Track(name, Kind.AUDIO, rtcTrack) { |
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * Adds a sink that receives the audio bytes and related information | ||
| 35 | + * for this audio track. Repeated calls using the same sink will | ||
| 36 | + * only add the sink once. | ||
| 37 | + * | ||
| 38 | + * Implementations should copy the audio data into a local copy if they wish | ||
| 39 | + * to use the data after the [AudioTrackSink.onData] callback returns. | ||
| 40 | + * Long running processing of the received audio data should be done in a separate | ||
| 41 | + * thread, as doing so inline may block the audio thread. | ||
| 42 | + */ | ||
| 43 | + abstract fun addSink(sink: AudioTrackSink) | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * Removes a previously added sink. | ||
| 47 | + */ | ||
| 48 | + abstract fun removeSink(sink: AudioTrackSink) | ||
| 49 | +} |
| @@ -24,6 +24,7 @@ import dagger.assisted.Assisted | @@ -24,6 +24,7 @@ import dagger.assisted.Assisted | ||
| 24 | import dagger.assisted.AssistedFactory | 24 | import dagger.assisted.AssistedFactory |
| 25 | import dagger.assisted.AssistedInject | 25 | import dagger.assisted.AssistedInject |
| 26 | import io.livekit.android.audio.AudioProcessingController | 26 | import io.livekit.android.audio.AudioProcessingController |
| 27 | +import io.livekit.android.audio.AudioRecordSamplesDispatcher | ||
| 27 | import io.livekit.android.dagger.InjectionNames | 28 | import io.livekit.android.dagger.InjectionNames |
| 28 | import io.livekit.android.room.participant.LocalParticipant | 29 | import io.livekit.android.room.participant.LocalParticipant |
| 29 | import io.livekit.android.util.FlowObservable | 30 | import io.livekit.android.util.FlowObservable |
| @@ -37,10 +38,13 @@ import kotlinx.coroutines.flow.combine | @@ -37,10 +38,13 @@ import kotlinx.coroutines.flow.combine | ||
| 37 | import kotlinx.coroutines.flow.map | 38 | import kotlinx.coroutines.flow.map |
| 38 | import kotlinx.coroutines.flow.stateIn | 39 | import kotlinx.coroutines.flow.stateIn |
| 39 | import livekit.LivekitModels.AudioTrackFeature | 40 | import livekit.LivekitModels.AudioTrackFeature |
| 41 | +import livekit.org.webrtc.AudioTrackSink | ||
| 40 | import livekit.org.webrtc.MediaConstraints | 42 | import livekit.org.webrtc.MediaConstraints |
| 41 | import livekit.org.webrtc.PeerConnectionFactory | 43 | import livekit.org.webrtc.PeerConnectionFactory |
| 42 | import livekit.org.webrtc.RtpSender | 44 | import livekit.org.webrtc.RtpSender |
| 43 | import livekit.org.webrtc.RtpTransceiver | 45 | import livekit.org.webrtc.RtpTransceiver |
| 46 | +import livekit.org.webrtc.audio.AudioDeviceModule | ||
| 47 | +import livekit.org.webrtc.audio.JavaAudioDeviceModule | ||
| 44 | import java.util.UUID | 48 | import java.util.UUID |
| 45 | import javax.inject.Named | 49 | import javax.inject.Named |
| 46 | 50 | ||
| @@ -58,6 +62,8 @@ constructor( | @@ -58,6 +62,8 @@ constructor( | ||
| 58 | private val audioProcessingController: AudioProcessingController, | 62 | private val audioProcessingController: AudioProcessingController, |
| 59 | @Named(InjectionNames.DISPATCHER_DEFAULT) | 63 | @Named(InjectionNames.DISPATCHER_DEFAULT) |
| 60 | private val dispatcher: CoroutineDispatcher, | 64 | private val dispatcher: CoroutineDispatcher, |
| 65 | + @Named(InjectionNames.LOCAL_AUDIO_RECORD_SAMPLES_DISPATCHER) | ||
| 66 | + private val audioRecordSamplesDispatcher: AudioRecordSamplesDispatcher, | ||
| 61 | ) : AudioTrack(name, mediaTrack) { | 67 | ) : AudioTrack(name, mediaTrack) { |
| 62 | /** | 68 | /** |
| 63 | * To only be used for flow delegate scoping, and should not be cancelled. | 69 | * To only be used for flow delegate scoping, and should not be cancelled. |
| @@ -68,6 +74,30 @@ constructor( | @@ -68,6 +74,30 @@ constructor( | ||
| 68 | internal val sender: RtpSender? | 74 | internal val sender: RtpSender? |
| 69 | get() = transceiver?.sender | 75 | get() = transceiver?.sender |
| 70 | 76 | ||
| 77 | + private val trackSinks = mutableSetOf<AudioTrackSink>() | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * Note: This function relies on us setting | ||
| 81 | + * [JavaAudioDeviceModule.Builder.setSamplesReadyCallback]. | ||
| 82 | + * If you provide your own [AudioDeviceModule], or set your own | ||
| 83 | + * callback, your sink will not receive any audio data. | ||
| 84 | + * | ||
| 85 | + * @see AudioTrack.addSink | ||
| 86 | + */ | ||
| 87 | + override fun addSink(sink: AudioTrackSink) { | ||
| 88 | + synchronized(trackSinks) { | ||
| 89 | + trackSinks.add(sink) | ||
| 90 | + audioRecordSamplesDispatcher.registerSink(sink) | ||
| 91 | + } | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + override fun removeSink(sink: AudioTrackSink) { | ||
| 95 | + synchronized(trackSinks) { | ||
| 96 | + trackSinks.remove(sink) | ||
| 97 | + audioRecordSamplesDispatcher.unregisterSink(sink) | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + | ||
| 71 | /** | 101 | /** |
| 72 | * Changes can be observed by using [io.livekit.android.util.flow] | 102 | * Changes can be observed by using [io.livekit.android.util.flow] |
| 73 | */ | 103 | */ |
| @@ -107,6 +137,16 @@ constructor( | @@ -107,6 +137,16 @@ constructor( | ||
| 107 | return features | 137 | return features |
| 108 | } | 138 | } |
| 109 | 139 | ||
| 140 | + override fun dispose() { | ||
| 141 | + synchronized(trackSinks) { | ||
| 142 | + for (sink in trackSinks) { | ||
| 143 | + trackSinks.remove(sink) | ||
| 144 | + audioRecordSamplesDispatcher.unregisterSink(sink) | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + super.dispose() | ||
| 148 | + } | ||
| 149 | + | ||
| 110 | companion object { | 150 | companion object { |
| 111 | internal fun createTrack( | 151 | internal fun createTrack( |
| 112 | context: Context, | 152 | context: Context, |
| @@ -29,24 +29,13 @@ class RemoteAudioTrack( | @@ -29,24 +29,13 @@ class RemoteAudioTrack( | ||
| 29 | internal val receiver: RtpReceiver, | 29 | internal val receiver: RtpReceiver, |
| 30 | ) : io.livekit.android.room.track.AudioTrack(name, rtcTrack) { | 30 | ) : io.livekit.android.room.track.AudioTrack(name, rtcTrack) { |
| 31 | 31 | ||
| 32 | - /** | ||
| 33 | - * Adds a sink that receives the audio bytes and related information | ||
| 34 | - * for this audio track. Repeated calls using the same sink will | ||
| 35 | - * only add the sink once. | ||
| 36 | - * | ||
| 37 | - * Implementations should copy the audio data into a local copy if they wish | ||
| 38 | - * to use the data after this function returns. | ||
| 39 | - */ | ||
| 40 | - fun addSink(sink: AudioTrackSink) { | 32 | + override fun addSink(sink: AudioTrackSink) { |
| 41 | withRTCTrack { | 33 | withRTCTrack { |
| 42 | rtcTrack.addSink(sink) | 34 | rtcTrack.addSink(sink) |
| 43 | } | 35 | } |
| 44 | } | 36 | } |
| 45 | 37 | ||
| 46 | - /** | ||
| 47 | - * Removes a previously added sink. | ||
| 48 | - */ | ||
| 49 | - fun removeSink(sink: AudioTrackSink) { | 38 | + override fun removeSink(sink: AudioTrackSink) { |
| 50 | withRTCTrack { | 39 | withRTCTrack { |
| 51 | rtcTrack.removeSink(sink) | 40 | rtcTrack.removeSink(sink) |
| 52 | } | 41 | } |
| @@ -21,6 +21,7 @@ import android.javax.sdp.SdpFactory | @@ -21,6 +21,7 @@ import android.javax.sdp.SdpFactory | ||
| 21 | import dagger.Module | 21 | import dagger.Module |
| 22 | import dagger.Provides | 22 | import dagger.Provides |
| 23 | import io.livekit.android.audio.AudioProcessingController | 23 | import io.livekit.android.audio.AudioProcessingController |
| 24 | +import io.livekit.android.audio.AudioRecordSamplesDispatcher | ||
| 24 | import io.livekit.android.dagger.CapabilitiesGetter | 25 | import io.livekit.android.dagger.CapabilitiesGetter |
| 25 | import io.livekit.android.dagger.InjectionNames | 26 | import io.livekit.android.dagger.InjectionNames |
| 26 | import io.livekit.android.test.mock.MockAudioDeviceModule | 27 | import io.livekit.android.test.mock.MockAudioDeviceModule |
| @@ -59,6 +60,13 @@ object TestRTCModule { | @@ -59,6 +60,13 @@ object TestRTCModule { | ||
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | @Provides | 62 | @Provides |
| 63 | + @Named(InjectionNames.LOCAL_AUDIO_RECORD_SAMPLES_DISPATCHER) | ||
| 64 | + @Singleton | ||
| 65 | + fun localAudioSamplesDispatcher(): AudioRecordSamplesDispatcher { | ||
| 66 | + return AudioRecordSamplesDispatcher() | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Provides | ||
| 62 | @Singleton | 70 | @Singleton |
| 63 | fun peerConnectionFactory( | 71 | fun peerConnectionFactory( |
| 64 | appContext: Context, | 72 | appContext: Context, |
livekit-android-test/src/main/java/io/livekit/android/test/mock/room/track/MockLocalAudioTrack.kt
0 → 100644
| 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.room.track | ||
| 18 | + | ||
| 19 | +import io.livekit.android.audio.AudioProcessingController | ||
| 20 | +import io.livekit.android.audio.AudioRecordSamplesDispatcher | ||
| 21 | +import io.livekit.android.room.track.LocalAudioTrack | ||
| 22 | +import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 23 | +import io.livekit.android.test.MockE2ETest | ||
| 24 | +import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 25 | +import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 26 | +import io.livekit.android.test.mock.TestData | ||
| 27 | +import kotlinx.coroutines.CoroutineDispatcher | ||
| 28 | +import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
| 29 | +import livekit.org.webrtc.AudioTrack | ||
| 30 | + | ||
| 31 | +@OptIn(ExperimentalCoroutinesApi::class) | ||
| 32 | +fun MockE2ETest.createMockLocalAudioTrack( | ||
| 33 | + name: String = "", | ||
| 34 | + mediaTrack: AudioTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 35 | + options: LocalAudioTrackOptions = LocalAudioTrackOptions(), | ||
| 36 | + audioProcessingController: AudioProcessingController = MockAudioProcessingController(), | ||
| 37 | + dispatcher: CoroutineDispatcher = coroutineRule.dispatcher, | ||
| 38 | + audioRecordSamplesDispatcher: AudioRecordSamplesDispatcher = AudioRecordSamplesDispatcher(), | ||
| 39 | +): LocalAudioTrack { | ||
| 40 | + return LocalAudioTrack( | ||
| 41 | + name = name, | ||
| 42 | + mediaTrack = mediaTrack, | ||
| 43 | + options = options, | ||
| 44 | + audioProcessingController = audioProcessingController, | ||
| 45 | + dispatcher = dispatcher, | ||
| 46 | + audioRecordSamplesDispatcher = audioRecordSamplesDispatcher, | ||
| 47 | + ) | ||
| 48 | +} |
| @@ -22,19 +22,17 @@ import io.livekit.android.events.ParticipantEvent | @@ -22,19 +22,17 @@ import io.livekit.android.events.ParticipantEvent | ||
| 22 | import io.livekit.android.events.RoomEvent | 22 | import io.livekit.android.events.RoomEvent |
| 23 | import io.livekit.android.events.convert | 23 | import io.livekit.android.events.convert |
| 24 | import io.livekit.android.room.participant.ConnectionQuality | 24 | import io.livekit.android.room.participant.ConnectionQuality |
| 25 | -import io.livekit.android.room.track.LocalAudioTrack | ||
| 26 | -import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 27 | import io.livekit.android.room.track.Track | 25 | import io.livekit.android.room.track.Track |
| 28 | import io.livekit.android.test.MockE2ETest | 26 | import io.livekit.android.test.MockE2ETest |
| 29 | import io.livekit.android.test.assert.assertIsClassList | 27 | import io.livekit.android.test.assert.assertIsClassList |
| 30 | import io.livekit.android.test.events.EventCollector | 28 | import io.livekit.android.test.events.EventCollector |
| 31 | import io.livekit.android.test.events.FlowCollector | 29 | import io.livekit.android.test.events.FlowCollector |
| 32 | -import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 33 | import io.livekit.android.test.mock.MockAudioStreamTrack | 30 | import io.livekit.android.test.mock.MockAudioStreamTrack |
| 34 | import io.livekit.android.test.mock.MockMediaStream | 31 | import io.livekit.android.test.mock.MockMediaStream |
| 35 | import io.livekit.android.test.mock.MockRtpReceiver | 32 | import io.livekit.android.test.mock.MockRtpReceiver |
| 36 | import io.livekit.android.test.mock.TestData | 33 | import io.livekit.android.test.mock.TestData |
| 37 | import io.livekit.android.test.mock.createMediaStreamId | 34 | import io.livekit.android.test.mock.createMediaStreamId |
| 35 | +import io.livekit.android.test.mock.room.track.createMockLocalAudioTrack | ||
| 38 | import io.livekit.android.util.flow | 36 | import io.livekit.android.util.flow |
| 39 | import io.livekit.android.util.toOkioByteString | 37 | import io.livekit.android.util.toOkioByteString |
| 40 | import junit.framework.Assert.assertEquals | 38 | import junit.framework.Assert.assertEquals |
| @@ -378,13 +376,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -378,13 +376,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 378 | connect() | 376 | connect() |
| 379 | 377 | ||
| 380 | room.localParticipant.publishAudioTrack( | 378 | room.localParticipant.publishAudioTrack( |
| 381 | - LocalAudioTrack( | ||
| 382 | - name = "", | ||
| 383 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 384 | - options = LocalAudioTrackOptions(), | ||
| 385 | - audioProcessingController = MockAudioProcessingController(), | ||
| 386 | - dispatcher = coroutineRule.dispatcher, | ||
| 387 | - ), | 379 | + track = createMockLocalAudioTrack(), |
| 388 | ) | 380 | ) |
| 389 | 381 | ||
| 390 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 382 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| @@ -427,13 +419,7 @@ class RoomMockE2ETest : MockE2ETest() { | @@ -427,13 +419,7 @@ class RoomMockE2ETest : MockE2ETest() { | ||
| 427 | return@registerSignalRequestHandler false | 419 | return@registerSignalRequestHandler false |
| 428 | } | 420 | } |
| 429 | room.localParticipant.publishAudioTrack( | 421 | room.localParticipant.publishAudioTrack( |
| 430 | - LocalAudioTrack( | ||
| 431 | - name = "", | ||
| 432 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 433 | - options = LocalAudioTrackOptions(), | ||
| 434 | - audioProcessingController = MockAudioProcessingController(), | ||
| 435 | - dispatcher = coroutineRule.dispatcher, | ||
| 436 | - ), | 422 | + track = createMockLocalAudioTrack(), |
| 437 | ) | 423 | ) |
| 438 | 424 | ||
| 439 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 425 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
| @@ -19,15 +19,12 @@ package io.livekit.android.room | @@ -19,15 +19,12 @@ package io.livekit.android.room | ||
| 19 | import io.livekit.android.events.ParticipantEvent | 19 | import io.livekit.android.events.ParticipantEvent |
| 20 | import io.livekit.android.events.RoomEvent | 20 | import io.livekit.android.events.RoomEvent |
| 21 | import io.livekit.android.room.participant.AudioTrackPublishOptions | 21 | import io.livekit.android.room.participant.AudioTrackPublishOptions |
| 22 | -import io.livekit.android.room.track.LocalAudioTrack | ||
| 23 | -import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 24 | import io.livekit.android.room.track.Track | 22 | import io.livekit.android.room.track.Track |
| 25 | import io.livekit.android.test.MockE2ETest | 23 | import io.livekit.android.test.MockE2ETest |
| 26 | import io.livekit.android.test.assert.assertIsClass | 24 | import io.livekit.android.test.assert.assertIsClass |
| 27 | import io.livekit.android.test.events.EventCollector | 25 | import io.livekit.android.test.events.EventCollector |
| 28 | -import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 29 | -import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 30 | import io.livekit.android.test.mock.TestData | 26 | import io.livekit.android.test.mock.TestData |
| 27 | +import io.livekit.android.test.mock.room.track.createMockLocalAudioTrack | ||
| 31 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 28 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 32 | import livekit.LivekitRtc | 29 | import livekit.LivekitRtc |
| 33 | import livekit.LivekitRtc.ParticipantUpdate | 30 | import livekit.LivekitRtc.ParticipantUpdate |
| @@ -80,13 +77,7 @@ class RoomParticipantEventMockE2ETest : MockE2ETest() { | @@ -80,13 +77,7 @@ class RoomParticipantEventMockE2ETest : MockE2ETest() { | ||
| 80 | fun localTrackSubscribed() = runTest { | 77 | fun localTrackSubscribed() = runTest { |
| 81 | connect() | 78 | connect() |
| 82 | room.localParticipant.publishAudioTrack( | 79 | room.localParticipant.publishAudioTrack( |
| 83 | - LocalAudioTrack( | ||
| 84 | - name = "", | ||
| 85 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 86 | - options = LocalAudioTrackOptions(), | ||
| 87 | - audioProcessingController = MockAudioProcessingController(), | ||
| 88 | - dispatcher = coroutineRule.dispatcher, | ||
| 89 | - ), | 80 | + track = createMockLocalAudioTrack(), |
| 90 | options = AudioTrackPublishOptions( | 81 | options = AudioTrackPublishOptions( |
| 91 | source = Track.Source.MICROPHONE, | 82 | source = Track.Source.MICROPHONE, |
| 92 | ), | 83 | ), |
| @@ -16,12 +16,9 @@ | @@ -16,12 +16,9 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room | 17 | package io.livekit.android.room |
| 18 | 18 | ||
| 19 | -import io.livekit.android.room.track.LocalAudioTrack | ||
| 20 | -import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 21 | import io.livekit.android.test.MockE2ETest | 19 | import io.livekit.android.test.MockE2ETest |
| 22 | -import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 23 | -import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 24 | import io.livekit.android.test.mock.TestData | 20 | import io.livekit.android.test.mock.TestData |
| 21 | +import io.livekit.android.test.mock.room.track.createMockLocalAudioTrack | ||
| 25 | import io.livekit.android.test.util.toPBByteString | 22 | import io.livekit.android.test.util.toPBByteString |
| 26 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 23 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 27 | import livekit.LivekitRtc | 24 | import livekit.LivekitRtc |
| @@ -104,13 +101,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() { | @@ -104,13 +101,7 @@ class RoomReconnectionMockE2ETest : MockE2ETest() { | ||
| 104 | 101 | ||
| 105 | // publish track | 102 | // publish track |
| 106 | room.localParticipant.publishAudioTrack( | 103 | room.localParticipant.publishAudioTrack( |
| 107 | - LocalAudioTrack( | ||
| 108 | - name = "", | ||
| 109 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 110 | - options = LocalAudioTrackOptions(), | ||
| 111 | - audioProcessingController = MockAudioProcessingController(), | ||
| 112 | - dispatcher = coroutineRule.dispatcher, | ||
| 113 | - ), | 104 | + track = createMockLocalAudioTrack(), |
| 114 | ) | 105 | ) |
| 115 | 106 | ||
| 116 | disconnectPeerConnection() | 107 | disconnectPeerConnection() |
| @@ -20,17 +20,14 @@ import io.livekit.android.events.ParticipantEvent | @@ -20,17 +20,14 @@ import io.livekit.android.events.ParticipantEvent | ||
| 20 | import io.livekit.android.events.RoomEvent | 20 | import io.livekit.android.events.RoomEvent |
| 21 | import io.livekit.android.events.TrackPublicationEvent | 21 | import io.livekit.android.events.TrackPublicationEvent |
| 22 | import io.livekit.android.room.participant.AudioTrackPublishOptions | 22 | import io.livekit.android.room.participant.AudioTrackPublishOptions |
| 23 | -import io.livekit.android.room.track.LocalAudioTrack | ||
| 24 | -import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 25 | import io.livekit.android.room.track.Track | 23 | import io.livekit.android.room.track.Track |
| 26 | import io.livekit.android.test.MockE2ETest | 24 | import io.livekit.android.test.MockE2ETest |
| 27 | import io.livekit.android.test.assert.assertIsClass | 25 | import io.livekit.android.test.assert.assertIsClass |
| 28 | import io.livekit.android.test.events.EventCollector | 26 | import io.livekit.android.test.events.EventCollector |
| 29 | -import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 30 | -import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 31 | import io.livekit.android.test.mock.MockDataChannel | 27 | import io.livekit.android.test.mock.MockDataChannel |
| 32 | import io.livekit.android.test.mock.MockPeerConnection | 28 | import io.livekit.android.test.mock.MockPeerConnection |
| 33 | import io.livekit.android.test.mock.TestData | 29 | import io.livekit.android.test.mock.TestData |
| 30 | +import io.livekit.android.test.mock.room.track.createMockLocalAudioTrack | ||
| 34 | import io.livekit.android.test.util.toDataChannelBuffer | 31 | import io.livekit.android.test.util.toDataChannelBuffer |
| 35 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 32 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 36 | import kotlinx.coroutines.delay | 33 | import kotlinx.coroutines.delay |
| @@ -45,13 +42,7 @@ class RoomTranscriptionMockE2ETest : MockE2ETest() { | @@ -45,13 +42,7 @@ class RoomTranscriptionMockE2ETest : MockE2ETest() { | ||
| 45 | fun transcriptionReceived() = runTest { | 42 | fun transcriptionReceived() = runTest { |
| 46 | connect() | 43 | connect() |
| 47 | room.localParticipant.publishAudioTrack( | 44 | room.localParticipant.publishAudioTrack( |
| 48 | - LocalAudioTrack( | ||
| 49 | - name = "", | ||
| 50 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 51 | - options = LocalAudioTrackOptions(), | ||
| 52 | - audioProcessingController = MockAudioProcessingController(), | ||
| 53 | - dispatcher = coroutineRule.dispatcher, | ||
| 54 | - ), | 45 | + track = createMockLocalAudioTrack(), |
| 55 | options = AudioTrackPublishOptions( | 46 | options = AudioTrackPublishOptions( |
| 56 | source = Track.Source.MICROPHONE, | 47 | source = Track.Source.MICROPHONE, |
| 57 | ), | 48 | ), |
| @@ -105,13 +96,7 @@ class RoomTranscriptionMockE2ETest : MockE2ETest() { | @@ -105,13 +96,7 @@ class RoomTranscriptionMockE2ETest : MockE2ETest() { | ||
| 105 | fun transcriptionFirstReceivedStaysSame() = runTest { | 96 | fun transcriptionFirstReceivedStaysSame() = runTest { |
| 106 | connect() | 97 | connect() |
| 107 | room.localParticipant.publishAudioTrack( | 98 | room.localParticipant.publishAudioTrack( |
| 108 | - LocalAudioTrack( | ||
| 109 | - name = "", | ||
| 110 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 111 | - options = LocalAudioTrackOptions(), | ||
| 112 | - audioProcessingController = MockAudioProcessingController(), | ||
| 113 | - dispatcher = coroutineRule.dispatcher, | ||
| 114 | - ), | 99 | + track = createMockLocalAudioTrack(), |
| 115 | options = AudioTrackPublishOptions( | 100 | options = AudioTrackPublishOptions( |
| 116 | source = Track.Source.MICROPHONE, | 101 | source = Track.Source.MICROPHONE, |
| 117 | ), | 102 | ), |
| @@ -24,8 +24,6 @@ import io.livekit.android.audio.AudioProcessorInterface | @@ -24,8 +24,6 @@ import io.livekit.android.audio.AudioProcessorInterface | ||
| 24 | import io.livekit.android.events.ParticipantEvent | 24 | import io.livekit.android.events.ParticipantEvent |
| 25 | import io.livekit.android.events.RoomEvent | 25 | import io.livekit.android.events.RoomEvent |
| 26 | import io.livekit.android.room.DefaultsManager | 26 | import io.livekit.android.room.DefaultsManager |
| 27 | -import io.livekit.android.room.track.LocalAudioTrack | ||
| 28 | -import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 29 | import io.livekit.android.room.track.LocalVideoTrack | 27 | import io.livekit.android.room.track.LocalVideoTrack |
| 30 | import io.livekit.android.room.track.LocalVideoTrackOptions | 28 | import io.livekit.android.room.track.LocalVideoTrackOptions |
| 31 | import io.livekit.android.room.track.Track | 29 | import io.livekit.android.room.track.Track |
| @@ -35,12 +33,12 @@ import io.livekit.android.test.MockE2ETest | @@ -35,12 +33,12 @@ import io.livekit.android.test.MockE2ETest | ||
| 35 | import io.livekit.android.test.assert.assertIsClassList | 33 | import io.livekit.android.test.assert.assertIsClassList |
| 36 | import io.livekit.android.test.events.EventCollector | 34 | import io.livekit.android.test.events.EventCollector |
| 37 | import io.livekit.android.test.mock.MockAudioProcessingController | 35 | import io.livekit.android.test.mock.MockAudioProcessingController |
| 38 | -import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 39 | import io.livekit.android.test.mock.MockEglBase | 36 | import io.livekit.android.test.mock.MockEglBase |
| 40 | import io.livekit.android.test.mock.MockVideoCapturer | 37 | import io.livekit.android.test.mock.MockVideoCapturer |
| 41 | import io.livekit.android.test.mock.MockVideoStreamTrack | 38 | import io.livekit.android.test.mock.MockVideoStreamTrack |
| 42 | import io.livekit.android.test.mock.TestData | 39 | import io.livekit.android.test.mock.TestData |
| 43 | import io.livekit.android.test.mock.camera.MockCameraProvider | 40 | import io.livekit.android.test.mock.camera.MockCameraProvider |
| 41 | +import io.livekit.android.test.mock.room.track.createMockLocalAudioTrack | ||
| 44 | import io.livekit.android.test.util.toPBByteString | 42 | import io.livekit.android.test.util.toPBByteString |
| 45 | import io.livekit.android.util.toOkioByteString | 43 | import io.livekit.android.util.toOkioByteString |
| 46 | import kotlinx.coroutines.CoroutineScope | 44 | import kotlinx.coroutines.CoroutineScope |
| @@ -78,13 +76,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -78,13 +76,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 78 | connect() | 76 | connect() |
| 79 | 77 | ||
| 80 | room.localParticipant.publishAudioTrack( | 78 | room.localParticipant.publishAudioTrack( |
| 81 | - LocalAudioTrack( | ||
| 82 | - name = "", | ||
| 83 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 84 | - options = LocalAudioTrackOptions(), | ||
| 85 | - audioProcessingController = MockAudioProcessingController(), | ||
| 86 | - dispatcher = coroutineRule.dispatcher, | ||
| 87 | - ), | 79 | + track = createMockLocalAudioTrack(), |
| 88 | ) | 80 | ) |
| 89 | 81 | ||
| 90 | room.disconnect() | 82 | room.disconnect() |
| @@ -439,13 +431,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -439,13 +431,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 439 | 431 | ||
| 440 | wsFactory.ws.clearRequests() | 432 | wsFactory.ws.clearRequests() |
| 441 | room.localParticipant.publishAudioTrack( | 433 | room.localParticipant.publishAudioTrack( |
| 442 | - LocalAudioTrack( | ||
| 443 | - name = "", | ||
| 444 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 445 | - options = LocalAudioTrackOptions(), | ||
| 446 | - audioProcessingController = MockAudioProcessingController(), | ||
| 447 | - dispatcher = coroutineRule.dispatcher, | ||
| 448 | - ), | 434 | + track = createMockLocalAudioTrack(), |
| 449 | ) | 435 | ) |
| 450 | 436 | ||
| 451 | advanceUntilIdle() | 437 | advanceUntilIdle() |
| @@ -471,13 +457,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -471,13 +457,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 471 | 457 | ||
| 472 | val audioProcessingController = MockAudioProcessingController() | 458 | val audioProcessingController = MockAudioProcessingController() |
| 473 | room.localParticipant.publishAudioTrack( | 459 | room.localParticipant.publishAudioTrack( |
| 474 | - LocalAudioTrack( | ||
| 475 | - name = "", | ||
| 476 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 477 | - options = LocalAudioTrackOptions(), | ||
| 478 | - audioProcessingController = audioProcessingController, | ||
| 479 | - dispatcher = coroutineRule.dispatcher, | ||
| 480 | - ), | 460 | + track = createMockLocalAudioTrack(audioProcessingController = audioProcessingController), |
| 481 | ) | 461 | ) |
| 482 | 462 | ||
| 483 | advanceUntilIdle() | 463 | advanceUntilIdle() |
| @@ -517,13 +497,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | @@ -517,13 +497,7 @@ class LocalParticipantMockE2ETest : MockE2ETest() { | ||
| 517 | 497 | ||
| 518 | val audioProcessingController = MockAudioProcessingController() | 498 | val audioProcessingController = MockAudioProcessingController() |
| 519 | room.localParticipant.publishAudioTrack( | 499 | room.localParticipant.publishAudioTrack( |
| 520 | - LocalAudioTrack( | ||
| 521 | - name = "", | ||
| 522 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 523 | - options = LocalAudioTrackOptions(), | ||
| 524 | - audioProcessingController = audioProcessingController, | ||
| 525 | - dispatcher = coroutineRule.dispatcher, | ||
| 526 | - ), | 500 | + track = createMockLocalAudioTrack(audioProcessingController = audioProcessingController), |
| 527 | ) | 501 | ) |
| 528 | 502 | ||
| 529 | advanceUntilIdle() | 503 | advanceUntilIdle() |
| @@ -18,14 +18,11 @@ package io.livekit.android.room.participant | @@ -18,14 +18,11 @@ package io.livekit.android.room.participant | ||
| 18 | 18 | ||
| 19 | import io.livekit.android.events.ParticipantEvent | 19 | import io.livekit.android.events.ParticipantEvent |
| 20 | import io.livekit.android.events.RoomEvent | 20 | import io.livekit.android.events.RoomEvent |
| 21 | -import io.livekit.android.room.track.LocalAudioTrack | ||
| 22 | -import io.livekit.android.room.track.LocalAudioTrackOptions | ||
| 23 | import io.livekit.android.test.MockE2ETest | 21 | import io.livekit.android.test.MockE2ETest |
| 24 | import io.livekit.android.test.assert.assertIsClassList | 22 | import io.livekit.android.test.assert.assertIsClassList |
| 25 | import io.livekit.android.test.events.EventCollector | 23 | import io.livekit.android.test.events.EventCollector |
| 26 | -import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 27 | -import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 28 | import io.livekit.android.test.mock.TestData | 24 | import io.livekit.android.test.mock.TestData |
| 25 | +import io.livekit.android.test.mock.room.track.createMockLocalAudioTrack | ||
| 29 | import io.livekit.android.util.toOkioByteString | 26 | import io.livekit.android.util.toOkioByteString |
| 30 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 27 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 31 | import org.junit.Assert.assertEquals | 28 | import org.junit.Assert.assertEquals |
| @@ -43,13 +40,7 @@ class ParticipantMockE2ETest : MockE2ETest() { | @@ -43,13 +40,7 @@ class ParticipantMockE2ETest : MockE2ETest() { | ||
| 43 | 40 | ||
| 44 | // publish track | 41 | // publish track |
| 45 | room.localParticipant.publishAudioTrack( | 42 | room.localParticipant.publishAudioTrack( |
| 46 | - LocalAudioTrack( | ||
| 47 | - name = "", | ||
| 48 | - mediaTrack = MockAudioStreamTrack(id = TestData.LOCAL_TRACK_PUBLISHED.trackPublished.cid), | ||
| 49 | - options = LocalAudioTrackOptions(), | ||
| 50 | - audioProcessingController = MockAudioProcessingController(), | ||
| 51 | - dispatcher = coroutineRule.dispatcher, | ||
| 52 | - ), | 43 | + track = createMockLocalAudioTrack(), |
| 53 | ) | 44 | ) |
| 54 | 45 | ||
| 55 | val eventCollector = EventCollector(room.events, coroutineRule.scope) | 46 | val eventCollector = EventCollector(room.events, coroutineRule.scope) |
-
请 注册 或 登录 后发表评论