Committed by
GitHub
Implement LocalTrackSubscribed event (#489)
* Update protocol to 1.20.0 * Implement LocalTrackSubscribed event
正在显示
9 个修改的文件
包含
114 行增加
和
2 行删除
.changeset/empty-bobcats-design.md
0 → 100644
| @@ -75,6 +75,13 @@ sealed class ParticipantEvent(open val participant: Participant) : Event() { | @@ -75,6 +75,13 @@ sealed class ParticipantEvent(open val participant: Participant) : Event() { | ||
| 75 | class TrackUnmuted(participant: Participant, val publication: TrackPublication) : ParticipantEvent(participant) | 75 | class TrackUnmuted(participant: Participant, val publication: TrackPublication) : ParticipantEvent(participant) |
| 76 | 76 | ||
| 77 | // local participants | 77 | // local participants |
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * Fired when the first remote participant has subscribed to the localParticipant's track | ||
| 81 | + */ | ||
| 82 | + class LocalTrackSubscribed(override val participant: LocalParticipant, val publication: LocalTrackPublication) : | ||
| 83 | + ParticipantEvent(participant) | ||
| 84 | + | ||
| 78 | /** | 85 | /** |
| 79 | * When a new track is published by the local participant. | 86 | * When a new track is published by the local participant. |
| 80 | */ | 87 | */ |
| @@ -128,6 +128,11 @@ sealed class RoomEvent(val room: Room) : Event() { | @@ -128,6 +128,11 @@ sealed class RoomEvent(val room: Room) : Event() { | ||
| 128 | class TrackUnmuted(room: Room, val publication: TrackPublication, val participant: Participant) : RoomEvent(room) | 128 | class TrackUnmuted(room: Room, val publication: TrackPublication, val participant: Participant) : RoomEvent(room) |
| 129 | 129 | ||
| 130 | /** | 130 | /** |
| 131 | + * Fired when the first remote participant has subscribed to the localParticipant's track | ||
| 132 | + */ | ||
| 133 | + class LocalTrackSubscribed(room: Room, val publication: LocalTrackPublication, val participant: LocalParticipant) : RoomEvent(room) | ||
| 134 | + | ||
| 135 | + /** | ||
| 131 | * When a new track is published to room after the local participant has joined. It will | 136 | * When a new track is published to room after the local participant has joined. It will |
| 132 | * not fire for tracks that are already published | 137 | * not fire for tracks that are already published |
| 133 | */ | 138 | */ |
| @@ -267,6 +272,7 @@ enum class DisconnectReason { | @@ -267,6 +272,7 @@ enum class DisconnectReason { | ||
| 267 | JOIN_FAILURE, | 272 | JOIN_FAILURE, |
| 268 | MIGRATION, | 273 | MIGRATION, |
| 269 | SIGNAL_CLOSE, | 274 | SIGNAL_CLOSE, |
| 275 | + ROOM_CLOSED, | ||
| 270 | } | 276 | } |
| 271 | 277 | ||
| 272 | /** | 278 | /** |
| @@ -283,6 +289,7 @@ fun LivekitModels.DisconnectReason?.convert(): DisconnectReason { | @@ -283,6 +289,7 @@ fun LivekitModels.DisconnectReason?.convert(): DisconnectReason { | ||
| 283 | LivekitModels.DisconnectReason.JOIN_FAILURE -> DisconnectReason.JOIN_FAILURE | 289 | LivekitModels.DisconnectReason.JOIN_FAILURE -> DisconnectReason.JOIN_FAILURE |
| 284 | LivekitModels.DisconnectReason.MIGRATION -> DisconnectReason.MIGRATION | 290 | LivekitModels.DisconnectReason.MIGRATION -> DisconnectReason.MIGRATION |
| 285 | LivekitModels.DisconnectReason.SIGNAL_CLOSE -> DisconnectReason.SIGNAL_CLOSE | 291 | LivekitModels.DisconnectReason.SIGNAL_CLOSE -> DisconnectReason.SIGNAL_CLOSE |
| 292 | + LivekitModels.DisconnectReason.ROOM_CLOSED -> DisconnectReason.ROOM_CLOSED | ||
| 286 | LivekitModels.DisconnectReason.UNKNOWN_REASON, | 293 | LivekitModels.DisconnectReason.UNKNOWN_REASON, |
| 287 | LivekitModels.DisconnectReason.UNRECOGNIZED, | 294 | LivekitModels.DisconnectReason.UNRECOGNIZED, |
| 288 | null, | 295 | null, |
| @@ -776,6 +776,7 @@ internal constructor( | @@ -776,6 +776,7 @@ internal constructor( | ||
| 776 | suspend fun onPostReconnect(isFullReconnect: Boolean) | 776 | suspend fun onPostReconnect(isFullReconnect: Boolean) |
| 777 | fun onLocalTrackUnpublished(trackUnpublished: LivekitRtc.TrackUnpublishedResponse) | 777 | fun onLocalTrackUnpublished(trackUnpublished: LivekitRtc.TrackUnpublishedResponse) |
| 778 | fun onTranscriptionReceived(transcription: LivekitModels.Transcription) | 778 | fun onTranscriptionReceived(transcription: LivekitModels.Transcription) |
| 779 | + fun onLocalTrackSubscribed(trackSubscribed: LivekitRtc.TrackSubscribed) | ||
| 779 | } | 780 | } |
| 780 | 781 | ||
| 781 | companion object { | 782 | companion object { |
| @@ -909,6 +910,10 @@ internal constructor( | @@ -909,6 +910,10 @@ internal constructor( | ||
| 909 | cont.resume(response.track) | 910 | cont.resume(response.track) |
| 910 | } | 911 | } |
| 911 | 912 | ||
| 913 | + override fun onLocalTrackSubscribed(trackSubscribed: LivekitRtc.TrackSubscribed) { | ||
| 914 | + listener?.onLocalTrackSubscribed(trackSubscribed) | ||
| 915 | + } | ||
| 916 | + | ||
| 912 | override fun onParticipantUpdate(updates: List<LivekitModels.ParticipantInfo>) { | 917 | override fun onParticipantUpdate(updates: List<LivekitModels.ParticipantInfo>) { |
| 913 | listener?.onUpdateParticipants(updates) | 918 | listener?.onUpdateParticipants(updates) |
| 914 | } | 919 | } |
| @@ -1249,6 +1249,23 @@ constructor( | @@ -1249,6 +1249,23 @@ constructor( | ||
| 1249 | /** | 1249 | /** |
| 1250 | * @suppress | 1250 | * @suppress |
| 1251 | */ | 1251 | */ |
| 1252 | + override fun onLocalTrackSubscribed(response: LivekitRtc.TrackSubscribed) { | ||
| 1253 | + val publication = localParticipant.trackPublications[response.trackSid] as? LocalTrackPublication | ||
| 1254 | + | ||
| 1255 | + if (publication == null) { | ||
| 1256 | + LKLog.w { "Could not find local track publication for subscribed event " } | ||
| 1257 | + return | ||
| 1258 | + } | ||
| 1259 | + | ||
| 1260 | + coroutineScope.launch { | ||
| 1261 | + emitWhenConnected(RoomEvent.LocalTrackSubscribed(this@Room, publication, this@Room.localParticipant)) | ||
| 1262 | + } | ||
| 1263 | + this.localParticipant.onLocalTrackSubscribed(publication) | ||
| 1264 | + } | ||
| 1265 | + | ||
| 1266 | + /** | ||
| 1267 | + * @suppress | ||
| 1268 | + */ | ||
| 1252 | override fun onLocalTrackUnpublished(trackUnpublished: LivekitRtc.TrackUnpublishedResponse) { | 1269 | override fun onLocalTrackUnpublished(trackUnpublished: LivekitRtc.TrackUnpublishedResponse) { |
| 1253 | localParticipant.handleLocalTrackUnpublished(trackUnpublished) | 1270 | localParticipant.handleLocalTrackUnpublished(trackUnpublished) |
| 1254 | } | 1271 | } |
| @@ -697,6 +697,10 @@ constructor( | @@ -697,6 +697,10 @@ constructor( | ||
| 697 | listener?.onParticipantUpdate(response.update.participantsList) | 697 | listener?.onParticipantUpdate(response.update.participantsList) |
| 698 | } | 698 | } |
| 699 | 699 | ||
| 700 | + LivekitRtc.SignalResponse.MessageCase.TRACK_SUBSCRIBED -> { | ||
| 701 | + listener?.onLocalTrackSubscribed(response.trackSubscribed) | ||
| 702 | + } | ||
| 703 | + | ||
| 700 | LivekitRtc.SignalResponse.MessageCase.TRACK_PUBLISHED -> { | 704 | LivekitRtc.SignalResponse.MessageCase.TRACK_PUBLISHED -> { |
| 701 | listener?.onLocalTrackPublished(response.trackPublished) | 705 | listener?.onLocalTrackPublished(response.trackPublished) |
| 702 | } | 706 | } |
| @@ -766,7 +770,7 @@ constructor( | @@ -766,7 +770,7 @@ constructor( | ||
| 766 | // TODO | 770 | // TODO |
| 767 | } | 771 | } |
| 768 | 772 | ||
| 769 | - LivekitRtc.SignalResponse.MessageCase.ERROR_RESPONSE -> { | 773 | + LivekitRtc.SignalResponse.MessageCase.REQUEST_RESPONSE -> { |
| 770 | // TODO | 774 | // TODO |
| 771 | } | 775 | } |
| 772 | 776 | ||
| @@ -857,6 +861,7 @@ constructor( | @@ -857,6 +861,7 @@ constructor( | ||
| 857 | fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) | 861 | fun onSubscriptionPermissionUpdate(subscriptionPermissionUpdate: LivekitRtc.SubscriptionPermissionUpdate) |
| 858 | fun onRefreshToken(token: String) | 862 | fun onRefreshToken(token: String) |
| 859 | fun onLocalTrackUnpublished(trackUnpublished: LivekitRtc.TrackUnpublishedResponse) | 863 | fun onLocalTrackUnpublished(trackUnpublished: LivekitRtc.TrackUnpublishedResponse) |
| 864 | + fun onLocalTrackSubscribed(trackSubscribed: LivekitRtc.TrackSubscribed) | ||
| 860 | } | 865 | } |
| 861 | 866 | ||
| 862 | companion object { | 867 | companion object { |
| @@ -884,6 +884,15 @@ internal constructor( | @@ -884,6 +884,15 @@ internal constructor( | ||
| 884 | } | 884 | } |
| 885 | } | 885 | } |
| 886 | 886 | ||
| 887 | + internal fun onLocalTrackSubscribed(publication: LocalTrackPublication) { | ||
| 888 | + if (!trackPublications.containsKey(publication.sid)) { | ||
| 889 | + LKLog.w { "Could not find local track publication for subscribed event " } | ||
| 890 | + return | ||
| 891 | + } | ||
| 892 | + | ||
| 893 | + eventBus.postEvent(ParticipantEvent.LocalTrackSubscribed(this, publication), scope) | ||
| 894 | + } | ||
| 895 | + | ||
| 887 | /** | 896 | /** |
| 888 | * @suppress | 897 | * @suppress |
| 889 | */ | 898 | */ |
| @@ -16,12 +16,20 @@ | @@ -16,12 +16,20 @@ | ||
| 16 | 16 | ||
| 17 | package io.livekit.android.room | 17 | package io.livekit.android.room |
| 18 | 18 | ||
| 19 | +import io.livekit.android.events.ParticipantEvent | ||
| 19 | import io.livekit.android.events.RoomEvent | 20 | import io.livekit.android.events.RoomEvent |
| 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 | ||
| 20 | import io.livekit.android.test.MockE2ETest | 25 | import io.livekit.android.test.MockE2ETest |
| 21 | import io.livekit.android.test.assert.assertIsClass | 26 | import io.livekit.android.test.assert.assertIsClass |
| 22 | import io.livekit.android.test.events.EventCollector | 27 | import io.livekit.android.test.events.EventCollector |
| 28 | +import io.livekit.android.test.mock.MockAudioProcessingController | ||
| 29 | +import io.livekit.android.test.mock.MockAudioStreamTrack | ||
| 23 | import io.livekit.android.test.mock.TestData | 30 | import io.livekit.android.test.mock.TestData |
| 24 | import kotlinx.coroutines.ExperimentalCoroutinesApi | 31 | import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 32 | +import livekit.LivekitRtc | ||
| 25 | import livekit.LivekitRtc.ParticipantUpdate | 33 | import livekit.LivekitRtc.ParticipantUpdate |
| 26 | import livekit.LivekitRtc.SignalResponse | 34 | import livekit.LivekitRtc.SignalResponse |
| 27 | import org.junit.Assert.assertEquals | 35 | import org.junit.Assert.assertEquals |
| @@ -67,4 +75,53 @@ class RoomParticipantEventMockE2ETest : MockE2ETest() { | @@ -67,4 +75,53 @@ class RoomParticipantEventMockE2ETest : MockE2ETest() { | ||
| 67 | assertEquals(1, events.size) | 75 | assertEquals(1, events.size) |
| 68 | assertIsClass(RoomEvent.ParticipantAttributesChanged::class.java, events.first()) | 76 | assertIsClass(RoomEvent.ParticipantAttributesChanged::class.java, events.first()) |
| 69 | } | 77 | } |
| 78 | + | ||
| 79 | + @Test | ||
| 80 | + fun localTrackSubscribed() = runTest { | ||
| 81 | + connect() | ||
| 82 | + 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 | + ), | ||
| 90 | + options = AudioTrackPublishOptions( | ||
| 91 | + source = Track.Source.MICROPHONE, | ||
| 92 | + ), | ||
| 93 | + ) | ||
| 94 | + val roomCollector = EventCollector(room.events, coroutineRule.scope) | ||
| 95 | + val participantCollector = EventCollector(room.localParticipant.events, coroutineRule.scope) | ||
| 96 | + | ||
| 97 | + wsFactory.receiveMessage( | ||
| 98 | + with(SignalResponse.newBuilder()) { | ||
| 99 | + trackSubscribed = with(LivekitRtc.TrackSubscribed.newBuilder()) { | ||
| 100 | + trackSid = TestData.LOCAL_AUDIO_TRACK.sid | ||
| 101 | + build() | ||
| 102 | + } | ||
| 103 | + build() | ||
| 104 | + }, | ||
| 105 | + ) | ||
| 106 | + | ||
| 107 | + val roomEvents = roomCollector.stopCollecting() | ||
| 108 | + val participantEvents = participantCollector.stopCollecting() | ||
| 109 | + | ||
| 110 | + // Verify room events | ||
| 111 | + run { | ||
| 112 | + assertEquals(1, roomEvents.size) | ||
| 113 | + assertIsClass(RoomEvent.LocalTrackSubscribed::class.java, roomEvents[0]) | ||
| 114 | + | ||
| 115 | + val event = roomEvents.first() as RoomEvent.LocalTrackSubscribed | ||
| 116 | + assertEquals(room, event.room) | ||
| 117 | + assertEquals(room.localParticipant, event.participant) | ||
| 118 | + assertEquals(room.localParticipant.getTrackPublication(Track.Source.MICROPHONE), event.publication) | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + // Verify participant events | ||
| 122 | + run { | ||
| 123 | + assertEquals(1, participantEvents.size) | ||
| 124 | + assertIsClass(ParticipantEvent.LocalTrackSubscribed::class.java, participantEvents[0]) | ||
| 125 | + } | ||
| 126 | + } | ||
| 70 | } | 127 | } |
-
请 注册 或 登录 后发表评论