David Liu

speaker view

... ... @@ -10,8 +10,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.ajalt.timberkt.Timber
import com.snakydesign.livedataextensions.combineLatest
import com.snakydesign.livedataextensions.scan
import com.snakydesign.livedataextensions.take
import com.xwray.groupie.GroupieAdapter
import io.livekit.android.room.track.LocalVideoTrack
import io.livekit.android.room.participant.Participant
import io.livekit.android.room.track.VideoTrack
import io.livekit.android.sample.databinding.CallActivityBinding
import kotlinx.parcelize.Parcelize
... ... @@ -68,15 +71,26 @@ class CallActivity : AppCompatActivity() {
}
// speaker view setup
viewModel.room.observe(this) { room ->
viewModel.room.take(1).observe(this) { room ->
room.initVideoRenderer(binding.speakerView)
val videoTrack = room.localParticipant.videoTracks.values
.firstOrNull()
?.track as? LocalVideoTrack
videoTrack?.let {
it.addRenderer(binding.speakerView)
}
viewModel.activeSpeaker
.scan(Pair<Participant?, Participant?>(null, null)) { pair, participant ->
// old participant is first
// latest active participant is second
Pair(pair.second, participant)
}.observe(this) { (oldSpeaker, newSpeaker) ->
// Remove any renderering from the old speaker
oldSpeaker?.videoTracks
?.values
?.forEach { trackPublication ->
(trackPublication.track as? VideoTrack)?.removeRenderer(binding.speakerView)
}
val videoTrack = newSpeaker?.videoTracks?.values
?.firstOrNull()
?.track as? VideoTrack
videoTrack?.addRenderer(binding.speakerView)
}
}
// Controls setup
... ...
... ... @@ -12,6 +12,7 @@ import io.livekit.android.events.RoomEvent
import io.livekit.android.events.collect
import io.livekit.android.room.Room
import io.livekit.android.room.participant.Participant
import io.livekit.android.room.participant.RemoteParticipant
import io.livekit.android.room.track.CameraPosition
import io.livekit.android.room.track.LocalVideoTrack
import io.livekit.android.room.track.Track
... ... @@ -81,13 +82,18 @@ class CallViewModel(
}
private fun updateParticipants(room: Room) {
mutableParticipants.postValue(
listOf(room.localParticipant) +
room.remoteParticipants
.keys
.sortedBy { it }
.mapNotNull { room.remoteParticipants[it] }
)
val participantList = listOf(room.localParticipant) +
room.remoteParticipants
.keys
.sortedBy { it }
.mapNotNull { room.remoteParticipants[it] }
mutableParticipants.postValue(participantList)
if (!participantList.contains(mutableActiveSpeaker.value) || mutableActiveSpeaker.value == null) {
// active speaker has left, choose someone else at random.
mutableActiveSpeaker.postValue(participantList.last())
}
}
fun handleActiveSpeakersChanged(speakers: List<Participant>) {
... ... @@ -95,7 +101,9 @@ class CallViewModel(
if (speakers.isEmpty() || speakers.contains(mutableActiveSpeaker.value)) {
return
}
val newSpeaker = speakers.firstOrNull() ?: return
val newSpeaker = speakers
.filter { it is RemoteParticipant } // Try not to display local participant as speaker.
.firstOrNull() ?: return
mutableActiveSpeaker.postValue(newSpeaker)
}
... ...