Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
xuning
/
livekitAndroidXuningTest
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
David Zhao
2021-03-25 14:22:34 -0700
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
6cb172f13fcefea72005de9316df375c70c07900
6cb172f1
1 parent
bf5c21ec
also fire events off of the Participant
隐藏空白字符变更
内嵌
并排对比
正在显示
9 个修改的文件
包含
219 行增加
和
179 行删除
livekit-android-sdk/src/main/java/io/livekit/android/LiveKit.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/participant/LocalParticipant.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Participant.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/participant/RemoteParticipant.kt
sample-app/src/main/java/io/livekit/android/sample/CallActivity.kt
sample-app/src/main/java/io/livekit/android/sample/CallViewModel.kt
sample-app/src/main/java/io/livekit/android/sample/ParticipantItem.kt
livekit-android-sdk/src/main/java/io/livekit/android/LiveKit.kt
查看文件 @
6cb172f
...
...
@@ -3,6 +3,7 @@ package io.livekit.android
import android.content.Context
import io.livekit.android.dagger.DaggerLiveKitComponent
import io.livekit.android.room.Room
import io.livekit.android.room.RoomListener
import io.livekit.android.room.track.LocalAudioTrack
import io.livekit.android.room.track.LocalVideoTrack
import org.webrtc.EglBase
...
...
@@ -21,7 +22,7 @@ class LiveKit {
url: String,
token: String,
options: ConnectOptions,
listener: Room
.
Listener?
listener: RoomListener?
): Room {
val component = DaggerLiveKitComponent
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt
查看文件 @
6cb172f
...
...
@@ -26,7 +26,6 @@ import kotlin.coroutines.suspendCoroutine
class RTCEngine
@Inject
constructor(
private val appContext: Context,
val client: RTCClient,
pctFactory: PeerConnectionTransport.Factory,
@Named(InjectionNames.DISPATCHER_IO) ioDispatcher: CoroutineDispatcher,
...
...
@@ -190,6 +189,7 @@ constructor(
override fun onAnswer(sessionDescription: SessionDescription) {
Timber.v { "received server answer: ${sessionDescription.type}, ${publisher.peerConnection.signalingState()}" }
coroutineScope.launch {
Timber.i { sessionDescription.toString() }
when (val outcome = publisher.peerConnection.setRemoteDescription(sessionDescription)) {
is Either.Left -> {
if (!rtcConnected) {
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/Room.kt
查看文件 @
6cb172f
package io.livekit.android.room
import com.github.ajalt.timberkt.Timber
import com.vdurmont.semver4j.Semver
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import io.livekit.android.ConnectOptions
import io.livekit.android.room.participant.LocalParticipant
import io.livekit.android.room.participant.Participant
import io.livekit.android.room.participant.ParticipantListener
import io.livekit.android.room.participant.RemoteParticipant
import io.livekit.android.room.track.DataTrack
import io.livekit.android.room.track.Track
...
...
@@ -27,7 +27,7 @@ constructor(
@Assisted private val connectOptions: ConnectOptions,
private val engine: RTCEngine,
private val eglBase: EglBase,
) : RTCEngine.Listener,
RemoteParticipant.
Listener {
) : RTCEngine.Listener,
Participant
Listener {
init {
engine.listener = this
}
...
...
@@ -41,7 +41,7 @@ constructor(
inline class Sid(val sid: String)
var listener: Listener? = null
var listener:
Room
Listener? = null
var sid: Sid? = null
private set
...
...
@@ -95,7 +95,7 @@ constructor(
} else {
RemoteParticipant(sid, null)
}
participant.
l
istener = this
participant.
internalL
istener = this
mutableRemoteParticipants[sid] = participant
return participant
}
...
...
@@ -108,13 +108,15 @@ constructor(
val speakerSid = speakerInfo.sid!!
seenSids.add(speakerSid)
if (speakerSid == localParticipant
?
.sid) {
if (speakerSid == localParticipant.sid) {
localParticipant.audioLevel = speakerInfo.level
localParticipant.isSpeaking = true
speakers.add(localParticipant)
} else {
val participant = remoteParticipants[speakerSid]
if (participant != null) {
participant.audioLevel = speakerInfo.level
participant.isSpeaking = true
speakers.add(participant)
}
}
...
...
@@ -122,10 +124,14 @@ constructor(
if (!seenSids.contains(localParticipant.sid)) {
localParticipant.audioLevel = 0.0f
localParticipant.isSpeaking = false
}
remoteParticipants.values
.filterNot { seenSids.contains(it.sid) }
.forEach { it.audioLevel = 0.0f }
.forEach {
it.audioLevel = 0.0f
it.isSpeaking = false
}
mutableActiveSpeakers.clear()
mutableActiveSpeakers.addAll(speakers)
...
...
@@ -296,86 +302,86 @@ constructor(
viewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
viewRenderer.setEnableHardwareScaler(false /* enabled */);
}
}
/**
* Room Listener, this class provides callbacks that clients should override.
*
*/
interface RoomListener {
/**
* Disconnected from room
*/
fun onDisconnect(room: Room, error: Exception?) {}
/**
* When a [RemoteParticipant] joins after the local participant. It will not emit events
* for participants that are already in the room
*/
fun onParticipantConnected(room: Room, participant: RemoteParticipant) {}
/**
* When a [RemoteParticipant] leaves after the local participant has joined.
*/
fun onParticipantDisconnected(room: Room, participant: RemoteParticipant) {}
/**
* Room Listener, this class provides callbacks that clients should override.
*
* Could not connect to the room
*/
interface Listener {
/**
* Disconnected from room
*/
fun onDisconnect(room: Room, error: Exception?) {}
/**
* When a [RemoteParticipant] joins after the local participant. It will not emit events
* for participants that are already in the room
*/
fun onParticipantConnected(room: Room, participant: RemoteParticipant) {}
/**
* When a [RemoteParticipant] leaves after the local participant has joined.
*/
fun onParticipantDisconnected(room: Room, participant: RemoteParticipant) {}
/**
* Could not connect to the room
*/
fun onFailedToConnect(room: Room, error: Exception) {}
fun onFailedToConnect(room: Room, error: Exception) {}
// fun onReconnecting(room: Room, error: Exception) {}
// fun onReconnect(room: Room) {}
/**
* Active speakers changed. List of speakers are ordered by their audio level. loudest
* speakers first. This will include the [LocalParticipant] too.
*/
fun onActiveSpeakersChanged(speakers: List<Participant>, room: Room) {}
// Participant callbacks
/**
* Participant metadata is a simple way for app-specific state to be pushed to all users.
* When RoomService.UpdateParticipantMetadata is called to change a participant's state,
* this event will be fired for all clients in the room.
*/
fun onMetadataChanged(Participant: Participant, prevMetadata: String?, room: Room) {}
/**
* When a new track is published to room after the local participant has joined. It will
* not fire for tracks that are already published
*/
fun onTrackPublished(publication: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* A [RemoteParticipant] has unpublished a track
*/
fun onTrackUnpublished(publication: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* The [LocalParticipant] has subscribed to a new track. This event will always fire as
* long as new tracks are ready for use.
*/
fun onTrackSubscribed(track: Track, publication: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* Could not subscribe to a track
*/
fun onTrackSubscriptionFailed(sid: String, exception: Exception, participant: RemoteParticipant, room: Room) {}
/**
* A subscribed track is no longer available. Clients should listen to this event and ensure
* the track removes all renderers
*/
fun onTrackUnsubscribed(track: Track, publications: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* Message received over a [DataTrack]
*/
fun onDataReceived(data: ByteBuffer, dataTrack: DataTrack, participant: RemoteParticipant, room: Room) {}
}
/**
* Active speakers changed. List of speakers are ordered by their audio level. loudest
* speakers first. This will include the [LocalParticipant] too.
*/
fun onActiveSpeakersChanged(speakers: List<Participant>, room: Room) {}
// Participant callbacks
/**
* Participant metadata is a simple way for app-specific state to be pushed to all users.
* When RoomService.UpdateParticipantMetadata is called to change a participant's state,
* this event will be fired for all clients in the room.
*/
fun onMetadataChanged(participant: Participant, prevMetadata: String?, room: Room) {}
/**
* When a new track is published to room after the local participant has joined. It will
* not fire for tracks that are already published
*/
fun onTrackPublished(publication: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* A [RemoteParticipant] has unpublished a track
*/
fun onTrackUnpublished(publication: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* The [LocalParticipant] has subscribed to a new track. This event will always fire as
* long as new tracks are ready for use.
*/
fun onTrackSubscribed(track: Track, publication: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* Could not subscribe to a track
*/
fun onTrackSubscriptionFailed(sid: String, exception: Exception, participant: RemoteParticipant, room: Room) {}
/**
* A subscribed track is no longer available. Clients should listen to this event and ensure
* the track removes all renderers
*/
fun onTrackUnsubscribed(track: Track, publications: TrackPublication, participant: RemoteParticipant, room: Room) {}
/**
* Message received over a [DataTrack]
*/
fun onDataReceived(data: ByteBuffer, dataTrack: DataTrack, participant: RemoteParticipant, room: Room) {}
}
sealed class RoomException(message: String? = null, cause: Throwable? = null) :
Exception(message, cause) {
class ConnectException(message: String? = null, cause: Throwable? = null) :
RoomException(message, cause)
}
\ No newline at end of file
}
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/participant/LocalParticipant.kt
查看文件 @
6cb172f
...
...
@@ -15,15 +15,9 @@ class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine:
updateFromInfo(info)
}
val localTrackPublications
private
val localTrackPublications
get() = tracks.values.toList()
var listener: Listener? = null
set(v) {
field = v
participantListener = v
}
suspend fun publishAudioTrack(
track: LocalAudioTrack,
publishListener: PublishListener? = null
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Participant.kt
查看文件 @
6cb172f
...
...
@@ -2,6 +2,7 @@ package io.livekit.android.room.participant
import io.livekit.android.room.track.*
import livekit.LivekitModels
import java.nio.ByteBuffer
open class Participant(var sid: String, identity: String? = null) {
var participantInfo: LivekitModels.ParticipantInfo? = null
...
...
@@ -10,8 +11,27 @@ open class Participant(var sid: String, identity: String? = null) {
internal set
var audioLevel: Float = 0f
internal set
var isSpeaking: Boolean = false
internal set(v) {
val changed = v == field
field = v
if (changed) {
listener?.onSpeakingChanged(this)
internalListener?.onSpeakingChanged(this)
}
}
var metadata: String? = null
var participantListener: Listener? = null
/**
* Listener for when participant properties change
*/
var listener: ParticipantListener? = null
/**
* @suppress
*/
internal var internalListener: ParticipantListener? = null
val hasInfo
get() = participantInfo != null
...
...
@@ -49,7 +69,8 @@ open class Participant(var sid: String, identity: String? = null) {
metadata = info.metadata
if (prevMetadata != metadata) {
participantListener?.onMetadataChanged(this, prevMetadata)
listener?.onMetadataChanged(this, prevMetadata)
internalListener?.onMetadataChanged(this, prevMetadata)
}
}
...
...
@@ -67,8 +88,48 @@ open class Participant(var sid: String, identity: String? = null) {
override fun hashCode(): Int {
return sid.hashCode()
}
}
interface Listener {
fun onMetadataChanged(participant: Participant, prevMetadata: String?) {}
interface ParticipantListener {
/**
* When a participant's metadata is updated, fired for all participants
*/
fun onMetadataChanged(participant: Participant, prevMetadata: String?) {}
/**
* Fired when the current participant's isSpeaking property changes. (including LocalParticipant)
*/
fun onSpeakingChanged(participant: Participant) {}
fun onTrackPublished(publication: TrackPublication, participant: RemoteParticipant) {}
fun onTrackUnpublished(publication: TrackPublication, participant: RemoteParticipant) {}
fun onEnable(publication: TrackPublication, participant: RemoteParticipant) {}
fun onDisable(publication: TrackPublication, participant: RemoteParticipant) {}
fun onTrackSubscribed(track: Track, publication: TrackPublication, participant: RemoteParticipant) {}
fun onTrackSubscriptionFailed(
sid: String,
exception: Exception,
participant: RemoteParticipant
) {
}
fun onTrackUnsubscribed(
track: Track,
publication: TrackPublication,
participant: RemoteParticipant
) {
}
fun onDataReceived(
data: ByteBuffer,
dataTrack: DataTrack,
participant: RemoteParticipant
) {
}
fun switchedOffVideo(track: VideoTrack, publication: TrackPublication, participant: RemoteParticipant) {}
fun switchedOnVideo(track: VideoTrack, publication: TrackPublication, participant: RemoteParticipant) {}
}
\ No newline at end of file
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/participant/RemoteParticipant.kt
查看文件 @
6cb172f
...
...
@@ -23,12 +23,6 @@ class RemoteParticipant(
updateFromInfo(info)
}
var listener: Listener? = null
set(v) {
field = v
participantListener = v
}
private val coroutineScope = CloseableCoroutineScope(SupervisorJob())
fun getTrackPublication(sid: String): TrackPublication? = tracks[sid]
...
...
@@ -61,6 +55,7 @@ class RemoteParticipant(
if (hadInfo) {
for (publication in newTrackPublications.values) {
internalListener?.onTrackPublished(publication, this)
listener?.onTrackPublished(publication, this)
}
}
...
...
@@ -89,6 +84,7 @@ class RemoteParticipant(
val exception = TrackException.InvalidTrackStateException(message)
Timber.e { "remote participant ${this.sid} --- $message" }
internalListener?.onTrackSubscriptionFailed(sid, exception, this)
listener?.onTrackSubscriptionFailed(sid, exception, this)
} else {
coroutineScope.launch {
...
...
@@ -106,6 +102,7 @@ class RemoteParticipant(
// TODO: how does mediatrack send ended event?
internalListener?.onTrackSubscribed(track, publication, this)
listener?.onTrackSubscribed(track, publication, this)
}
...
...
@@ -125,6 +122,7 @@ class RemoteParticipant(
publication = TrackPublication(info = trackInfo)
addTrackPublication(publication)
if (hasInfo) {
internalListener?.onTrackPublished(publication, this)
listener?.onTrackPublished(publication, this)
}
}
...
...
@@ -137,15 +135,18 @@ class RemoteParticipant(
val newState = dataChannel.state()
if (newState == DataChannel.State.CLOSED) {
publication.track = null
internalListener?.onTrackUnsubscribed(track, publication, this@RemoteParticipant)
listener?.onTrackUnsubscribed(track, publication, this@RemoteParticipant)
}
}
override fun onMessage(buffer: DataChannel.Buffer) {
internalListener?.onDataReceived(buffer.data, track, this@RemoteParticipant)
listener?.onDataReceived(buffer.data, track, this@RemoteParticipant)
}
})
listener?.onTrackSubscribed(track, publication, participant = this)
internalListener?.onTrackSubscribed(track, publication, participant = this)
listener?.onTrackSubscribed(track, publication, this)
}
fun unpublishTrack(trackSid: String, sendUnpublish: Boolean = false) {
...
...
@@ -160,9 +161,11 @@ class RemoteParticipant(
val track = publication.track
if (track != null) {
track.stop()
internalListener?.onTrackUnsubscribed(track, publication, this)
listener?.onTrackUnsubscribed(track, publication, this)
}
if (sendUnpublish) {
internalListener?.onTrackUnpublished(publication, this)
listener?.onTrackUnpublished(publication, this)
}
}
...
...
@@ -171,38 +174,4 @@ class RemoteParticipant(
private const val KIND_AUDIO = "audio"
private const val KIND_VIDEO = "video"
}
interface Listener: Participant.Listener {
fun onTrackPublished(publication: TrackPublication, participant: RemoteParticipant) {}
fun onTrackUnpublished(publication: TrackPublication, participant: RemoteParticipant) {}
fun onEnable(publication: TrackPublication, participant: RemoteParticipant) {}
fun onDisable(publication: TrackPublication, participant: RemoteParticipant) {}
fun onTrackSubscribed(track: Track, publication: TrackPublication, participant: RemoteParticipant) {}
fun onTrackSubscriptionFailed(
sid: String,
exception: Exception,
participant: RemoteParticipant
) {
}
fun onTrackUnsubscribed(
track: Track,
publication: TrackPublication,
participant: RemoteParticipant
) {
}
fun onDataReceived(
data: ByteBuffer,
dataTrack: DataTrack,
participant: RemoteParticipant
) {
}
fun switchedOffVideo(track: VideoTrack, publication: TrackPublication, participant: RemoteParticipant) {}
fun switchedOnVideo(track: VideoTrack, publication: TrackPublication, participant: RemoteParticipant) {}
}
}
\ No newline at end of file
}
...
...
sample-app/src/main/java/io/livekit/android/sample/CallActivity.kt
查看文件 @
6cb172f
...
...
@@ -41,7 +41,6 @@ class CallActivity : AppCompatActivity() {
viewModel.remoteParticipants
) { room, participants -> room to participants }
.observe(this) {
tabLayoutMediator?.detach()
tabLayoutMediator = null
...
...
@@ -58,13 +57,10 @@ class CallActivity : AppCompatActivity() {
viewModel.room.observe(this) { room ->
room.initVideoRenderer(binding.pipVideoView)
val localParticipant = room.localParticipant
if (localParticipant != null) {
val videoTrack = localParticipant.videoTracks.values
.firstOrNull()
?.track as? LocalVideoTrack
videoTrack?.addRenderer(binding.pipVideoView)
}
val videoTrack = room.localParticipant.videoTracks.values
.firstOrNull()
?.track as? LocalVideoTrack
videoTrack?.addRenderer(binding.pipVideoView)
}
val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
with(audioManager) {
...
...
sample-app/src/main/java/io/livekit/android/sample/CallViewModel.kt
查看文件 @
6cb172f
...
...
@@ -5,9 +5,11 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.github.ajalt.timberkt.Timber
import io.livekit.android.ConnectOptions
import io.livekit.android.LiveKit
import io.livekit.android.room.Room
import io.livekit.android.room.RoomListener
import io.livekit.android.room.participant.Participant
import io.livekit.android.room.participant.RemoteParticipant
import kotlinx.coroutines.launch
...
...
@@ -16,57 +18,34 @@ class CallViewModel(
val url: String,
val token: String,
application: Application
) : AndroidViewModel(application) {
) : AndroidViewModel(application), RoomListener {
private val mutableRoom = MutableLiveData<Room>()
val room: LiveData<Room> = mutableRoom
private val mutableRemoteParticipants = MutableLiveData<List<RemoteParticipant>>()
val remoteParticipants: LiveData<List<RemoteParticipant>> = mutableRemoteParticipants
private val participants = HashMap<String, LiveData<RemoteParticipant>>()
init {
viewModelScope.launch {
mutableRoom.value = LiveKit.connect(
val room = LiveKit.connect(
application,
url,
token,
ConnectOptions(false),
object : Room.Listener {
override fun onDisconnect(room: Room, error: Exception?) {
}
override fun onParticipantConnected(
room: Room,
participant: RemoteParticipant
) {
updateParticipants(room)
}
override fun onParticipantDisconnected(
room: Room,
participant: RemoteParticipant
) {
updateParticipants(room)
}
override fun onFailedToConnect(room: Room, error: Exception) {
}
override fun onActiveSpeakersChanged(speakers: List<Participant>, room: Room) {
}
override fun onMetadataChanged(Participant: Participant, prevMetadata: String?, room: Room) {
}
}
this@CallViewModel
)
updateParticipants(mutableRoom.value!!)
updateParticipants(room)
for (p in room.remoteParticipants.values) {
if (p.identity == null) {
continue
}
participants[p.identity!!] = MutableLiveData(p)
}
mutableRoom.value = room
}
}
fun updateParticipants(room: Room) {
private
fun updateParticipants(room: Room) {
mutableRemoteParticipants.postValue(
room.remoteParticipants
.keys
...
...
@@ -75,9 +54,43 @@ class CallViewModel(
)
}
fun getParticipantObservable(identity: String): LiveData<RemoteParticipant>? {
return participants[identity]
}
override fun onCleared() {
super.onCleared()
mutableRoom.value?.disconnect()
mutableRoom.value = null
}
override fun onDisconnect(room: Room, error: Exception?) {
}
override fun onParticipantConnected(
room: Room,
participant: RemoteParticipant
) {
updateParticipants(room)
participants[participant.identity!!] = MutableLiveData(participant)
}
override fun onParticipantDisconnected(
room: Room,
participant: RemoteParticipant
) {
updateParticipants(room)
participants.remove(participant.identity!!)
}
override fun onFailedToConnect(room: Room, error: Exception) {
}
override fun onActiveSpeakersChanged(speakers: List<Participant>, room: Room) {
}
override fun onMetadataChanged(participant: Participant, prevMetadata: String?, room: Room) {
Timber.i { "Participant metadata changed: ${participant.identity}" }
}
}
...
...
sample-app/src/main/java/io/livekit/android/sample/ParticipantItem.kt
查看文件 @
6cb172f
package io.livekit.android.sample
import android.provider.MediaStore
import android.view.View
import com.github.ajalt.timberkt.Timber
import com.xwray.groupie.viewbinding.BindableItem
import com.xwray.groupie.viewbinding.GroupieViewHolder
import io.livekit.android.room.Room
import io.livekit.android.room.participant.ParticipantListener
import io.livekit.android.room.participant.RemoteParticipant
import io.livekit.android.room.track.*
import io.livekit.android.sample.databinding.ParticipantItemBinding
...
...
@@ -27,7 +27,7 @@ class ParticipantItem(
override fun bind(viewBinding: ParticipantItemBinding, position: Int) {
viewBinding.run {
remoteParticipant.listener = object :
RemoteParticipant.
Listener {
remoteParticipant.listener = object :
Participant
Listener {
override fun onTrackSubscribed(
track: Track,
publication: TrackPublication,
...
...
@@ -51,7 +51,7 @@ class ParticipantItem(
.firstOrNull()?.track as? VideoTrack
}
private
fun setupVideoIfNeeded(videoTrack: VideoTrack, viewBinding: ParticipantItemBinding) {
internal
fun setupVideoIfNeeded(videoTrack: VideoTrack, viewBinding: ParticipantItemBinding) {
if (videoBound) {
return
}
...
...
请
注册
或
登录
后发表评论