正在显示
6 个修改的文件
包含
84 行增加
和
17 行删除
| @@ -78,8 +78,8 @@ internal constructor( | @@ -78,8 +78,8 @@ internal constructor( | ||
| 78 | listOf(this.sid) | 78 | listOf(this.sid) |
| 79 | ) | 79 | ) |
| 80 | // TODO: sendEncodings to customize | 80 | // TODO: sendEncodings to customize |
| 81 | - val transceiver = | ||
| 82 | - engine.publisher.peerConnection.addTransceiver(track.rtcTrack, transInit) | 81 | + val transceiver = engine.publisher.peerConnection.addTransceiver(track.rtcTrack, transInit) |
| 82 | + track.transceiver = transceiver | ||
| 83 | 83 | ||
| 84 | if (transceiver == null) { | 84 | if (transceiver == null) { |
| 85 | publishListener?.onPublishFailure(TrackException.PublishException("null sender returned from peer connection")) | 85 | publishListener?.onPublishFailure(TrackException.PublishException("null sender returned from peer connection")) |
| @@ -108,8 +108,8 @@ internal constructor( | @@ -108,8 +108,8 @@ internal constructor( | ||
| 108 | listOf(this.sid) | 108 | listOf(this.sid) |
| 109 | ) | 109 | ) |
| 110 | // TODO: video encodings & simulcast | 110 | // TODO: video encodings & simulcast |
| 111 | - val transceiver = | ||
| 112 | - engine.publisher.peerConnection.addTransceiver(track.rtcTrack, transInit) | 111 | + val transceiver = engine.publisher.peerConnection.addTransceiver(track.rtcTrack, transInit) |
| 112 | + track.transceiver = transceiver | ||
| 113 | 113 | ||
| 114 | if (transceiver == null) { | 114 | if (transceiver == null) { |
| 115 | publishListener?.onPublishFailure(TrackException.PublishException("null sender returned from peer connection")) | 115 | publishListener?.onPublishFailure(TrackException.PublishException("null sender returned from peer connection")) |
| @@ -2,6 +2,8 @@ package io.livekit.android.room.track | @@ -2,6 +2,8 @@ package io.livekit.android.room.track | ||
| 2 | 2 | ||
| 3 | import org.webrtc.MediaConstraints | 3 | import org.webrtc.MediaConstraints |
| 4 | import org.webrtc.PeerConnectionFactory | 4 | import org.webrtc.PeerConnectionFactory |
| 5 | +import org.webrtc.RtpSender | ||
| 6 | +import org.webrtc.RtpTransceiver | ||
| 5 | import java.util.* | 7 | import java.util.* |
| 6 | 8 | ||
| 7 | /** | 9 | /** |
| @@ -19,6 +21,10 @@ class LocalAudioTrack( | @@ -19,6 +21,10 @@ class LocalAudioTrack( | ||
| 19 | rtcTrack.setEnabled(value) | 21 | rtcTrack.setEnabled(value) |
| 20 | } | 22 | } |
| 21 | 23 | ||
| 24 | + internal var transceiver: RtpTransceiver? = null | ||
| 25 | + private val sender: RtpSender? | ||
| 26 | + get() = transceiver?.sender | ||
| 27 | + | ||
| 22 | companion object { | 28 | companion object { |
| 23 | internal fun createTrack( | 29 | internal fun createTrack( |
| 24 | factory: PeerConnectionFactory, | 30 | factory: PeerConnectionFactory, |
| @@ -11,20 +11,32 @@ import java.util.* | @@ -11,20 +11,32 @@ import java.util.* | ||
| 11 | * [startCapture] should be called before use. | 11 | * [startCapture] should be called before use. |
| 12 | */ | 12 | */ |
| 13 | class LocalVideoTrack( | 13 | class LocalVideoTrack( |
| 14 | - private val capturer: VideoCapturer, | ||
| 15 | - private val source: VideoSource, | 14 | + private var capturer: VideoCapturer, |
| 15 | + private var source: VideoSource, | ||
| 16 | name: String, | 16 | name: String, |
| 17 | - private val options: LocalVideoTrackOptions, | ||
| 18 | - rtcTrack: org.webrtc.VideoTrack | 17 | + var options: LocalVideoTrackOptions, |
| 18 | + rtcTrack: org.webrtc.VideoTrack, | ||
| 19 | + private val peerConnectionFactory: PeerConnectionFactory, | ||
| 20 | + private val context: Context, | ||
| 21 | + private val eglBase: EglBase, | ||
| 19 | ) : VideoTrack(name, rtcTrack) { | 22 | ) : VideoTrack(name, rtcTrack) { |
| 20 | - val dimensions: Dimensions | ||
| 21 | 23 | ||
| 22 | - init { | ||
| 23 | - dimensions = Dimensions(options.captureParams.width, options.captureParams.height) | ||
| 24 | - } | 24 | + override var rtcTrack: org.webrtc.VideoTrack = rtcTrack |
| 25 | + internal set | ||
| 26 | + | ||
| 27 | + val dimensions: Dimensions = | ||
| 28 | + Dimensions(options.captureParams.width, options.captureParams.height) | ||
| 29 | + | ||
| 30 | + internal var transceiver: RtpTransceiver? = null | ||
| 31 | + private val sender: RtpSender? | ||
| 32 | + get() = transceiver?.sender | ||
| 25 | 33 | ||
| 26 | fun startCapture() { | 34 | fun startCapture() { |
| 27 | - capturer.startCapture(options.captureParams.width, options.captureParams.height, options.captureParams.maxFps) | 35 | + capturer.startCapture( |
| 36 | + options.captureParams.width, | ||
| 37 | + options.captureParams.height, | ||
| 38 | + options.captureParams.maxFps | ||
| 39 | + ) | ||
| 28 | } | 40 | } |
| 29 | 41 | ||
| 30 | override fun stop() { | 42 | override fun stop() { |
| @@ -32,6 +44,40 @@ class LocalVideoTrack( | @@ -32,6 +44,40 @@ class LocalVideoTrack( | ||
| 32 | super.stop() | 44 | super.stop() |
| 33 | } | 45 | } |
| 34 | 46 | ||
| 47 | + fun restartTrack(options: LocalVideoTrackOptions = LocalVideoTrackOptions()) { | ||
| 48 | + val newTrack = createTrack( | ||
| 49 | + peerConnectionFactory, | ||
| 50 | + context, | ||
| 51 | + name, | ||
| 52 | + options, | ||
| 53 | + eglBase | ||
| 54 | + ) | ||
| 55 | + | ||
| 56 | + val oldCapturer = capturer | ||
| 57 | + val oldSource = source | ||
| 58 | + val oldRtcTrack = rtcTrack | ||
| 59 | + | ||
| 60 | + oldCapturer.stopCapture() | ||
| 61 | + oldCapturer.dispose() | ||
| 62 | + oldSource.dispose() | ||
| 63 | + | ||
| 64 | + // sender owns rtcTrack, so it'll take care of disposing it. | ||
| 65 | + oldRtcTrack.setEnabled(false) | ||
| 66 | + | ||
| 67 | + // migrate video sinks to the new track | ||
| 68 | + for (sink in sinks) { | ||
| 69 | + oldRtcTrack.removeSink(sink) | ||
| 70 | + newTrack.addRenderer(sink) | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + capturer = newTrack.capturer | ||
| 74 | + source = newTrack.source | ||
| 75 | + rtcTrack = newTrack.rtcTrack | ||
| 76 | + this.options = options | ||
| 77 | + startCapture() | ||
| 78 | + sender?.setTrack(newTrack.rtcTrack, true) | ||
| 79 | + } | ||
| 80 | + | ||
| 35 | companion object { | 81 | companion object { |
| 36 | internal fun createTrack( | 82 | internal fun createTrack( |
| 37 | peerConnectionFactory: PeerConnectionFactory, | 83 | peerConnectionFactory: PeerConnectionFactory, |
| @@ -55,6 +101,9 @@ class LocalVideoTrack( | @@ -55,6 +101,9 @@ class LocalVideoTrack( | ||
| 55 | options = options, | 101 | options = options, |
| 56 | name = name, | 102 | name = name, |
| 57 | rtcTrack = track, | 103 | rtcTrack = track, |
| 104 | + peerConnectionFactory = peerConnectionFactory, | ||
| 105 | + context = context, | ||
| 106 | + eglBase = rootEglBase, | ||
| 58 | ) | 107 | ) |
| 59 | } | 108 | } |
| 60 | 109 |
| 1 | package io.livekit.android.room.track | 1 | package io.livekit.android.room.track |
| 2 | 2 | ||
| 3 | -import livekit.LivekitModels | ||
| 4 | import org.webrtc.VideoSink | 3 | import org.webrtc.VideoSink |
| 5 | import org.webrtc.VideoTrack | 4 | import org.webrtc.VideoTrack |
| 6 | 5 | ||
| 7 | open class VideoTrack(name: String, override val rtcTrack: VideoTrack) : | 6 | open class VideoTrack(name: String, override val rtcTrack: VideoTrack) : |
| 8 | Track(name, Kind.VIDEO, rtcTrack) { | 7 | Track(name, Kind.VIDEO, rtcTrack) { |
| 9 | - private val sinks: MutableList<VideoSink> = ArrayList(); | 8 | + internal val sinks: MutableList<VideoSink> = ArrayList(); |
| 10 | 9 | ||
| 11 | var enabled: Boolean | 10 | var enabled: Boolean |
| 12 | get() = rtcTrack.enabled() | 11 | get() = rtcTrack.enabled() |
| @@ -12,8 +12,10 @@ import io.livekit.android.room.Room | @@ -12,8 +12,10 @@ import io.livekit.android.room.Room | ||
| 12 | import io.livekit.android.room.RoomListener | 12 | import io.livekit.android.room.RoomListener |
| 13 | import io.livekit.android.room.participant.Participant | 13 | import io.livekit.android.room.participant.Participant |
| 14 | import io.livekit.android.room.participant.RemoteParticipant | 14 | import io.livekit.android.room.participant.RemoteParticipant |
| 15 | +import io.livekit.android.room.track.CameraPosition | ||
| 15 | import io.livekit.android.room.track.LocalAudioTrack | 16 | import io.livekit.android.room.track.LocalAudioTrack |
| 16 | import io.livekit.android.room.track.LocalVideoTrack | 17 | import io.livekit.android.room.track.LocalVideoTrack |
| 18 | +import io.livekit.android.room.track.LocalVideoTrackOptions | ||
| 17 | import kotlinx.coroutines.launch | 19 | import kotlinx.coroutines.launch |
| 18 | 20 | ||
| 19 | class CallViewModel( | 21 | class CallViewModel( |
| @@ -118,7 +120,19 @@ class CallViewModel( | @@ -118,7 +120,19 @@ class CallViewModel( | ||
| 118 | } | 120 | } |
| 119 | 121 | ||
| 120 | fun flipVideo() { | 122 | fun flipVideo() { |
| 121 | - // TODO | 123 | + room.value?.localParticipant?.let { participant -> |
| 124 | + val videoTrack = participant.videoTracks.values | ||
| 125 | + .firstOrNull() | ||
| 126 | + ?.track as? LocalVideoTrack | ||
| 127 | + ?: return@let | ||
| 128 | + | ||
| 129 | + val newOptions = when (videoTrack.options.position) { | ||
| 130 | + CameraPosition.FRONT -> LocalVideoTrackOptions(position = CameraPosition.BACK) | ||
| 131 | + CameraPosition.BACK -> LocalVideoTrackOptions(position = CameraPosition.FRONT) | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + videoTrack.restartTrack(newOptions) | ||
| 135 | + } | ||
| 122 | } | 136 | } |
| 123 | } | 137 | } |
| 124 | 138 |
| @@ -4,7 +4,6 @@ import android.Manifest | @@ -4,7 +4,6 @@ import android.Manifest | ||
| 4 | import android.content.Intent | 4 | import android.content.Intent |
| 5 | import android.content.pm.PackageManager | 5 | import android.content.pm.PackageManager |
| 6 | import android.os.Bundle | 6 | import android.os.Bundle |
| 7 | -import android.text.SpannableStringBuilder | ||
| 8 | import android.widget.Toast | 7 | import android.widget.Toast |
| 9 | import androidx.activity.ComponentActivity | 8 | import androidx.activity.ComponentActivity |
| 10 | import androidx.activity.compose.setContent | 9 | import androidx.activity.compose.setContent |
-
请 注册 或 登录 后发表评论