正在显示
2 个修改的文件
包含
39 行增加
和
17 行删除
| @@ -10,8 +10,11 @@ import androidx.appcompat.app.AppCompatActivity | @@ -10,8 +10,11 @@ import androidx.appcompat.app.AppCompatActivity | ||
| 10 | import androidx.recyclerview.widget.LinearLayoutManager | 10 | import androidx.recyclerview.widget.LinearLayoutManager |
| 11 | import com.github.ajalt.timberkt.Timber | 11 | import com.github.ajalt.timberkt.Timber |
| 12 | import com.snakydesign.livedataextensions.combineLatest | 12 | import com.snakydesign.livedataextensions.combineLatest |
| 13 | +import com.snakydesign.livedataextensions.scan | ||
| 14 | +import com.snakydesign.livedataextensions.take | ||
| 13 | import com.xwray.groupie.GroupieAdapter | 15 | import com.xwray.groupie.GroupieAdapter |
| 14 | -import io.livekit.android.room.track.LocalVideoTrack | 16 | +import io.livekit.android.room.participant.Participant |
| 17 | +import io.livekit.android.room.track.VideoTrack | ||
| 15 | import io.livekit.android.sample.databinding.CallActivityBinding | 18 | import io.livekit.android.sample.databinding.CallActivityBinding |
| 16 | import kotlinx.parcelize.Parcelize | 19 | import kotlinx.parcelize.Parcelize |
| 17 | 20 | ||
| @@ -68,15 +71,26 @@ class CallActivity : AppCompatActivity() { | @@ -68,15 +71,26 @@ class CallActivity : AppCompatActivity() { | ||
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | // speaker view setup | 73 | // speaker view setup |
| 71 | - viewModel.room.observe(this) { room -> | 74 | + viewModel.room.take(1).observe(this) { room -> |
| 72 | room.initVideoRenderer(binding.speakerView) | 75 | room.initVideoRenderer(binding.speakerView) |
| 73 | - val videoTrack = room.localParticipant.videoTracks.values | ||
| 74 | - .firstOrNull() | ||
| 75 | - ?.track as? LocalVideoTrack | ||
| 76 | - | ||
| 77 | - videoTrack?.let { | ||
| 78 | - it.addRenderer(binding.speakerView) | ||
| 79 | - } | 76 | + viewModel.activeSpeaker |
| 77 | + .scan(Pair<Participant?, Participant?>(null, null)) { pair, participant -> | ||
| 78 | + // old participant is first | ||
| 79 | + // latest active participant is second | ||
| 80 | + Pair(pair.second, participant) | ||
| 81 | + }.observe(this) { (oldSpeaker, newSpeaker) -> | ||
| 82 | + // Remove any renderering from the old speaker | ||
| 83 | + oldSpeaker?.videoTracks | ||
| 84 | + ?.values | ||
| 85 | + ?.forEach { trackPublication -> | ||
| 86 | + (trackPublication.track as? VideoTrack)?.removeRenderer(binding.speakerView) | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + val videoTrack = newSpeaker?.videoTracks?.values | ||
| 90 | + ?.firstOrNull() | ||
| 91 | + ?.track as? VideoTrack | ||
| 92 | + videoTrack?.addRenderer(binding.speakerView) | ||
| 93 | + } | ||
| 80 | } | 94 | } |
| 81 | 95 | ||
| 82 | // Controls setup | 96 | // Controls setup |
| @@ -12,6 +12,7 @@ import io.livekit.android.events.RoomEvent | @@ -12,6 +12,7 @@ import io.livekit.android.events.RoomEvent | ||
| 12 | import io.livekit.android.events.collect | 12 | import io.livekit.android.events.collect |
| 13 | import io.livekit.android.room.Room | 13 | import io.livekit.android.room.Room |
| 14 | import io.livekit.android.room.participant.Participant | 14 | import io.livekit.android.room.participant.Participant |
| 15 | +import io.livekit.android.room.participant.RemoteParticipant | ||
| 15 | import io.livekit.android.room.track.CameraPosition | 16 | import io.livekit.android.room.track.CameraPosition |
| 16 | import io.livekit.android.room.track.LocalVideoTrack | 17 | import io.livekit.android.room.track.LocalVideoTrack |
| 17 | import io.livekit.android.room.track.Track | 18 | import io.livekit.android.room.track.Track |
| @@ -81,13 +82,18 @@ class CallViewModel( | @@ -81,13 +82,18 @@ class CallViewModel( | ||
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | private fun updateParticipants(room: Room) { | 84 | private fun updateParticipants(room: Room) { |
| 84 | - mutableParticipants.postValue( | ||
| 85 | - listOf(room.localParticipant) + | ||
| 86 | - room.remoteParticipants | ||
| 87 | - .keys | ||
| 88 | - .sortedBy { it } | ||
| 89 | - .mapNotNull { room.remoteParticipants[it] } | ||
| 90 | - ) | 85 | + |
| 86 | + val participantList = listOf(room.localParticipant) + | ||
| 87 | + room.remoteParticipants | ||
| 88 | + .keys | ||
| 89 | + .sortedBy { it } | ||
| 90 | + .mapNotNull { room.remoteParticipants[it] } | ||
| 91 | + mutableParticipants.postValue(participantList) | ||
| 92 | + | ||
| 93 | + if (!participantList.contains(mutableActiveSpeaker.value) || mutableActiveSpeaker.value == null) { | ||
| 94 | + // active speaker has left, choose someone else at random. | ||
| 95 | + mutableActiveSpeaker.postValue(participantList.last()) | ||
| 96 | + } | ||
| 91 | } | 97 | } |
| 92 | 98 | ||
| 93 | fun handleActiveSpeakersChanged(speakers: List<Participant>) { | 99 | fun handleActiveSpeakersChanged(speakers: List<Participant>) { |
| @@ -95,7 +101,9 @@ class CallViewModel( | @@ -95,7 +101,9 @@ class CallViewModel( | ||
| 95 | if (speakers.isEmpty() || speakers.contains(mutableActiveSpeaker.value)) { | 101 | if (speakers.isEmpty() || speakers.contains(mutableActiveSpeaker.value)) { |
| 96 | return | 102 | return |
| 97 | } | 103 | } |
| 98 | - val newSpeaker = speakers.firstOrNull() ?: return | 104 | + val newSpeaker = speakers |
| 105 | + .filter { it is RemoteParticipant } // Try not to display local participant as speaker. | ||
| 106 | + .firstOrNull() ?: return | ||
| 99 | mutableActiveSpeaker.postValue(newSpeaker) | 107 | mutableActiveSpeaker.postValue(newSpeaker) |
| 100 | } | 108 | } |
| 101 | 109 |
-
请 注册 或 登录 后发表评论