正在显示
10 个修改的文件
包含
266 行增加
和
41 行删除
| 1 | +package io.livekit.android.events | ||
| 2 | + | ||
| 3 | +import io.livekit.android.room.participant.LocalParticipant | ||
| 4 | +import io.livekit.android.room.participant.Participant | ||
| 5 | +import io.livekit.android.room.participant.RemoteParticipant | ||
| 6 | +import io.livekit.android.room.track.LocalTrackPublication | ||
| 7 | +import io.livekit.android.room.track.RemoteTrackPublication | ||
| 8 | +import io.livekit.android.room.track.Track | ||
| 9 | +import io.livekit.android.room.track.TrackPublication | ||
| 10 | + | ||
| 11 | +sealed class ParticipantEvent(open val participant: Participant) : Event() { | ||
| 12 | + // all participants | ||
| 13 | + /** | ||
| 14 | + * When a participant's metadata is updated, fired for all participants | ||
| 15 | + */ | ||
| 16 | + class MetadataChanged(participant: Participant, val prevMetadata: String?) : ParticipantEvent(participant) | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * Fired when the current participant's isSpeaking property changes. (including LocalParticipant) | ||
| 20 | + */ | ||
| 21 | + class SpeakingChanged(participant: Participant, val isSpeaking: Boolean) : ParticipantEvent(participant) | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * The participant was muted. | ||
| 25 | + * | ||
| 26 | + * For the local participant, the callback will be called if setMute was called on the | ||
| 27 | + * [LocalTrackPublication], or if the server has requested the participant to be muted | ||
| 28 | + */ | ||
| 29 | + class TrackMuted(participant: Participant, val publication: TrackPublication) : ParticipantEvent(participant) | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * The participant was unmuted. | ||
| 33 | + * | ||
| 34 | + * For the local participant, the callback will be called if setMute was called on the | ||
| 35 | + * [LocalTrackPublication], or if the server has requested the participant to be muted | ||
| 36 | + */ | ||
| 37 | + class TrackUnmuted(participant: Participant, val publication: TrackPublication) : ParticipantEvent(participant) | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + // local participants | ||
| 41 | + /** | ||
| 42 | + * When a new track is published by the local participant. | ||
| 43 | + */ | ||
| 44 | + class LocalTrackPublished(override val participant: LocalParticipant, val publication: LocalTrackPublication) : | ||
| 45 | + ParticipantEvent(participant) | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * A [LocalParticipant] has unpublished a track | ||
| 49 | + */ | ||
| 50 | + class LocalTrackUnpublished(override val participant: LocalParticipant, val publication: LocalTrackPublication) : | ||
| 51 | + ParticipantEvent(participant) | ||
| 52 | + | ||
| 53 | + // remote participants | ||
| 54 | + /** | ||
| 55 | + * When a new track is published to room after the local participant has joined. | ||
| 56 | + * | ||
| 57 | + * It will not fire for tracks that are already published | ||
| 58 | + */ | ||
| 59 | + class TrackPublished(override val participant: RemoteParticipant, val publication: RemoteTrackPublication) : | ||
| 60 | + ParticipantEvent(participant) | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * A [RemoteParticipant] has unpublished a track | ||
| 64 | + */ | ||
| 65 | + class TrackUnpublished(override val participant: RemoteParticipant, val publication: RemoteTrackPublication) : | ||
| 66 | + ParticipantEvent(participant) | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * Subscribed to a new track | ||
| 70 | + */ | ||
| 71 | + class TrackSubscribed( | ||
| 72 | + override val participant: RemoteParticipant, | ||
| 73 | + val track: Track, | ||
| 74 | + val publication: RemoteTrackPublication, | ||
| 75 | + ) : | ||
| 76 | + ParticipantEvent(participant) | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * Error had occurred while subscribing to a track | ||
| 80 | + */ | ||
| 81 | + class TrackSubscriptionFailed( | ||
| 82 | + override val participant: RemoteParticipant, | ||
| 83 | + val sid: String, | ||
| 84 | + val exception: Exception, | ||
| 85 | + ) : ParticipantEvent(participant) | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * A subscribed track is no longer available. | ||
| 89 | + * Clients should listen to this event and handle cleanup | ||
| 90 | + */ | ||
| 91 | + class TrackUnsubscribed( | ||
| 92 | + override val participant: RemoteParticipant, | ||
| 93 | + val track: Track, | ||
| 94 | + val publication: RemoteTrackPublication | ||
| 95 | + ) : ParticipantEvent(participant) | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * Received data published by another participant | ||
| 99 | + */ | ||
| 100 | + class DataReceived(override val participant: RemoteParticipant, val data: ByteArray) : ParticipantEvent(participant) | ||
| 101 | +} |
| @@ -9,45 +9,45 @@ import io.livekit.android.room.track.LocalTrackPublication | @@ -9,45 +9,45 @@ import io.livekit.android.room.track.LocalTrackPublication | ||
| 9 | import io.livekit.android.room.track.Track | 9 | import io.livekit.android.room.track.Track |
| 10 | import io.livekit.android.room.track.TrackPublication | 10 | import io.livekit.android.room.track.TrackPublication |
| 11 | 11 | ||
| 12 | -sealed class RoomEvent : Event() { | 12 | +sealed class RoomEvent(val room: Room) : Event() { |
| 13 | /** | 13 | /** |
| 14 | * A network change has been detected and LiveKit attempts to reconnect to the room | 14 | * A network change has been detected and LiveKit attempts to reconnect to the room |
| 15 | * When reconnect attempts succeed, the room state will be kept, including tracks that are subscribed/published | 15 | * When reconnect attempts succeed, the room state will be kept, including tracks that are subscribed/published |
| 16 | */ | 16 | */ |
| 17 | - class Reconnecting(val room: Room): RoomEvent() | 17 | + class Reconnecting(room: Room) : RoomEvent(room) |
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| 20 | * The reconnect attempt had been successful | 20 | * The reconnect attempt had been successful |
| 21 | */ | 21 | */ |
| 22 | - class Reconnected(val room: Room): RoomEvent() | 22 | + class Reconnected(room: Room) : RoomEvent(room) |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Disconnected from room | 25 | * Disconnected from room |
| 26 | */ | 26 | */ |
| 27 | - class Disconnected(val room: Room, val error: Exception?): RoomEvent() | 27 | + class Disconnected(room: Room, val error: Exception?) : RoomEvent(room) |
| 28 | 28 | ||
| 29 | /** | 29 | /** |
| 30 | * When a [RemoteParticipant] joins after the local participant. It will not emit events | 30 | * When a [RemoteParticipant] joins after the local participant. It will not emit events |
| 31 | * for participants that are already in the room | 31 | * for participants that are already in the room |
| 32 | */ | 32 | */ |
| 33 | - class ParticipantConnected(val room: Room, val participant: RemoteParticipant): RoomEvent() | 33 | + class ParticipantConnected(room: Room, val participant: RemoteParticipant) : RoomEvent(room) |
| 34 | 34 | ||
| 35 | /** | 35 | /** |
| 36 | * When a [RemoteParticipant] leaves after the local participant has joined. | 36 | * When a [RemoteParticipant] leaves after the local participant has joined. |
| 37 | */ | 37 | */ |
| 38 | - class ParticipantDisconnected(val room: Room, val participant: RemoteParticipant) : RoomEvent() | 38 | + class ParticipantDisconnected(room: Room, val participant: RemoteParticipant) : RoomEvent(room) |
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | * Active speakers changed. List of speakers are ordered by their audio level. loudest | 41 | * Active speakers changed. List of speakers are ordered by their audio level. loudest |
| 42 | * speakers first. This will include the [LocalParticipant] too. | 42 | * speakers first. This will include the [LocalParticipant] too. |
| 43 | */ | 43 | */ |
| 44 | - class ActiveSpeakersChanged(val room: Room, val speakers: List<Participant>) : RoomEvent() | 44 | + class ActiveSpeakersChanged(room: Room, val speakers: List<Participant>) : RoomEvent(room) |
| 45 | 45 | ||
| 46 | class RoomMetadataChanged( | 46 | class RoomMetadataChanged( |
| 47 | - val room: Room, | 47 | + room: Room, |
| 48 | val newMetadata: String?, | 48 | val newMetadata: String?, |
| 49 | val prevMetadata: String? | 49 | val prevMetadata: String? |
| 50 | - ) : RoomEvent() | 50 | + ) : RoomEvent(room) |
| 51 | 51 | ||
| 52 | // Participant callbacks | 52 | // Participant callbacks |
| 53 | /** | 53 | /** |
| @@ -56,10 +56,10 @@ sealed class RoomEvent : Event() { | @@ -56,10 +56,10 @@ sealed class RoomEvent : Event() { | ||
| 56 | * this event will be fired for all clients in the room. | 56 | * this event will be fired for all clients in the room. |
| 57 | */ | 57 | */ |
| 58 | class ParticipantMetadataChanged( | 58 | class ParticipantMetadataChanged( |
| 59 | - val room: Room, | 59 | + room: Room, |
| 60 | val participant: Participant, | 60 | val participant: Participant, |
| 61 | val prevMetadata: String? | 61 | val prevMetadata: String? |
| 62 | - ) : RoomEvent() | 62 | + ) : RoomEvent(room) |
| 63 | 63 | ||
| 64 | /** | 64 | /** |
| 65 | * The participant was muted. | 65 | * The participant was muted. |
| @@ -67,7 +67,7 @@ sealed class RoomEvent : Event() { | @@ -67,7 +67,7 @@ sealed class RoomEvent : Event() { | ||
| 67 | * For the local participant, the callback will be called if setMute was called on the | 67 | * For the local participant, the callback will be called if setMute was called on the |
| 68 | * [LocalTrackPublication], or if the server has requested the participant to be muted | 68 | * [LocalTrackPublication], or if the server has requested the participant to be muted |
| 69 | */ | 69 | */ |
| 70 | - class TrackMuted(val room: Room, val publication: TrackPublication, val participant: Participant): RoomEvent() | 70 | + class TrackMuted(room: Room, val publication: TrackPublication, val participant: Participant) : RoomEvent(room) |
| 71 | 71 | ||
| 72 | /** | 72 | /** |
| 73 | * The participant was unmuted. | 73 | * The participant was unmuted. |
| @@ -75,40 +75,56 @@ sealed class RoomEvent : Event() { | @@ -75,40 +75,56 @@ sealed class RoomEvent : Event() { | ||
| 75 | * For the local participant, the callback will be called if setMute was called on the | 75 | * For the local participant, the callback will be called if setMute was called on the |
| 76 | * [LocalTrackPublication], or if the server has requested the participant to be muted | 76 | * [LocalTrackPublication], or if the server has requested the participant to be muted |
| 77 | */ | 77 | */ |
| 78 | - class TrackUnmuted(val room: Room, val publication: TrackPublication, val participant: Participant): RoomEvent() | 78 | + class TrackUnmuted(room: Room, val publication: TrackPublication, val participant: Participant) : RoomEvent(room) |
| 79 | 79 | ||
| 80 | /** | 80 | /** |
| 81 | * When a new track is published to room after the local participant has joined. It will | 81 | * When a new track is published to room after the local participant has joined. It will |
| 82 | * not fire for tracks that are already published | 82 | * not fire for tracks that are already published |
| 83 | */ | 83 | */ |
| 84 | - class TrackPublished(val room: Room, val publication: TrackPublication, val participant: Participant): RoomEvent() | 84 | + class TrackPublished(room: Room, val publication: TrackPublication, val participant: Participant) : RoomEvent(room) |
| 85 | 85 | ||
| 86 | /** | 86 | /** |
| 87 | * A [Participant] has unpublished a track | 87 | * A [Participant] has unpublished a track |
| 88 | */ | 88 | */ |
| 89 | - class TrackUnpublished(val room: Room, val publication: TrackPublication, val participant: Participant): RoomEvent() | 89 | + class TrackUnpublished(room: Room, val publication: TrackPublication, val participant: Participant) : |
| 90 | + RoomEvent(room) | ||
| 90 | 91 | ||
| 91 | /** | 92 | /** |
| 92 | * The [LocalParticipant] has subscribed to a new track. This event will always fire as | 93 | * The [LocalParticipant] has subscribed to a new track. This event will always fire as |
| 93 | * long as new tracks are ready for use. | 94 | * long as new tracks are ready for use. |
| 94 | */ | 95 | */ |
| 95 | - class TrackSubscribed(val room: Room, val track: Track, val publication: TrackPublication, val participant: RemoteParticipant): RoomEvent() | 96 | + class TrackSubscribed( |
| 97 | + room: Room, | ||
| 98 | + val track: Track, | ||
| 99 | + val publication: TrackPublication, | ||
| 100 | + val participant: RemoteParticipant | ||
| 101 | + ) : RoomEvent(room) | ||
| 96 | 102 | ||
| 97 | /** | 103 | /** |
| 98 | * Could not subscribe to a track | 104 | * Could not subscribe to a track |
| 99 | */ | 105 | */ |
| 100 | - class TrackSubscriptionFailed(val room: Room, val sid: String, val exception: Exception, val participant: RemoteParticipant): RoomEvent() | 106 | + class TrackSubscriptionFailed( |
| 107 | + room: Room, | ||
| 108 | + val sid: String, | ||
| 109 | + val exception: Exception, | ||
| 110 | + val participant: RemoteParticipant | ||
| 111 | + ) : RoomEvent(room) | ||
| 101 | 112 | ||
| 102 | /** | 113 | /** |
| 103 | * A subscribed track is no longer available. Clients should listen to this event and ensure | 114 | * A subscribed track is no longer available. Clients should listen to this event and ensure |
| 104 | * the track removes all renderers | 115 | * the track removes all renderers |
| 105 | */ | 116 | */ |
| 106 | - class TrackUnsubscribed(val room: Room, val track: Track, val publications: TrackPublication, val participant: RemoteParticipant): RoomEvent() | 117 | + class TrackUnsubscribed( |
| 118 | + room: Room, | ||
| 119 | + val track: Track, | ||
| 120 | + val publications: TrackPublication, | ||
| 121 | + val participant: RemoteParticipant | ||
| 122 | + ) : RoomEvent(room) | ||
| 107 | 123 | ||
| 108 | /** | 124 | /** |
| 109 | * Received data published by another participant | 125 | * Received data published by another participant |
| 110 | */ | 126 | */ |
| 111 | - class DataReceived(val room: Room, val data: ByteArray, val participant: RemoteParticipant): RoomEvent() | 127 | + class DataReceived(room: Room, val data: ByteArray, val participant: RemoteParticipant) : RoomEvent(room) |
| 112 | 128 | ||
| 113 | /** | 129 | /** |
| 114 | * The connection quality for a participant has changed. | 130 | * The connection quality for a participant has changed. |
| @@ -116,6 +132,7 @@ sealed class RoomEvent : Event() { | @@ -116,6 +132,7 @@ sealed class RoomEvent : Event() { | ||
| 116 | * @param participant Either a remote participant or [Room.localParticipant] | 132 | * @param participant Either a remote participant or [Room.localParticipant] |
| 117 | * @param quality the new connection quality | 133 | * @param quality the new connection quality |
| 118 | */ | 134 | */ |
| 119 | - class ConnectionQualityChanged(val room: Room, val participant: Participant, val quality: ConnectionQuality): RoomEvent() | 135 | + class ConnectionQualityChanged(room: Room, val participant: Participant, val quality: ConnectionQuality) : |
| 136 | + RoomEvent(room) | ||
| 120 | 137 | ||
| 121 | } | 138 | } |
| @@ -149,9 +149,9 @@ constructor( | @@ -149,9 +149,9 @@ constructor( | ||
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | participant = if (info != null) { | 151 | participant = if (info != null) { |
| 152 | - RemoteParticipant(info, engine.client, ioDispatcher) | 152 | + RemoteParticipant(info, engine.client, ioDispatcher, defaultDispatcher) |
| 153 | } else { | 153 | } else { |
| 154 | - RemoteParticipant(sid, null, engine.client, ioDispatcher) | 154 | + RemoteParticipant(sid, null, engine.client, ioDispatcher, defaultDispatcher) |
| 155 | } | 155 | } |
| 156 | participant.internalListener = this | 156 | participant.internalListener = this |
| 157 | mutableRemoteParticipants[sid] = participant | 157 | mutableRemoteParticipants[sid] = participant |
| @@ -381,7 +381,7 @@ constructor( | @@ -381,7 +381,7 @@ constructor( | ||
| 381 | 381 | ||
| 382 | listener?.onDataReceived(data, participant, this) | 382 | listener?.onDataReceived(data, participant, this) |
| 383 | eventBus.postEvent(RoomEvent.DataReceived(this, data, participant), coroutineScope) | 383 | eventBus.postEvent(RoomEvent.DataReceived(this, data, participant), coroutineScope) |
| 384 | - participant.listener?.onDataReceived(data, participant) | 384 | + participant.onDataReceived(data) |
| 385 | } | 385 | } |
| 386 | 386 | ||
| 387 | /** | 387 | /** |
| @@ -7,16 +7,20 @@ import com.google.protobuf.ByteString | @@ -7,16 +7,20 @@ import com.google.protobuf.ByteString | ||
| 7 | import dagger.assisted.Assisted | 7 | import dagger.assisted.Assisted |
| 8 | import dagger.assisted.AssistedFactory | 8 | import dagger.assisted.AssistedFactory |
| 9 | import dagger.assisted.AssistedInject | 9 | import dagger.assisted.AssistedInject |
| 10 | +import io.livekit.android.dagger.InjectionNames | ||
| 11 | +import io.livekit.android.events.ParticipantEvent | ||
| 10 | import io.livekit.android.room.DefaultsManager | 12 | import io.livekit.android.room.DefaultsManager |
| 11 | import io.livekit.android.room.RTCEngine | 13 | import io.livekit.android.room.RTCEngine |
| 12 | import io.livekit.android.room.track.* | 14 | import io.livekit.android.room.track.* |
| 13 | import io.livekit.android.util.LKLog | 15 | import io.livekit.android.util.LKLog |
| 16 | +import kotlinx.coroutines.CoroutineDispatcher | ||
| 14 | import livekit.LivekitModels | 17 | import livekit.LivekitModels |
| 15 | import livekit.LivekitRtc | 18 | import livekit.LivekitRtc |
| 16 | import org.webrtc.EglBase | 19 | import org.webrtc.EglBase |
| 17 | import org.webrtc.PeerConnectionFactory | 20 | import org.webrtc.PeerConnectionFactory |
| 18 | import org.webrtc.RtpParameters | 21 | import org.webrtc.RtpParameters |
| 19 | import org.webrtc.RtpTransceiver | 22 | import org.webrtc.RtpTransceiver |
| 23 | +import javax.inject.Named | ||
| 20 | import kotlin.math.abs | 24 | import kotlin.math.abs |
| 21 | import kotlin.math.roundToInt | 25 | import kotlin.math.roundToInt |
| 22 | 26 | ||
| @@ -31,8 +35,10 @@ internal constructor( | @@ -31,8 +35,10 @@ internal constructor( | ||
| 31 | private val eglBase: EglBase, | 35 | private val eglBase: EglBase, |
| 32 | private val screencastVideoTrackFactory: LocalScreencastVideoTrack.Factory, | 36 | private val screencastVideoTrackFactory: LocalScreencastVideoTrack.Factory, |
| 33 | private val videoTrackFactory: LocalVideoTrack.Factory, | 37 | private val videoTrackFactory: LocalVideoTrack.Factory, |
| 34 | - private val defaultsManager: DefaultsManager | ||
| 35 | -) : Participant(info.sid, info.identity) { | 38 | + private val defaultsManager: DefaultsManager, |
| 39 | + @Named(InjectionNames.DISPATCHER_DEFAULT) | ||
| 40 | + coroutineDispatcher: CoroutineDispatcher, | ||
| 41 | +) : Participant(info.sid, info.identity, coroutineDispatcher) { | ||
| 36 | 42 | ||
| 37 | var audioTrackCaptureDefaults: LocalAudioTrackOptions by defaultsManager::audioTrackCaptureDefaults | 43 | var audioTrackCaptureDefaults: LocalAudioTrackOptions by defaultsManager::audioTrackCaptureDefaults |
| 38 | var audioTrackPublishDefaults: AudioTrackPublishDefaults by defaultsManager::audioTrackPublishDefaults | 44 | var audioTrackPublishDefaults: AudioTrackPublishDefaults by defaultsManager::audioTrackPublishDefaults |
| @@ -212,6 +218,7 @@ internal constructor( | @@ -212,6 +218,7 @@ internal constructor( | ||
| 212 | addTrackPublication(publication) | 218 | addTrackPublication(publication) |
| 213 | publishListener?.onPublishSuccess(publication) | 219 | publishListener?.onPublishSuccess(publication) |
| 214 | internalListener?.onTrackPublished(publication, this) | 220 | internalListener?.onTrackPublished(publication, this) |
| 221 | + eventBus.postEvent(ParticipantEvent.LocalTrackPublished(this, publication), scope) | ||
| 215 | } | 222 | } |
| 216 | 223 | ||
| 217 | suspend fun publishVideoTrack( | 224 | suspend fun publishVideoTrack( |
| @@ -260,6 +267,7 @@ internal constructor( | @@ -260,6 +267,7 @@ internal constructor( | ||
| 260 | addTrackPublication(publication) | 267 | addTrackPublication(publication) |
| 261 | publishListener?.onPublishSuccess(publication) | 268 | publishListener?.onPublishSuccess(publication) |
| 262 | internalListener?.onTrackPublished(publication, this) | 269 | internalListener?.onTrackPublished(publication, this) |
| 270 | + eventBus.postEvent(ParticipantEvent.LocalTrackPublished(this, publication), scope) | ||
| 263 | } | 271 | } |
| 264 | 272 | ||
| 265 | private fun computeVideoEncodings( | 273 | private fun computeVideoEncodings( |
| @@ -374,6 +382,7 @@ internal constructor( | @@ -374,6 +382,7 @@ internal constructor( | ||
| 374 | } | 382 | } |
| 375 | track.stop() | 383 | track.stop() |
| 376 | internalListener?.onTrackUnpublished(publication, this) | 384 | internalListener?.onTrackUnpublished(publication, this) |
| 385 | + eventBus.postEvent(ParticipantEvent.LocalTrackUnpublished(this, publication), scope) | ||
| 377 | } | 386 | } |
| 378 | 387 | ||
| 379 | /** | 388 | /** |
| @@ -420,11 +429,15 @@ internal constructor( | @@ -420,11 +429,15 @@ internal constructor( | ||
| 420 | } | 429 | } |
| 421 | } | 430 | } |
| 422 | 431 | ||
| 432 | + /** | ||
| 433 | + * @suppress | ||
| 434 | + */ | ||
| 423 | fun onRemoteMuteChanged(trackSid: String, muted: Boolean) { | 435 | fun onRemoteMuteChanged(trackSid: String, muted: Boolean) { |
| 424 | val pub = tracks[trackSid] | 436 | val pub = tracks[trackSid] |
| 425 | pub?.muted = muted | 437 | pub?.muted = muted |
| 426 | } | 438 | } |
| 427 | 439 | ||
| 440 | + | ||
| 428 | interface PublishListener { | 441 | interface PublishListener { |
| 429 | fun onPublishSuccess(publication: TrackPublication) {} | 442 | fun onPublishSuccess(publication: TrackPublication) {} |
| 430 | fun onPublishFailure(exception: Exception) {} | 443 | fun onPublishFailure(exception: Exception) {} |
| 1 | package io.livekit.android.room.participant | 1 | package io.livekit.android.room.participant |
| 2 | 2 | ||
| 3 | +import io.livekit.android.dagger.InjectionNames | ||
| 4 | +import io.livekit.android.events.BroadcastEventBus | ||
| 5 | +import io.livekit.android.events.ParticipantEvent | ||
| 3 | import io.livekit.android.room.track.LocalTrackPublication | 6 | import io.livekit.android.room.track.LocalTrackPublication |
| 4 | import io.livekit.android.room.track.RemoteTrackPublication | 7 | import io.livekit.android.room.track.RemoteTrackPublication |
| 5 | import io.livekit.android.room.track.Track | 8 | import io.livekit.android.room.track.Track |
| 6 | import io.livekit.android.room.track.TrackPublication | 9 | import io.livekit.android.room.track.TrackPublication |
| 10 | +import kotlinx.coroutines.CoroutineDispatcher | ||
| 11 | +import kotlinx.coroutines.CoroutineScope | ||
| 12 | +import kotlinx.coroutines.SupervisorJob | ||
| 7 | import livekit.LivekitModels | 13 | import livekit.LivekitModels |
| 14 | +import javax.inject.Named | ||
| 15 | + | ||
| 16 | +open class Participant( | ||
| 17 | + var sid: String, | ||
| 18 | + identity: String? = null, | ||
| 19 | + @Named(InjectionNames.DISPATCHER_DEFAULT) | ||
| 20 | + coroutineDispatcher: CoroutineDispatcher, | ||
| 21 | +) { | ||
| 22 | + protected val scope = CoroutineScope(coroutineDispatcher + SupervisorJob()) | ||
| 23 | + | ||
| 24 | + protected val eventBus = BroadcastEventBus<ParticipantEvent>() | ||
| 25 | + val events = eventBus.readOnly() | ||
| 8 | 26 | ||
| 9 | -open class Participant(var sid: String, identity: String? = null) { | ||
| 10 | var participantInfo: LivekitModels.ParticipantInfo? = null | 27 | var participantInfo: LivekitModels.ParticipantInfo? = null |
| 11 | private set | 28 | private set |
| 12 | var identity: String? = identity | 29 | var identity: String? = identity |
| @@ -15,11 +32,12 @@ open class Participant(var sid: String, identity: String? = null) { | @@ -15,11 +32,12 @@ open class Participant(var sid: String, identity: String? = null) { | ||
| 15 | internal set | 32 | internal set |
| 16 | var isSpeaking: Boolean = false | 33 | var isSpeaking: Boolean = false |
| 17 | internal set(v) { | 34 | internal set(v) { |
| 18 | - val changed = v == field | 35 | + val changed = v != field |
| 19 | field = v | 36 | field = v |
| 20 | if (changed) { | 37 | if (changed) { |
| 21 | listener?.onSpeakingChanged(this) | 38 | listener?.onSpeakingChanged(this) |
| 22 | internalListener?.onSpeakingChanged(this) | 39 | internalListener?.onSpeakingChanged(this) |
| 40 | + eventBus.postEvent(ParticipantEvent.SpeakingChanged(this, v), scope) | ||
| 23 | } | 41 | } |
| 24 | } | 42 | } |
| 25 | var metadata: String? = null | 43 | var metadata: String? = null |
| @@ -29,6 +47,7 @@ open class Participant(var sid: String, identity: String? = null) { | @@ -29,6 +47,7 @@ open class Participant(var sid: String, identity: String? = null) { | ||
| 29 | if (prevMetadata != v) { | 47 | if (prevMetadata != v) { |
| 30 | listener?.onMetadataChanged(this, prevMetadata) | 48 | listener?.onMetadataChanged(this, prevMetadata) |
| 31 | internalListener?.onMetadataChanged(this, prevMetadata) | 49 | internalListener?.onMetadataChanged(this, prevMetadata) |
| 50 | + eventBus.postEvent(ParticipantEvent.MetadataChanged(this, prevMetadata), scope) | ||
| 32 | } | 51 | } |
| 33 | } | 52 | } |
| 34 | var connectionQuality: ConnectionQuality = ConnectionQuality.UNKNOWN | 53 | var connectionQuality: ConnectionQuality = ConnectionQuality.UNKNOWN |
| @@ -37,11 +56,13 @@ open class Participant(var sid: String, identity: String? = null) { | @@ -37,11 +56,13 @@ open class Participant(var sid: String, identity: String? = null) { | ||
| 37 | /** | 56 | /** |
| 38 | * Listener for when participant properties change | 57 | * Listener for when participant properties change |
| 39 | */ | 58 | */ |
| 59 | + @Deprecated("Use events instead") | ||
| 40 | var listener: ParticipantListener? = null | 60 | var listener: ParticipantListener? = null |
| 41 | 61 | ||
| 42 | /** | 62 | /** |
| 43 | * @suppress | 63 | * @suppress |
| 44 | */ | 64 | */ |
| 65 | + @Deprecated("Use events instead") | ||
| 45 | internal var internalListener: ParticipantListener? = null | 66 | internal var internalListener: ParticipantListener? = null |
| 46 | 67 | ||
| 47 | val hasInfo | 68 | val hasInfo |
| @@ -152,9 +173,24 @@ open class Participant(var sid: String, identity: String? = null) { | @@ -152,9 +173,24 @@ open class Participant(var sid: String, identity: String? = null) { | ||
| 152 | override fun hashCode(): Int { | 173 | override fun hashCode(): Int { |
| 153 | return sid.hashCode() | 174 | return sid.hashCode() |
| 154 | } | 175 | } |
| 155 | -} | ||
| 156 | 176 | ||
| 157 | 177 | ||
| 178 | + // Internal methods just for posting events. | ||
| 179 | + internal fun onTrackMuted(trackPublication: TrackPublication) { | ||
| 180 | + listener?.onTrackMuted(trackPublication, this) | ||
| 181 | + internalListener?.onTrackMuted(trackPublication, this) | ||
| 182 | + eventBus.postEvent(ParticipantEvent.TrackMuted(this, trackPublication), scope) | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + internal fun onTrackUnmuted(trackPublication: TrackPublication) { | ||
| 186 | + listener?.onTrackUnmuted(trackPublication, this) | ||
| 187 | + internalListener?.onTrackUnmuted(trackPublication, this) | ||
| 188 | + eventBus.postEvent(ParticipantEvent.TrackUnmuted(this, trackPublication), scope) | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | +} | ||
| 192 | + | ||
| 193 | +@Deprecated("Use Participant.events instead.") | ||
| 158 | interface ParticipantListener { | 194 | interface ParticipantListener { |
| 159 | // all participants | 195 | // all participants |
| 160 | /** | 196 | /** |
| 1 | package io.livekit.android.room.participant | 1 | package io.livekit.android.room.participant |
| 2 | 2 | ||
| 3 | +import io.livekit.android.events.ParticipantEvent | ||
| 3 | import io.livekit.android.room.SignalClient | 4 | import io.livekit.android.room.SignalClient |
| 4 | import io.livekit.android.room.track.* | 5 | import io.livekit.android.room.track.* |
| 5 | import io.livekit.android.util.CloseableCoroutineScope | 6 | import io.livekit.android.util.CloseableCoroutineScope |
| @@ -18,19 +19,22 @@ class RemoteParticipant( | @@ -18,19 +19,22 @@ class RemoteParticipant( | ||
| 18 | identity: String? = null, | 19 | identity: String? = null, |
| 19 | val signalClient: SignalClient, | 20 | val signalClient: SignalClient, |
| 20 | private val ioDispatcher: CoroutineDispatcher, | 21 | private val ioDispatcher: CoroutineDispatcher, |
| 21 | -) : Participant(sid, identity) { | 22 | + defaultdispatcher: CoroutineDispatcher, |
| 23 | +) : Participant(sid, identity, defaultdispatcher) { | ||
| 22 | /** | 24 | /** |
| 23 | * @suppress | 25 | * @suppress |
| 24 | */ | 26 | */ |
| 25 | constructor( | 27 | constructor( |
| 26 | info: LivekitModels.ParticipantInfo, | 28 | info: LivekitModels.ParticipantInfo, |
| 27 | signalClient: SignalClient, | 29 | signalClient: SignalClient, |
| 28 | - ioDispatcher: CoroutineDispatcher | 30 | + ioDispatcher: CoroutineDispatcher, |
| 31 | + defaultdispatcher: CoroutineDispatcher, | ||
| 29 | ) : this( | 32 | ) : this( |
| 30 | info.sid, | 33 | info.sid, |
| 31 | info.identity, | 34 | info.identity, |
| 32 | signalClient, | 35 | signalClient, |
| 33 | ioDispatcher, | 36 | ioDispatcher, |
| 37 | + defaultdispatcher | ||
| 34 | ) { | 38 | ) { |
| 35 | updateFromInfo(info) | 39 | updateFromInfo(info) |
| 36 | } | 40 | } |
| @@ -73,6 +77,7 @@ class RemoteParticipant( | @@ -73,6 +77,7 @@ class RemoteParticipant( | ||
| 73 | for (publication in newTrackPublications.values) { | 77 | for (publication in newTrackPublications.values) { |
| 74 | internalListener?.onTrackPublished(publication, this) | 78 | internalListener?.onTrackPublished(publication, this) |
| 75 | listener?.onTrackPublished(publication, this) | 79 | listener?.onTrackPublished(publication, this) |
| 80 | + eventBus.postEvent(ParticipantEvent.TrackPublished(this, publication), scope) | ||
| 76 | } | 81 | } |
| 77 | } | 82 | } |
| 78 | 83 | ||
| @@ -112,6 +117,7 @@ class RemoteParticipant( | @@ -112,6 +117,7 @@ class RemoteParticipant( | ||
| 112 | 117 | ||
| 113 | internalListener?.onTrackSubscriptionFailed(sid, exception, this) | 118 | internalListener?.onTrackSubscriptionFailed(sid, exception, this) |
| 114 | listener?.onTrackSubscriptionFailed(sid, exception, this) | 119 | listener?.onTrackSubscriptionFailed(sid, exception, this) |
| 120 | + eventBus.postEvent(ParticipantEvent.TrackSubscriptionFailed(this, sid, exception), scope) | ||
| 115 | } else { | 121 | } else { |
| 116 | coroutineScope.launch { | 122 | coroutineScope.launch { |
| 117 | delay(150) | 123 | delay(150) |
| @@ -131,6 +137,7 @@ class RemoteParticipant( | @@ -131,6 +137,7 @@ class RemoteParticipant( | ||
| 131 | 137 | ||
| 132 | internalListener?.onTrackSubscribed(track, publication, this) | 138 | internalListener?.onTrackSubscribed(track, publication, this) |
| 133 | listener?.onTrackSubscribed(track, publication, this) | 139 | listener?.onTrackSubscribed(track, publication, this) |
| 140 | + eventBus.postEvent(ParticipantEvent.TrackSubscribed(this, track, publication), scope) | ||
| 134 | } | 141 | } |
| 135 | 142 | ||
| 136 | fun unpublishTrack(trackSid: String, sendUnpublish: Boolean = false) { | 143 | fun unpublishTrack(trackSid: String, sendUnpublish: Boolean = false) { |
| @@ -146,13 +153,21 @@ class RemoteParticipant( | @@ -146,13 +153,21 @@ class RemoteParticipant( | ||
| 146 | track.stop() | 153 | track.stop() |
| 147 | internalListener?.onTrackUnsubscribed(track, publication, this) | 154 | internalListener?.onTrackUnsubscribed(track, publication, this) |
| 148 | listener?.onTrackUnsubscribed(track, publication, this) | 155 | listener?.onTrackUnsubscribed(track, publication, this) |
| 156 | + eventBus.postEvent(ParticipantEvent.TrackUnsubscribed(this, track, publication), scope) | ||
| 149 | } | 157 | } |
| 150 | if (sendUnpublish) { | 158 | if (sendUnpublish) { |
| 151 | internalListener?.onTrackUnpublished(publication, this) | 159 | internalListener?.onTrackUnpublished(publication, this) |
| 152 | listener?.onTrackUnpublished(publication, this) | 160 | listener?.onTrackUnpublished(publication, this) |
| 161 | + eventBus.postEvent(ParticipantEvent.TrackUnpublished(this, publication), scope) | ||
| 153 | } | 162 | } |
| 154 | } | 163 | } |
| 155 | 164 | ||
| 165 | + // Internal methods just for posting events. | ||
| 166 | + internal fun onDataReceived(data: ByteArray) { | ||
| 167 | + listener?.onDataReceived(data, this) | ||
| 168 | + eventBus.postEvent(ParticipantEvent.DataReceived(this, data), scope) | ||
| 169 | + } | ||
| 170 | + | ||
| 156 | companion object { | 171 | companion object { |
| 157 | private const val KIND_AUDIO = "audio" | 172 | private const val KIND_AUDIO = "audio" |
| 158 | private const val KIND_VIDEO = "video" | 173 | private const val KIND_VIDEO = "video" |
| @@ -31,11 +31,9 @@ class LocalTrackPublication( | @@ -31,11 +31,9 @@ class LocalTrackPublication( | ||
| 31 | participant.engine.updateMuteStatus(sid, muted) | 31 | participant.engine.updateMuteStatus(sid, muted) |
| 32 | 32 | ||
| 33 | if (muted) { | 33 | if (muted) { |
| 34 | - participant.listener?.onTrackMuted(this, participant) | ||
| 35 | - participant.internalListener?.onTrackMuted(this, participant) | 34 | + participant.onTrackMuted(this) |
| 36 | } else { | 35 | } else { |
| 37 | - participant.listener?.onTrackUnmuted(this, participant) | ||
| 38 | - participant.internalListener?.onTrackUnmuted(this, participant) | 36 | + participant.onTrackUnmuted(this) |
| 39 | } | 37 | } |
| 40 | } | 38 | } |
| 41 | } | 39 | } |
| @@ -77,11 +77,9 @@ class RemoteTrackPublication( | @@ -77,11 +77,9 @@ class RemoteTrackPublication( | ||
| 77 | field = v | 77 | field = v |
| 78 | val participant = this.participant.get() as? RemoteParticipant ?: return | 78 | val participant = this.participant.get() as? RemoteParticipant ?: return |
| 79 | if (v) { | 79 | if (v) { |
| 80 | - participant.listener?.onTrackMuted(this, participant) | ||
| 81 | - participant.internalListener?.onTrackMuted(this, participant) | 80 | + participant.onTrackMuted(this) |
| 82 | } else { | 81 | } else { |
| 83 | - participant.listener?.onTrackUnmuted(this, participant) | ||
| 84 | - participant.internalListener?.onTrackUnmuted(this, participant) | 82 | + participant.onTrackUnmuted(this) |
| 85 | } | 83 | } |
| 86 | } | 84 | } |
| 87 | 85 |
| 1 | package io.livekit.android.room.participant | 1 | package io.livekit.android.room.participant |
| 2 | 2 | ||
| 3 | +import io.livekit.android.coroutines.TestCoroutineRule | ||
| 4 | +import io.livekit.android.events.EventCollector | ||
| 5 | +import io.livekit.android.events.ParticipantEvent | ||
| 3 | import io.livekit.android.room.track.TrackPublication | 6 | import io.livekit.android.room.track.TrackPublication |
| 4 | import livekit.LivekitModels | 7 | import livekit.LivekitModels |
| 5 | import org.junit.Assert.assertEquals | 8 | import org.junit.Assert.assertEquals |
| 6 | import org.junit.Assert.assertTrue | 9 | import org.junit.Assert.assertTrue |
| 7 | import org.junit.Before | 10 | import org.junit.Before |
| 11 | +import org.junit.Rule | ||
| 8 | import org.junit.Test | 12 | import org.junit.Test |
| 9 | 13 | ||
| 10 | class ParticipantTest { | 14 | class ParticipantTest { |
| 11 | 15 | ||
| 16 | + @get:Rule | ||
| 17 | + var coroutineRule = TestCoroutineRule() | ||
| 18 | + | ||
| 12 | lateinit var participant: Participant | 19 | lateinit var participant: Participant |
| 13 | 20 | ||
| 14 | @Before | 21 | @Before |
| 15 | fun setup() { | 22 | fun setup() { |
| 16 | - participant = Participant("", null) | 23 | + participant = Participant("", null, coroutineRule.dispatcher) |
| 17 | } | 24 | } |
| 18 | 25 | ||
| 19 | @Test | 26 | @Test |
| @@ -58,7 +65,41 @@ class ParticipantTest { | @@ -58,7 +65,41 @@ class ParticipantTest { | ||
| 58 | 65 | ||
| 59 | checkValues(publicListener) | 66 | checkValues(publicListener) |
| 60 | checkValues(internalListener) | 67 | checkValues(internalListener) |
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Test | ||
| 71 | + fun setMetadataChangedEvent() { | ||
| 72 | + val eventCollector = EventCollector(participant.events, coroutineRule.scope) | ||
| 73 | + val prevMetadata = participant.metadata | ||
| 74 | + val metadata = "metadata" | ||
| 75 | + participant.metadata = metadata | ||
| 76 | + | ||
| 77 | + val events = eventCollector.stopCollectingEvents() | ||
| 78 | + | ||
| 79 | + assertEquals(1, events.size) | ||
| 80 | + assertEquals(true, events[0] is ParticipantEvent.MetadataChanged) | ||
| 81 | + | ||
| 82 | + val event = events[0] as ParticipantEvent.MetadataChanged | ||
| 83 | + | ||
| 84 | + assertEquals(prevMetadata, event.prevMetadata) | ||
| 85 | + assertEquals(participant, event.participant) | ||
| 86 | + } | ||
| 87 | + | ||
| 88 | + @Test | ||
| 89 | + fun setIsSpeakingChangedEvent() { | ||
| 90 | + val eventCollector = EventCollector(participant.events, coroutineRule.scope) | ||
| 91 | + val newIsSpeaking = !participant.isSpeaking | ||
| 92 | + participant.isSpeaking = newIsSpeaking | ||
| 93 | + | ||
| 94 | + val events = eventCollector.stopCollectingEvents() | ||
| 95 | + | ||
| 96 | + assertEquals(1, events.size) | ||
| 97 | + assertEquals(true, events[0] is ParticipantEvent.SpeakingChanged) | ||
| 98 | + | ||
| 99 | + val event = events[0] as ParticipantEvent.SpeakingChanged | ||
| 61 | 100 | ||
| 101 | + assertEquals(participant, event.participant) | ||
| 102 | + assertEquals(newIsSpeaking, event.isSpeaking) | ||
| 62 | } | 103 | } |
| 63 | 104 | ||
| 64 | @Test | 105 | @Test |
| @@ -23,7 +23,8 @@ class RemoteParticipantTest { | @@ -23,7 +23,8 @@ class RemoteParticipantTest { | ||
| 23 | participant = RemoteParticipant( | 23 | participant = RemoteParticipant( |
| 24 | "sid", | 24 | "sid", |
| 25 | signalClient = signalClient, | 25 | signalClient = signalClient, |
| 26 | - ioDispatcher = coroutineRule.dispatcher | 26 | + ioDispatcher = coroutineRule.dispatcher, |
| 27 | + defaultdispatcher = coroutineRule.dispatcher, | ||
| 27 | ) | 28 | ) |
| 28 | } | 29 | } |
| 29 | 30 | ||
| @@ -33,7 +34,12 @@ class RemoteParticipantTest { | @@ -33,7 +34,12 @@ class RemoteParticipantTest { | ||
| 33 | .addTracks(TRACK_INFO) | 34 | .addTracks(TRACK_INFO) |
| 34 | .build() | 35 | .build() |
| 35 | 36 | ||
| 36 | - participant = RemoteParticipant(info, signalClient, ioDispatcher = coroutineRule.dispatcher) | 37 | + participant = RemoteParticipant( |
| 38 | + info, | ||
| 39 | + signalClient, | ||
| 40 | + ioDispatcher = coroutineRule.dispatcher, | ||
| 41 | + defaultdispatcher = coroutineRule.dispatcher, | ||
| 42 | + ) | ||
| 37 | 43 | ||
| 38 | assertEquals(1, participant.tracks.values.size) | 44 | assertEquals(1, participant.tracks.values.size) |
| 39 | assertNotNull(participant.getTrackPublication(TRACK_INFO.sid)) | 45 | assertNotNull(participant.getTrackPublication(TRACK_INFO.sid)) |
-
请 注册 或 登录 后发表评论