正在显示
8 个修改的文件
包含
86 行增加
和
6 行删除
| @@ -3,6 +3,11 @@ package io.livekit.android | @@ -3,6 +3,11 @@ package io.livekit.android | ||
| 3 | import android.content.Context | 3 | import android.content.Context |
| 4 | import io.livekit.android.dagger.DaggerLiveKitComponent | 4 | import io.livekit.android.dagger.DaggerLiveKitComponent |
| 5 | import io.livekit.android.room.Room | 5 | import io.livekit.android.room.Room |
| 6 | +import io.livekit.android.room.track.LocalAudioTrack | ||
| 7 | +import io.livekit.android.room.track.LocalVideoTrack | ||
| 8 | +import org.webrtc.EglBase | ||
| 9 | +import org.webrtc.MediaConstraints | ||
| 10 | +import org.webrtc.PeerConnectionFactory | ||
| 6 | 11 | ||
| 7 | class LiveKit { | 12 | class LiveKit { |
| 8 | companion object { | 13 | companion object { |
| @@ -23,7 +28,42 @@ class LiveKit { | @@ -23,7 +28,42 @@ class LiveKit { | ||
| 23 | room.listener = listener | 28 | room.listener = listener |
| 24 | room.connect(url, token, options.isSecure) | 29 | room.connect(url, token, options.isSecure) |
| 25 | 30 | ||
| 31 | + val localParticipant = room.localParticipant | ||
| 32 | + if (localParticipant != null) { | ||
| 33 | + val factory = component.peerConnectionFactory() | ||
| 34 | + if (options.sendAudio) { | ||
| 35 | + localParticipant.publishAudioTrack(createLocalAudioTrack(factory)) | ||
| 36 | + } | ||
| 37 | + if (options.sendVideo) { | ||
| 38 | + localParticipant.publishVideoTrack( | ||
| 39 | + createLocalVideoTrack( | ||
| 40 | + factory, | ||
| 41 | + appContext, | ||
| 42 | + component.eglBase() | ||
| 43 | + ) | ||
| 44 | + ) | ||
| 45 | + } | ||
| 46 | + } | ||
| 26 | return room | 47 | return room |
| 27 | } | 48 | } |
| 49 | + | ||
| 50 | + private fun createLocalVideoTrack( | ||
| 51 | + peerConnectionFactory: PeerConnectionFactory, | ||
| 52 | + context: Context, | ||
| 53 | + rootEglBase: EglBase, | ||
| 54 | + ): LocalVideoTrack { | ||
| 55 | + return LocalVideoTrack.track( | ||
| 56 | + peerConnectionFactory, | ||
| 57 | + context, | ||
| 58 | + true, | ||
| 59 | + "LiveKit Video", | ||
| 60 | + rootEglBase | ||
| 61 | + ) | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + private fun createLocalAudioTrack(factory: PeerConnectionFactory): LocalAudioTrack { | ||
| 65 | + val audioConstraints = MediaConstraints() | ||
| 66 | + return LocalAudioTrack.createTrack(factory, audioConstraints) | ||
| 67 | + } | ||
| 28 | } | 68 | } |
| 29 | } | 69 | } |
| @@ -4,6 +4,8 @@ import android.content.Context | @@ -4,6 +4,8 @@ import android.content.Context | ||
| 4 | import dagger.BindsInstance | 4 | import dagger.BindsInstance |
| 5 | import dagger.Component | 5 | import dagger.Component |
| 6 | import io.livekit.android.room.Room | 6 | import io.livekit.android.room.Room |
| 7 | +import org.webrtc.EglBase | ||
| 8 | +import org.webrtc.PeerConnectionFactory | ||
| 7 | import javax.inject.Singleton | 9 | import javax.inject.Singleton |
| 8 | 10 | ||
| 9 | @Singleton | 11 | @Singleton |
| @@ -19,6 +21,10 @@ interface LiveKitComponent { | @@ -19,6 +21,10 @@ interface LiveKitComponent { | ||
| 19 | 21 | ||
| 20 | fun roomFactory(): Room.Factory | 22 | fun roomFactory(): Room.Factory |
| 21 | 23 | ||
| 24 | + fun peerConnectionFactory(): PeerConnectionFactory | ||
| 25 | + | ||
| 26 | + fun eglBase(): EglBase | ||
| 27 | + | ||
| 22 | @Component.Factory | 28 | @Component.Factory |
| 23 | interface Factory { | 29 | interface Factory { |
| 24 | fun create(@BindsInstance appContext: Context): LiveKitComponent | 30 | fun create(@BindsInstance appContext: Context): LiveKitComponent |
| 1 | package io.livekit.android.room | 1 | package io.livekit.android.room |
| 2 | 2 | ||
| 3 | +import com.github.ajalt.timberkt.Timber | ||
| 3 | import livekit.Rtc | 4 | import livekit.Rtc |
| 4 | import org.webrtc.* | 5 | import org.webrtc.* |
| 5 | 6 | ||
| @@ -22,6 +23,7 @@ class PublisherTransportObserver( | @@ -22,6 +23,7 @@ class PublisherTransportObserver( | ||
| 22 | 23 | ||
| 23 | override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { | 24 | override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState?) { |
| 24 | val state = newState ?: throw NullPointerException("unexpected null new state, what do?") | 25 | val state = newState ?: throw NullPointerException("unexpected null new state, what do?") |
| 26 | + Timber.v { "onIceConnection new state: $newState" } | ||
| 25 | if (state == PeerConnection.IceConnectionState.CONNECTED && !engine.iceConnected) { | 27 | if (state == PeerConnection.IceConnectionState.CONNECTED && !engine.iceConnected) { |
| 26 | engine.iceConnected = true | 28 | engine.iceConnected = true |
| 27 | } else if (state == PeerConnection.IceConnectionState.DISCONNECTED) { | 29 | } else if (state == PeerConnection.IceConnectionState.DISCONNECTED) { |
| @@ -36,9 +36,9 @@ constructor( | @@ -36,9 +36,9 @@ constructor( | ||
| 36 | var iceConnected: Boolean = false | 36 | var iceConnected: Boolean = false |
| 37 | set(value) { | 37 | set(value) { |
| 38 | field = value | 38 | field = value |
| 39 | - if (field) { | ||
| 40 | - // TODO get rid of this assertion | ||
| 41 | - listener?.onJoin(joinResponse!!) | 39 | + val savedJoinResponse = joinResponse |
| 40 | + if (field && savedJoinResponse != null) { | ||
| 41 | + listener?.onJoin(savedJoinResponse) | ||
| 42 | joinResponse = null | 42 | joinResponse = null |
| 43 | } | 43 | } |
| 44 | } | 44 | } |
| @@ -90,6 +90,7 @@ constructor( | @@ -90,6 +90,7 @@ constructor( | ||
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | fun close() { | 92 | fun close() { |
| 93 | + coroutineScope.close() | ||
| 93 | publisher.close() | 94 | publisher.close() |
| 94 | subscriber.close() | 95 | subscriber.close() |
| 95 | client.close() | 96 | client.close() |
| @@ -110,6 +111,7 @@ constructor( | @@ -110,6 +111,7 @@ constructor( | ||
| 110 | } | 111 | } |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 114 | + Timber.v { "sdp offer = $sdpOffer, description: ${sdpOffer.description}, type: ${sdpOffer.type}" } | ||
| 113 | when (val outcome = publisher.peerConnection.setLocalDescription(sdpOffer)) { | 115 | when (val outcome = publisher.peerConnection.setLocalDescription(sdpOffer)) { |
| 114 | is Either.Right -> { | 116 | is Either.Right -> { |
| 115 | Timber.d { "error setting local description: ${outcome.value}" } | 117 | Timber.d { "error setting local description: ${outcome.value}" } |
| @@ -14,6 +14,9 @@ import io.livekit.android.room.util.unpackedTrackLabel | @@ -14,6 +14,9 @@ import io.livekit.android.room.util.unpackedTrackLabel | ||
| 14 | import livekit.Model | 14 | import livekit.Model |
| 15 | import livekit.Rtc | 15 | import livekit.Rtc |
| 16 | import org.webrtc.* | 16 | import org.webrtc.* |
| 17 | +import kotlin.coroutines.Continuation | ||
| 18 | +import kotlin.coroutines.resume | ||
| 19 | +import kotlin.coroutines.suspendCoroutine | ||
| 17 | 20 | ||
| 18 | class Room | 21 | class Room |
| 19 | @AssistedInject | 22 | @AssistedInject |
| @@ -53,12 +56,15 @@ constructor( | @@ -53,12 +56,15 @@ constructor( | ||
| 53 | val activeSpeakers: List<Participant> | 56 | val activeSpeakers: List<Participant> |
| 54 | get() = mutableActiveSpeakers | 57 | get() = mutableActiveSpeakers |
| 55 | 58 | ||
| 59 | + private var connectContinuation: Continuation<Unit>? = null | ||
| 56 | suspend fun connect(url: String, token: String, isSecure: Boolean) { | 60 | suspend fun connect(url: String, token: String, isSecure: Boolean) { |
| 57 | if (localParticipant != null) { | 61 | if (localParticipant != null) { |
| 58 | Timber.d { "Attempting to connect to room when already connected." } | 62 | Timber.d { "Attempting to connect to room when already connected." } |
| 59 | return | 63 | return |
| 60 | } | 64 | } |
| 61 | engine.join(url, token, isSecure) | 65 | engine.join(url, token, isSecure) |
| 66 | + | ||
| 67 | + return suspendCoroutine { connectContinuation = it } | ||
| 62 | } | 68 | } |
| 63 | 69 | ||
| 64 | fun disconnect() { | 70 | fun disconnect() { |
| @@ -170,6 +176,8 @@ constructor( | @@ -170,6 +176,8 @@ constructor( | ||
| 170 | } | 176 | } |
| 171 | } | 177 | } |
| 172 | 178 | ||
| 179 | + connectContinuation?.resume(Unit) | ||
| 180 | + connectContinuation = null | ||
| 173 | listener?.onConnect(this) | 181 | listener?.onConnect(this) |
| 174 | } | 182 | } |
| 175 | 183 | ||
| @@ -192,7 +200,7 @@ constructor( | @@ -192,7 +200,7 @@ constructor( | ||
| 192 | participant.addSubscribedDataTrack(channel, trackSid, name) | 200 | participant.addSubscribedDataTrack(channel, trackSid, name) |
| 193 | } | 201 | } |
| 194 | 202 | ||
| 195 | - override fun onPublishLocalTrack(cid: String, track: Model.TrackInfo) { | 203 | + override fun onPublishLocalTrack(cid: Track.Cid, track: Model.TrackInfo) { |
| 196 | } | 204 | } |
| 197 | 205 | ||
| 198 | 206 |
| @@ -6,6 +6,7 @@ import io.livekit.android.room.track.* | @@ -6,6 +6,7 @@ import io.livekit.android.room.track.* | ||
| 6 | import livekit.Model | 6 | import livekit.Model |
| 7 | import org.webrtc.DataChannel | 7 | import org.webrtc.DataChannel |
| 8 | import org.webrtc.RtpTransceiver | 8 | import org.webrtc.RtpTransceiver |
| 9 | +import java.util.* | ||
| 9 | 10 | ||
| 10 | class LocalParticipant(sid: Sid, name: String? = null) : | 11 | class LocalParticipant(sid: Sid, name: String? = null) : |
| 11 | Participant(sid, name) { | 12 | Participant(sid, name) { |
| @@ -17,7 +18,7 @@ class LocalParticipant(sid: Sid, name: String? = null) : | @@ -17,7 +18,7 @@ class LocalParticipant(sid: Sid, name: String? = null) : | ||
| 17 | this.engine = engine | 18 | this.engine = engine |
| 18 | } | 19 | } |
| 19 | 20 | ||
| 20 | - private val streamId = "stream" | 21 | + private val streamId = UUID.randomUUID().toString() |
| 21 | 22 | ||
| 22 | val localAudioTrackPublications | 23 | val localAudioTrackPublications |
| 23 | get() = audioTracks.values.toList() | 24 | get() = audioTracks.values.toList() |
| 1 | package io.livekit.android.room.track | 1 | package io.livekit.android.room.track |
| 2 | 2 | ||
| 3 | +import org.webrtc.MediaConstraints | ||
| 4 | +import org.webrtc.PeerConnectionFactory | ||
| 5 | + | ||
| 3 | class LocalAudioTrack( | 6 | class LocalAudioTrack( |
| 4 | name: String, | 7 | name: String, |
| 5 | audioOptions: AudioOptions? = null, | 8 | audioOptions: AudioOptions? = null, |
| @@ -9,4 +12,20 @@ class LocalAudioTrack( | @@ -9,4 +12,20 @@ class LocalAudioTrack( | ||
| 9 | internal set | 12 | internal set |
| 10 | var audioOptions = audioOptions | 13 | var audioOptions = audioOptions |
| 11 | private set | 14 | private set |
| 15 | + | ||
| 16 | + companion object { | ||
| 17 | + fun createTrack( | ||
| 18 | + factory: PeerConnectionFactory, | ||
| 19 | + audioConstraints: MediaConstraints, | ||
| 20 | + name: String = "" | ||
| 21 | + ): LocalAudioTrack { | ||
| 22 | + | ||
| 23 | + val audioSource = factory.createAudioSource(audioConstraints) | ||
| 24 | + val rtcAudioTrack = | ||
| 25 | + factory.createAudioTrack("phone_audio_track_id", audioSource) | ||
| 26 | + rtcAudioTrack.setEnabled(true) | ||
| 27 | + | ||
| 28 | + return LocalAudioTrack(name = name, rtcTrack = rtcAudioTrack) | ||
| 29 | + } | ||
| 30 | + } | ||
| 12 | } | 31 | } |
-
请 注册 或 登录 后发表评论