David Liu

have users be response for creating and publishing tracks

  1 +<component name="ProjectDictionaryState">
  2 + <dictionary name="davidliu">
  3 + <words>
  4 + <w>capturer</w>
  5 + </words>
  6 + </dictionary>
  7 +</component>
@@ -2,6 +2,5 @@ package io.livekit.android @@ -2,6 +2,5 @@ package io.livekit.android
2 2
3 3
4 data class ConnectOptions( 4 data class ConnectOptions(
5 - val sendAudio: Boolean = true,  
6 - val sendVideo: Boolean = true, 5 + val placeholder: Unit = Unit
7 ) 6 )
@@ -4,11 +4,6 @@ import android.content.Context @@ -4,11 +4,6 @@ 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.RoomListener 6 import io.livekit.android.room.RoomListener
7 -import io.livekit.android.room.track.LocalAudioTrack  
8 -import io.livekit.android.room.track.LocalVideoTrack  
9 -import org.webrtc.EglBase  
10 -import org.webrtc.MediaConstraints  
11 -import org.webrtc.PeerConnectionFactory  
12 7
13 class LiveKit { 8 class LiveKit {
14 companion object { 9 companion object {
@@ -27,51 +22,15 @@ class LiveKit { @@ -27,51 +22,15 @@ class LiveKit {
27 22
28 val component = DaggerLiveKitComponent 23 val component = DaggerLiveKitComponent
29 .factory() 24 .factory()
30 - .create(appContext) 25 + .create(appContext.applicationContext)
31 26
32 val room = component.roomFactory() 27 val room = component.roomFactory()
33 .create(options) 28 .create(options)
34 room.listener = listener 29 room.listener = listener
35 room.connect(url, token) 30 room.connect(url, token)
36 31
37 - val localParticipant = room.localParticipant  
38 - if (localParticipant != null) {  
39 - val factory = component.peerConnectionFactory()  
40 - if (options.sendAudio) {  
41 - val audioTrack = createLocalAudioTrack(factory)  
42 - localParticipant.publishAudioTrack(audioTrack)  
43 - audioTrack.enabled = true  
44 - }  
45 - if (options.sendVideo) {  
46 - val videoTrack = createLocalVideoTrack(  
47 - factory,  
48 - appContext,  
49 - component.eglBase()  
50 - )  
51 - localParticipant.publishVideoTrack(videoTrack)  
52 - videoTrack.startCapture()  
53 - }  
54 - }  
55 return room 32 return room
56 } 33 }
57 34
58 - private fun createLocalVideoTrack(  
59 - peerConnectionFactory: PeerConnectionFactory,  
60 - context: Context,  
61 - rootEglBase: EglBase,  
62 - ): LocalVideoTrack {  
63 - return LocalVideoTrack.createTrack(  
64 - peerConnectionFactory,  
65 - context,  
66 - true,  
67 - "LiveKit Video",  
68 - rootEglBase  
69 - )  
70 - }  
71 -  
72 - private fun createLocalAudioTrack(factory: PeerConnectionFactory): LocalAudioTrack {  
73 - val audioConstraints = MediaConstraints()  
74 - return LocalAudioTrack.createTrack(factory, audioConstraints)  
75 - }  
76 } 35 }
77 } 36 }
@@ -77,11 +77,6 @@ class RTCModule { @@ -77,11 +77,6 @@ class RTCModule {
77 } 77 }
78 78
79 return JavaAudioDeviceModule.builder(appContext) 79 return JavaAudioDeviceModule.builder(appContext)
80 - .setSamplesReadyCallback(object : JavaAudioDeviceModule.SamplesReadyCallback {  
81 - override fun onWebRtcAudioRecordSamplesReady(samples: JavaAudioDeviceModule.AudioSamples?) {  
82 - Timber.v { "samples: $samples" }  
83 - }  
84 - })  
85 .setUseHardwareAcousticEchoCanceler(true) 80 .setUseHardwareAcousticEchoCanceler(true)
86 .setUseHardwareNoiseSuppressor(true) 81 .setUseHardwareNoiseSuppressor(true)
87 .setAudioRecordErrorCallback(audioRecordErrorCallback) 82 .setAudioRecordErrorCallback(audioRecordErrorCallback)
1 package io.livekit.android.room 1 package io.livekit.android.room
2 2
3 -import android.content.Context  
4 import com.github.ajalt.timberkt.Timber 3 import com.github.ajalt.timberkt.Timber
5 import io.livekit.android.dagger.InjectionNames 4 import io.livekit.android.dagger.InjectionNames
6 -import io.livekit.android.room.track.Track  
7 import io.livekit.android.room.track.TrackException 5 import io.livekit.android.room.track.TrackException
8 import io.livekit.android.room.util.* 6 import io.livekit.android.room.util.*
9 import io.livekit.android.util.CloseableCoroutineScope 7 import io.livekit.android.util.CloseableCoroutineScope
@@ -16,6 +14,7 @@ import livekit.LivekitRtc @@ -16,6 +14,7 @@ import livekit.LivekitRtc
16 import org.webrtc.* 14 import org.webrtc.*
17 import javax.inject.Inject 15 import javax.inject.Inject
18 import javax.inject.Named 16 import javax.inject.Named
  17 +import javax.inject.Singleton
