LocalParticipant.kt
5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package io.livekit.android.room.participant
import com.github.ajalt.timberkt.Timber
import io.livekit.android.room.RTCEngine
import io.livekit.android.room.track.*
import livekit.LivekitModels
import org.webrtc.DataChannel
import org.webrtc.RtpTransceiver
import java.util.*
class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine: RTCEngine) :
Participant(info.sid, info.identity) {
init {
updateFromInfo(info)
}
private val localTrackPublications
get() = tracks.values.toList()
suspend fun publishAudioTrack(
track: LocalAudioTrack,
publishListener: PublishListener? = null
) {
if (localTrackPublications.any { it.track == track }) {
publishListener?.onPublishFailure(TrackException.PublishException("Track has already been published"))
return
}
val cid = track.rtcTrack.id()
val trackInfo =
engine.addTrack(cid = cid, name = track.name, kind = track.kind)
val transInit = RtpTransceiver.RtpTransceiverInit(
RtpTransceiver.RtpTransceiverDirection.SEND_ONLY,
listOf(this.sid)
)
// TODO: sendEncodings to customize
val transceiver =
engine.publisher.peerConnection.addTransceiver(track.rtcTrack, transInit)
if (transceiver == null) {
publishListener?.onPublishFailure(TrackException.PublishException("null sender returned from peer connection"))
return
}
val publication = TrackPublication(trackInfo, track)
addTrackPublication(publication)
publishListener?.onPublishSuccess(publication)
}
suspend fun publishVideoTrack(
track: LocalVideoTrack,
publishListener: PublishListener? = null
) {
if (localTrackPublications.any { it.track == track }) {
publishListener?.onPublishFailure(TrackException.PublishException("Track has already been published"))
return
}
val cid = track.rtcTrack.id()
val trackInfo =
engine.addTrack(cid = cid, name = track.name, kind = LivekitModels.TrackType.VIDEO)
val transInit = RtpTransceiver.RtpTransceiverInit(
RtpTransceiver.RtpTransceiverDirection.SEND_ONLY,
listOf(this.sid)
)
// TODO: video encodings & simulcast
val transceiver =
engine.publisher.peerConnection.addTransceiver(track.rtcTrack, transInit)
if (transceiver == null) {
publishListener?.onPublishFailure(TrackException.PublishException("null sender returned from peer connection"))
return
}
val publication = TrackPublication(trackInfo, track)
addTrackPublication(publication)
publishListener?.onPublishSuccess(publication)
}
suspend fun publishDataTrack(
track: LocalDataTrack,
publishListener: PublishListener? = null
) {
if (localTrackPublications.any { it.track == track }) {
publishListener?.onPublishFailure(TrackException.PublishException("Track has already been published"))
return
}
// data track cid isn't ready until peer connection creates it, so we'll use name
val cid = track.name
val trackInfo =
engine.addTrack(cid = cid, name = track.name, track.kind)
val publication = TrackPublication(trackInfo, track)
val config = DataChannel.Init().apply {
ordered = track.options.ordered
maxRetransmitTimeMs = track.options.maxRetransmitTimeMs
maxRetransmits = track.options.maxRetransmits
}
val dataChannel = engine.publisher.peerConnection.createDataChannel(track.name, config)
if (dataChannel == null) {
publishListener?.onPublishFailure(TrackException.PublishException("could not create data channel"))
return
}
track.dataChannel = dataChannel
track.updateConfig(config)
addTrackPublication(publication)
publishListener?.onPublishSuccess(publication)
}
fun unpublishTrack(track: Track) {
val publication = localTrackPublications.firstOrNull { it.track == track }
if (publication === null) {
Timber.d { "this track was never published." }
return
}
track.stop()
if (track is MediaTrack) {
unpublishMediaTrack(track, sid)
}
val sid = publication.sid
tracks.remove(sid)
when (publication.kind) {
LivekitModels.TrackType.AUDIO -> audioTracks.remove(sid)
LivekitModels.TrackType.VIDEO -> videoTracks.remove(sid)
LivekitModels.TrackType.DATA -> dataTracks.remove(sid)
}
}
private fun <T> unpublishMediaTrack(
track: T,
sid: String
) where T : MediaTrack {
val senders = engine?.publisher?.peerConnection?.senders ?: return
for (sender in senders) {
val t = sender.track() ?: continue
if (t == track.rtcTrack) {
engine?.publisher?.peerConnection?.removeTrack(sender)
}
}
}
interface PublishListener {
fun onPublishSuccess(publication: TrackPublication) {}
fun onPublishFailure(exception: Exception) {}
}
}