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 Liu
2021-03-07 22:54:46 +0900
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
c3952c2ae637560263cd24faf77713dd94e2d3d8
c3952c2a
1 parent
eb75cbff
some coroutines stuff
隐藏空白字符变更
内嵌
并排对比
正在显示
8 个修改的文件
包含
290 行增加
和
20 行删除
livekit-android-sdk/src/main/java/io/livekit/android/dagger/CoroutinesModule.kt
livekit-android-sdk/src/main/java/io/livekit/android/dagger/InjectionNames.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/RTCClient.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/track/Track.kt
livekit-android-sdk/src/main/java/io/livekit/android/room/util/CoroutineSdpObserver.kt
livekit-android-sdk/src/main/java/io/livekit/android/util/CloseableCoroutineScope.kt
livekit-android-sdk/src/main/java/io/livekit/android/util/Either.kt
livekit-android-sdk/src/main/java/io/livekit/android/dagger/CoroutinesModule.kt
0 → 100644
查看文件 @
c3952c2
package io.livekit.android.dagger
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.Dispatchers
import javax.inject.Named
@Module
class CoroutinesModule {
companion object {
@Provides
@Named(InjectionNames.DISPATCHER_DEFAULT)
fun defaultDispatcher() = Dispatchers.Default
@Provides
@Named(InjectionNames.DISPATCHER_IO)
fun ioDispatcher() = Dispatchers.IO
@Provides
@Named(InjectionNames.DISPATCHER_MAIN)
fun mainDispatcher() = Dispatchers.Main
@Provides
@Named(InjectionNames.DISPATCHER_UNCONFINED)
fun unconfinedDispatcher() = Dispatchers.Unconfined
}
}
\ No newline at end of file
...
...
livekit-android-sdk/src/main/java/io/livekit/android/dagger/InjectionNames.kt
0 → 100644
查看文件 @
c3952c2
package io.livekit.android.dagger
class InjectionNames {
companion object {
const val DISPATCHER_DEFAULT = "dispatcher_default"
const val DISPATCHER_IO = "dispatcher_io";
const val DISPATCHER_MAIN = "dispatcher_main"
const val DISPATCHER_UNCONFINED = "dispatcher_unconfined"
}
}
\ No newline at end of file
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/RTCClient.kt
查看文件 @
c3952c2
...
...
@@ -2,6 +2,7 @@ package io.livekit.android.room
import com.github.ajalt.timberkt.Timber
import com.google.protobuf.util.JsonFormat
import io.livekit.android.room.track.Track
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
...
...
@@ -144,9 +145,9 @@ constructor(
sendRequest(request)
}
fun sendMuteTrack(trackSid:
String
, muted: Boolean) {
fun sendMuteTrack(trackSid:
Track.Sid
, muted: Boolean) {
val muteRequest = Rtc.MuteTrackRequest.newBuilder()
.setSid(trackSid)
.setSid(trackSid
.sid
)
.setMuted(muted)
.build()
...
...
@@ -157,9 +158,9 @@ constructor(
sendRequest(request)
}
fun sendAddTrack(cid:
String
, name: String, type: Model.TrackType) {
fun sendAddTrack(cid:
Track.Cid
, name: String, type: Model.TrackType) {
val addTrackRequest = Rtc.AddTrackRequest.newBuilder()
.setCid(cid)
.setCid(cid
.cid
)
.setName(name)
.setType(type)
.build()
...
...
@@ -183,7 +184,6 @@ constructor(
Timber.d { "error sending request: $request" }
throw IllegalStateException()
}
}
fun handleSignalResponse(response: Rtc.SignalResponse) {
...
...
@@ -224,15 +224,18 @@ constructor(
}
}
fun close() {
TODO("Not yet implemented")
}
interface Listener {
fun onJoin(info: Rtc.JoinResponse)
fun onAnswer(sessionDescription: SessionDescription)
fun onOffer(sessionDescription: SessionDescription)
fun onTrickle(candidate: IceCandidate, target: Rtc.SignalTarget)
fun onLocalTrackPublished(
trackPublished
: Rtc.TrackPublishedResponse)
fun onLocalTrackPublished(
response
: Rtc.TrackPublishedResponse)
fun onParticipantUpdate(updates: List<Model.ParticipantInfo>)
fun onActiveSpeakersChanged(speakers: List<Rtc.SpeakerInfo>)
fun onClose(reason: String, code: Int)
fun onError(error: Error)
}
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/RTCEngine.kt
查看文件 @
c3952c2
package io.livekit.android.room
import android.content.Context
import com.github.ajalt.timberkt.Timber
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.room.track.Track
import io.livekit.android.room.track.TrackException
import io.livekit.android.room.util.CoroutineSdpObserver
import io.livekit.android.util.CloseableCoroutineScope
import io.livekit.android.util.Either
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import livekit.Model
import livekit.Rtc
import org.webrtc.*
import javax.inject.Inject
import javax.inject.Named
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class RTCEngine
...
...
@@ -15,6 +27,7 @@ constructor(
private val appContext: Context,
val client: RTCClient,
pctFactory: PeerConnectionTransport.Factory,
@Named(InjectionNames.DISPATCHER_IO) ioDispatcher: CoroutineDispatcher,
) : RTCClient.Listener {
var listener: Listener? = null
...
...
@@ -39,6 +52,7 @@ constructor(
private var privateDataChannel: DataChannel
private val coroutineScope = CloseableCoroutineScope(SupervisorJob() + ioDispatcher)
init {
val rtcConfig = PeerConnection.RTCConfiguration(RTCClient.DEFAULT_ICE_SERVERS).apply {
sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN
...
...
@@ -55,12 +69,66 @@ constructor(
)
}
suspend
fun join(url: String, token: String, isSecure: Boolean) {
fun join(url: String, token: String, isSecure: Boolean) {
client.join(url, token, isSecure)
}
suspend fun addTrack(cid: Track.Cid, name: String, kind: Model.TrackType): Model.TrackInfo {
if (pendingTrackResolvers[cid] != null) {
throw TrackException.DuplicateTrackException("Track with same ID $cid has already been published!")
}
return suspendCoroutine { cont ->
pendingTrackResolvers[cid] = cont
client.sendAddTrack(cid, name, kind)
}
}
fun updateMuteStatus(sid: Track.Sid, muted: Boolean) {
client.sendMuteTrack(sid, muted)
}
fun close() {
publisher.close()
subscriber.close()
client.close()
}
fun negotiate() {
TODO("Not yet implemented")
coroutineScope.launch {
val offerObserver = CoroutineSdpObserver()
publisher.peerConnection.createOffer(offerObserver, OFFER_CONSTRAINTS)
val offerOutcome = offerObserver.awaitCreate()
val sdpOffer = when (offerOutcome) {
is Either.Left -> offerOutcome.value
is Either.Right -> {
Timber.d { "error creating offer: ${offerOutcome.value}" }
return@launch
}
}
if (sdpOffer == null) {
Timber.d { "sdp is missing during negotiation?" }
return@launch
}
val setObserver = CoroutineSdpObserver()
publisher.peerConnection.setLocalDescription(setObserver, sdpOffer)
val setOutcome = setObserver.awaitSet()
when (setOutcome) {
is Either.Left -> client.sendOffer(sdpOffer)
is Either.Right -> Timber.d { "error setting local description: ${setOutcome.value}" }
}
}
}
private fun onRTCConnected() {
Timber.v { "RTC Connected" }
rtcConnected = true
pendingCandidates.forEach { candidate ->
client.sendCandidate(candidate, Rtc.SignalTarget.PUBLISHER)
}
pendingCandidates.clear()
}
interface Listener {
...
...
@@ -69,17 +137,58 @@ constructor(
fun onPublishLocalTrack(cid: String, track: Model.TrackInfo)
fun onAddDataChannel(channel: DataChannel)
fun onUpdateParticipants(updates: Array<out Model.ParticipantInfo>)
fun onUpdateSpeakers(speakers:
Array<out
Rtc.SpeakerInfo>)
fun onUpdateSpeakers(speakers:
List<
Rtc.SpeakerInfo>)
fun onDisconnect(reason: String)
fun onFailToConnect(error: Error)
}
companion object {
private const val PRIVATE_DATA_CHANNEL_LABEL = "_private"
private val OFFER_CONSTRAINTS = MediaConstraints().apply {
with(mandatory) {
add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "false"))
add(MediaConstraints.KeyValuePair("OfferToReceiveVideo", "false"))
}
}
private val MEDIA_CONSTRAINTS = MediaConstraints()
private val CONN_CONSTRAINTS = MediaConstraints().apply {
with(optional) {
add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))
}
}
}
override fun onJoin(info: Rtc.JoinResponse) {
TODO("Not yet implemented")
joinResponse = info
coroutineScope.launch {
val offerObserver = CoroutineSdpObserver()
publisher.peerConnection.createOffer(offerObserver, OFFER_CONSTRAINTS)
val offerOutcome = offerObserver.awaitCreate()
val sdpOffer = when (offerOutcome) {
is Either.Left -> offerOutcome.value
is Either.Right -> {
Timber.d { "error creating offer: ${offerOutcome.value}" }
return@launch
}
}
if (sdpOffer == null) {
Timber.d { "sdp is missing during negotiation?" }
return@launch
}
val setObserver = CoroutineSdpObserver()
publisher.peerConnection.setLocalDescription(setObserver, sdpOffer)
val setOutcome = setObserver.awaitSet()
when (setOutcome) {
is Either.Left -> client.sendOffer(sdpOffer)
is Either.Right -> Timber.d { "error setting local description: ${setOutcome.value}" }
}
}
}
override fun onAnswer(sessionDescription: SessionDescription) {
...
...
@@ -94,14 +203,36 @@ constructor(
TODO("Not yet implemented")
}
override fun onLocalTrackPublished(trackPublished: Rtc.TrackPublishedResponse) {
TODO("Not yet implemented")
override fun onLocalTrackPublished(response: Rtc.TrackPublishedResponse) {
val cid = response.cid ?: run {
Timber.e { "local track published with null cid?" }
return
}
val track = response.track
if (track == null) {
Timber.d { "local track published with null track info?" }
}
Timber.v { "local track published $cid" }
val cont = pendingTrackResolvers.remove(cid)
if (cont == null) {
Timber.d { "missing track resolver for: $cid" }
return
}
cont.resume(response.track)
listener?.onPublishLocalTrack(cid, track)
}
override fun onParticipantUpdate(updates: List<Model.ParticipantInfo>) {
TODO("Not yet implemented")
}
override fun onActiveSpeakersChanged(speakers: List<Rtc.SpeakerInfo>) {
listener?.onUpdateSpeakers(speakers)
}
override fun onClose(reason: String, code: Int) {
TODO("Not yet implemented")
}
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/track/Track.kt
查看文件 @
c3952c2
...
...
@@ -44,16 +44,20 @@ class Track(name: String, state: State) {
}
}
sealed class TrackException(message: String?, cause: Throwable?) : Exception(message, cause) {
class InvalidTrackTypeException(message: String?, cause: Throwable?) :
sealed class TrackException(message: String? = null, cause: Throwable? = null) :
Exception(message, cause) {
class InvalidTrackTypeException(message: String? = null, cause: Throwable? = null) :
TrackException(message, cause)
class DuplicateTrackException(message: String?
, cause: Throwable?
) :
class DuplicateTrackException(message: String?
= null, cause: Throwable? = null
) :
TrackException(message, cause)
class InvalidTrackStateException(message: String?
, cause: Throwable?
) :
class InvalidTrackStateException(message: String?
= null, cause: Throwable? = null
) :
TrackException(message, cause)
class MediaException(message: String?, cause: Throwable?) : TrackException(message, cause)
class PublishException(message: String?, cause: Throwable?) : TrackException(message, cause)
class MediaException(message: String? = null, cause: Throwable? = null) :
TrackException(message, cause)
class PublishException(message: String? = null, cause: Throwable? = null) :
TrackException(message, cause)
}
\ No newline at end of file
...
...
livekit-android-sdk/src/main/java/io/livekit/android/room/util/CoroutineSdpObserver.kt
0 → 100644
查看文件 @
c3952c2
package io.livekit.android.room.util
import io.livekit.android.util.Either
import org.webrtc.SdpObserver
import org.webrtc.SessionDescription
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class CoroutineSdpObserver : SdpObserver {
private var createOutcome: Either<SessionDescription?, String?>? = null
set(value) {
field = value
if (value != null) {
val conts = pendingCreate.toList()
pendingCreate.clear()
conts.forEach {
it.resume(value)
}
}
}
private var pendingCreate = mutableListOf<Continuation<Either<SessionDescription?, String?>>>()
private var setOutcome: Either<Unit, String?>? = null
set(value) {
field = value
if (value != null) {
val conts = pendingSets.toList()
pendingSets.clear()
conts.forEach {
it.resume(value)
}
}
}
private var pendingSets = mutableListOf<Continuation<Either<Unit, String?>>>()
override fun onCreateSuccess(sdp: SessionDescription?) {
createOutcome = Either.Left(sdp)
}
override fun onSetSuccess() {
setOutcome = Either.Left(Unit)
}
override fun onCreateFailure(message: String?) {
createOutcome = Either.Right(message)
}
override fun onSetFailure(message: String?) {
setOutcome = Either.Right(message)
}
suspend fun awaitCreate() = suspendCoroutine<Either<SessionDescription?, String?>> { cont ->
val curOutcome = createOutcome
if (curOutcome != null) {
cont.resume(curOutcome)
} else {
pendingCreate.add(cont)
}
}
suspend fun awaitSet() = suspendCoroutine<Either<Unit, String?>> { cont ->
val curOutcome = setOutcome
if (curOutcome != null) {
cont.resume(curOutcome)
} else {
pendingSets.add(cont)
}
}
}
\ No newline at end of file
...
...
livekit-android-sdk/src/main/java/io/livekit/android/util/CloseableCoroutineScope.kt
0 → 100644
查看文件 @
c3952c2
package io.livekit.android.util
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import java.io.Closeable
import kotlin.coroutines.CoroutineContext
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
\ No newline at end of file
...
...
livekit-android-sdk/src/main/java/io/livekit/android/util/Either.kt
0 → 100644
查看文件 @
c3952c2
package io.livekit.android.util
sealed class Either<out A, out B> {
class Left<A>(val value: A) : Either<A, Nothing>()
class Right<B>(val value: B) : Either<Nothing, B>()
}
\ No newline at end of file
...
...
请
注册
或
登录
后发表评论