19 import kotlin.coroutines.Continuation 18 import kotlin.coroutines.Continuation
20 import kotlin.coroutines.resume 19 import kotlin.coroutines.resume
21 import kotlin.coroutines.suspendCoroutine 20 import kotlin.coroutines.suspendCoroutine
@@ -23,6 +22,7 @@ import kotlin.coroutines.suspendCoroutine @@ -23,6 +22,7 @@ import kotlin.coroutines.suspendCoroutine
23 /** 22 /**
24 * @suppress 23 * @suppress
25 */ 24 */
  25 +@Singleton
26 class RTCEngine 26 class RTCEngine
27 @Inject 27 @Inject
28 constructor( 28 constructor(
@@ -27,6 +27,7 @@ constructor( @@ -27,6 +27,7 @@ constructor(
27 @Assisted private val connectOptions: ConnectOptions, 27 @Assisted private val connectOptions: ConnectOptions,
28 private val engine: RTCEngine, 28 private val engine: RTCEngine,
29 private val eglBase: EglBase, 29 private val eglBase: EglBase,
  30 + private val localParticipantFactory: LocalParticipant.Factory
30 ) : RTCEngine.Listener, ParticipantListener { 31 ) : RTCEngine.Listener, ParticipantListener {
31 init { 32 init {
32 engine.listener = this 33 engine.listener = this
@@ -162,7 +163,7 @@ constructor( @@ -162,7 +163,7 @@ constructor(
162 return 163 return
163 } 164 }
164 165
165 - val lp = LocalParticipant(response.participant, engine) 166 + val lp = localParticipantFactory.create(response.participant)
166 lp.listener = this 167 lp.listener = this
167 localParticipant = lp 168 localParticipant = lp
168 if (response.otherParticipantsList.isNotEmpty()) { 169 if (response.otherParticipantsList.isNotEmpty()) {
1 package io.livekit.android.room.participant 1 package io.livekit.android.room.participant
2 2
  3 +import android.content.Context
3 import com.github.ajalt.timberkt.Timber 4 import com.github.ajalt.timberkt.Timber
  5 +import dagger.assisted.Assisted
  6 +import dagger.assisted.AssistedFactory
  7 +import dagger.assisted.AssistedInject
4 import io.livekit.android.room.RTCEngine 8 import io.livekit.android.room.RTCEngine
5 import io.livekit.android.room.track.* 9 import io.livekit.android.room.track.*
6 import livekit.LivekitModels 10 import livekit.LivekitModels
7 -import org.webrtc.DataChannel  
8 -import org.webrtc.RtpTransceiver  
9 -import java.util.*  
10 -  
11 -class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine: RTCEngine) : 11 +import org.webrtc.*
  12 +
  13 +class LocalParticipant
  14 +@AssistedInject
  15 +internal constructor(
  16 + @Assisted
  17 + info: LivekitModels.ParticipantInfo,
  18 + private val engine: RTCEngine,
  19 + private val peerConnectionFactory: PeerConnectionFactory,
  20 + private val context: Context,
  21 + private val eglBase: EglBase,
  22 +) :
12 Participant(info.sid, info.identity) { 23 Participant(info.sid, info.identity) {
13 24
14 init { 25 init {
@@ -18,6 +29,24 @@ class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine: @@ -18,6 +29,24 @@ class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine:
18 private val localTrackPublications 29 private val localTrackPublications
19 get() = tracks.values.toList() 30 get() = tracks.values.toList()
20 31
  32 + fun createAudioTrack(
  33 + audioConstraints: MediaConstraints = MediaConstraints(),
  34 + name: String = ""
  35 + ) = LocalAudioTrack.createTrack(peerConnectionFactory, audioConstraints, name)
  36 +
  37 + fun createVideoTrack(
  38 + isScreencast: Boolean = false,
  39 + name: String = "",
  40 + ): LocalVideoTrack {
  41 + return LocalVideoTrack.createTrack(
  42 + peerConnectionFactory,
  43 + context,
  44 + isScreencast,
  45 + name,
  46 + eglBase
  47 + )
  48 + }
  49 +
21 suspend fun publishAudioTrack( 50 suspend fun publishAudioTrack(
22 track: LocalAudioTrack, 51 track: LocalAudioTrack,
23 publishListener: PublishListener? = null 52 publishListener: PublishListener? = null
@@ -147,4 +176,9 @@ class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine: @@ -147,4 +176,9 @@ class LocalParticipant(info: LivekitModels.ParticipantInfo, private val engine:
147 fun onPublishSuccess(publication: TrackPublication) {} 176 fun onPublishSuccess(publication: TrackPublication) {}
148 fun onPublishFailure(exception: Exception) {} 177 fun onPublishFailure(exception: Exception) {}
149 } 178 }
  179 +
  180 + @AssistedFactory
  181 + interface Factory {
  182 + fun create(info: LivekitModels.ParticipantInfo): LocalParticipant
  183 + }
150 } 184 }
@@ -4,6 +4,11 @@ import org.webrtc.MediaConstraints @@ -4,6 +4,11 @@ import org.webrtc.MediaConstraints
4 import org.webrtc.PeerConnectionFactory 4 import org.webrtc.PeerConnectionFactory
5 import java.util.* 5 import java.util.*
6 6
  7 +/**
  8 + * Represents a local audio track (generally using the microphone as input).
  9 + *
  10 + * This class should not be constructed directly, but rather through [LocalParticipant]
  11 + */
7 class LocalAudioTrack( 12 class LocalAudioTrack(
8 name: String, 13 name: String,
9 audioOptions: AudioOptions? = null, 14 audioOptions: AudioOptions? = null,
@@ -19,9 +24,9 @@ class LocalAudioTrack( @@ -19,9 +24,9 @@ class LocalAudioTrack(
19 private set 24 private set
20 25
21 companion object { 26 companion object {
22 - fun createTrack( 27 + internal fun createTrack(
23 factory: PeerConnectionFactory, 28 factory: PeerConnectionFactory,
24 - audioConstraints: MediaConstraints, 29 + audioConstraints: MediaConstraints = MediaConstraints(),
25 name: String = "" 30 name: String = ""
26 ): LocalAudioTrack { 31 ): LocalAudioTrack {
27 32
@@ -5,7 +5,11 @@ import com.github.ajalt.timberkt.Timber @@ -5,7 +5,11 @@ import com.github.ajalt.timberkt.Timber
5 import org.webrtc.* 5 import org.webrtc.*
6 import java.util.* 6 import java.util.*
7 7
8 - 8 +/**
  9 + * A representation of a local video track (generally input coming from camera or screen).
  10 + *
  11 + * [startCapture] should be called before use.
  12 + */
9 class LocalVideoTrack( 13 class LocalVideoTrack(
10 private val capturer: VideoCapturer, 14 private val capturer: VideoCapturer,
11 private val source: VideoSource, 15 private val source: VideoSource,
@@ -20,19 +24,18 @@ class LocalVideoTrack( @@ -20,19 +24,18 @@ class LocalVideoTrack(
20 internal fun createTrack( 24 internal fun createTrack(
21 peerConnectionFactory: PeerConnectionFactory, 25 peerConnectionFactory: PeerConnectionFactory,
22 context: Context, 26 context: Context,
23 - enabled: Boolean, 27 + isScreencast: Boolean,
24 name: String, 28 name: String,
25 rootEglBase: EglBase, 29 rootEglBase: EglBase,
26 ): LocalVideoTrack { 30 ): LocalVideoTrack {
27 - val source = peerConnectionFactory.createVideoSource(false) 31 + val source = peerConnectionFactory.createVideoSource(isScreencast)
28 val capturer = createVideoCapturer(context) ?: TODO() 32 val capturer = createVideoCapturer(context) ?: TODO()
29 capturer.initialize( 33 capturer.initialize(
30 - SurfaceTextureHelper.create("CaptureThread", rootEglBase.eglBaseContext), 34 + SurfaceTextureHelper.create("VideoCaptureThread", rootEglBase.eglBaseContext),
31 context, 35 context,
32 source.capturerObserver 36 source.capturerObserver
33 ) 37 )
34 val track = peerConnectionFactory.createVideoTrack(UUID.randomUUID().toString(), source) 38 val track = peerConnectionFactory.createVideoTrack(UUID.randomUUID().toString(), source)
35 - track.setEnabled(enabled)  
36 39
37 return LocalVideoTrack( 40 return LocalVideoTrack(
38 capturer = capturer, 41 capturer = capturer,
@@ -30,9 +30,17 @@ class CallViewModel( @@ -30,9 +30,17 @@ class CallViewModel(
30 application, 30 application,
31 url, 31 url,
32 token, 32 token,
33 - ConnectOptions(true), 33 + ConnectOptions(),
34 this@CallViewModel 34 this@CallViewModel
35 ) 35 )
  36 +
  37 + val localParticipant = room.localParticipant
  38 + val audioTrack = localParticipant.createAudioTrack()
  39 + localParticipant.publishAudioTrack(audioTrack)
  40 + val videoTrack = localParticipant.createVideoTrack()
  41 + localParticipant.publishVideoTrack(videoTrack)
  42 + videoTrack.startCapture()
  43 +
36 updateParticipants(room) 44 updateParticipants(room)
37 mutableRoom.value = room 45 mutableRoom.value = room
38 } 46 }
@@ -50,7 +58,6 @@ class CallViewModel( @@ -50,7 +58,6 @@ class CallViewModel(
50 override fun onCleared() { 58 override fun onCleared() {
51 super.onCleared() 59 super.onCleared()
52 mutableRoom.value?.disconnect() 60 mutableRoom.value?.disconnect()
53 - mutableRoom.value = null  
54 } 61 }
55 62
56 override fun onDisconnect(room: Room, error: Exception?) { 63 override fun onDisconnect(room: Room, error: Exception?) {
@@ -20,9 +20,8 @@ @@ -20,9 +20,8 @@
20 <org.webrtc.SurfaceViewRenderer 20 <org.webrtc.SurfaceViewRenderer
21 android:id="@+id/pip_video_view" 21 android:id="@+id/pip_video_view"
22 android:layout_height="144dp" 22 android:layout_height="144dp"
23 - android:layout_width="0dp" 23 + android:layout_width="144dp"
24 app:layout_constraintBottom_toBottomOf="parent" 24 app:layout_constraintBottom_toBottomOf="parent"
25 app:layout_constraintEnd_toEndOf="parent" 25 app:layout_constraintEnd_toEndOf="parent"
26 - app:layout_constraintStart_toStartOf="parent"  
27 android:layout_margin="16dp" /> 26 android:layout_margin="16dp" />
28 </androidx.constraintlayout.widget.ConstraintLayout> 27 </androidx.constraintlayout.widget.ConstraintLayout>