Committed by
GitHub
Add mirror parameter to VideoRenderer (#218)
正在显示
3 个修改的文件
包含
39 行增加
和
8 行删除
| @@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
| 7 | </inspection_tool> | 7 | </inspection_tool> |
| 8 | <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true"> | 8 | <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true"> |
| 9 | <option name="composableFile" value="true" /> | 9 | <option name="composableFile" value="true" /> |
| 10 | + <option name="previewFile" value="true" /> | ||
| 10 | </inspection_tool> | 11 | </inspection_tool> |
| 11 | <inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> | 12 | <inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> |
| 12 | <option name="composableFile" value="true" /> | 13 | <option name="composableFile" value="true" /> |
| @@ -14,6 +15,7 @@ | @@ -14,6 +15,7 @@ | ||
| 14 | </inspection_tool> | 15 | </inspection_tool> |
| 15 | <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true"> | 16 | <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true"> |
| 16 | <option name="composableFile" value="true" /> | 17 | <option name="composableFile" value="true" /> |
| 18 | + <option name="previewFile" value="true" /> | ||
| 17 | </inspection_tool> | 19 | </inspection_tool> |
| 18 | <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true"> | 20 | <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true"> |
| 19 | <option name="composableFile" value="true" /> | 21 | <option name="composableFile" value="true" /> |
| @@ -29,6 +31,7 @@ | @@ -29,6 +31,7 @@ | ||
| 29 | </inspection_tool> | 31 | </inspection_tool> |
| 30 | <inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true"> | 32 | <inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true"> |
| 31 | <option name="composableFile" value="true" /> | 33 | <option name="composableFile" value="true" /> |
| 34 | + <option name="previewFile" value="true" /> | ||
| 32 | </inspection_tool> | 35 | </inspection_tool> |
| 33 | <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> | 36 | <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> |
| 34 | <option name="composableFile" value="true" /> | 37 | <option name="composableFile" value="true" /> |
| 1 | package io.livekit.android.compose | 1 | package io.livekit.android.compose |
| 2 | 2 | ||
| 3 | +import android.graphics.Matrix | ||
| 3 | import androidx.compose.runtime.* | 4 | import androidx.compose.runtime.* |
| 4 | import androidx.compose.ui.Modifier | 5 | import androidx.compose.ui.Modifier |
| 5 | import androidx.compose.ui.layout.onGloballyPositioned | 6 | import androidx.compose.ui.layout.onGloballyPositioned |
| @@ -18,12 +19,20 @@ import io.livekit.android.room.track.video.ComposeVisibility | @@ -18,12 +19,20 @@ import io.livekit.android.room.track.video.ComposeVisibility | ||
| 18 | fun VideoRenderer( | 19 | fun VideoRenderer( |
| 19 | room: Room, | 20 | room: Room, |
| 20 | videoTrack: VideoTrack, | 21 | videoTrack: VideoTrack, |
| 21 | - modifier: Modifier = Modifier | 22 | + modifier: Modifier = Modifier, |
| 23 | + mirror: Boolean = false, | ||
| 22 | ) { | 24 | ) { |
| 23 | 25 | ||
| 24 | val videoSinkVisibility = remember(room, videoTrack) { ComposeVisibility() } | 26 | val videoSinkVisibility = remember(room, videoTrack) { ComposeVisibility() } |
| 25 | var boundVideoTrack by remember { mutableStateOf<VideoTrack?>(null) } | 27 | var boundVideoTrack by remember { mutableStateOf<VideoTrack?>(null) } |
| 26 | var view: TextureViewRenderer? by remember { mutableStateOf(null) } | 28 | var view: TextureViewRenderer? by remember { mutableStateOf(null) } |
| 29 | + var videoScale by remember { mutableStateOf(1f) } | ||
| 30 | + | ||
| 31 | + videoScale = if (mirror) { | ||
| 32 | + -1f | ||
| 33 | + } else { | ||
| 34 | + 1f | ||
| 35 | + } | ||
| 27 | 36 | ||
| 28 | fun cleanupVideoTrack() { | 37 | fun cleanupVideoTrack() { |
| 29 | view?.let { boundVideoTrack?.removeRenderer(it) } | 38 | view?.let { boundVideoTrack?.removeRenderer(it) } |
| @@ -45,6 +54,11 @@ fun VideoRenderer( | @@ -45,6 +54,11 @@ fun VideoRenderer( | ||
| 45 | } | 54 | } |
| 46 | } | 55 | } |
| 47 | 56 | ||
| 57 | + DisposableEffect(view, videoScale) { | ||
| 58 | + view?.scaleX = videoScale | ||
| 59 | + onDispose { } | ||
| 60 | + } | ||
| 61 | + | ||
| 48 | DisposableEffect(room, videoTrack) { | 62 | DisposableEffect(room, videoTrack) { |
| 49 | onDispose { | 63 | onDispose { |
| 50 | videoSinkVisibility.onDispose() | 64 | videoSinkVisibility.onDispose() |
| @@ -13,28 +13,42 @@ import io.livekit.android.room.participant.Participant | @@ -13,28 +13,42 @@ import io.livekit.android.room.participant.Participant | ||
| 13 | import io.livekit.android.room.track.Track | 13 | import io.livekit.android.room.track.Track |
| 14 | import io.livekit.android.room.track.VideoTrack | 14 | import io.livekit.android.room.track.VideoTrack |
| 15 | import io.livekit.android.util.flow | 15 | import io.livekit.android.util.flow |
| 16 | -import kotlinx.coroutines.flow.* | ||
| 17 | 16 | ||
| 18 | /** | 17 | /** |
| 19 | - * This widget primarily serves as a way to observe changes in [videoTracks]. | 18 | + * This widget primarily serves as a way to observe changes in [Participant.videoTracks]. |
| 20 | */ | 19 | */ |
| 21 | @Composable | 20 | @Composable |
| 22 | fun VideoItemTrackSelector( | 21 | fun VideoItemTrackSelector( |
| 23 | room: Room, | 22 | room: Room, |
| 24 | participant: Participant, | 23 | participant: Participant, |
| 25 | - modifier: Modifier = Modifier | 24 | + modifier: Modifier = Modifier, |
| 25 | + mirror: Boolean = false, | ||
| 26 | ) { | 26 | ) { |
| 27 | val videoTrackMap by participant::videoTracks.flow.collectAsState(initial = emptyList()) | 27 | val videoTrackMap by participant::videoTracks.flow.collectAsState(initial = emptyList()) |
| 28 | val videoPubs = videoTrackMap.filter { (pub) -> pub.subscribed } | 28 | val videoPubs = videoTrackMap.filter { (pub) -> pub.subscribed } |
| 29 | .map { (pub) -> pub } | 29 | .map { (pub) -> pub } |
| 30 | - val videoTrack = videoPubs.firstOrNull { pub -> pub.source == Track.Source.SCREEN_SHARE }?.track as? VideoTrack | ||
| 31 | - ?: videoPubs.firstOrNull { pub -> pub.source == Track.Source.CAMERA }?.track as? VideoTrack | ||
| 32 | - ?: videoPubs.firstOrNull()?.track as? VideoTrack | ||
| 33 | 30 | ||
| 34 | - if (videoTrack != null) { | 31 | + // Find the most appropriate video stream to show |
| 32 | + // Prioritize screen share, then camera, then any video stream. | ||
| 33 | + val videoPub = videoPubs.firstOrNull { pub -> pub.source == Track.Source.SCREEN_SHARE } | ||
| 34 | + ?: videoPubs.firstOrNull { pub -> pub.source == Track.Source.CAMERA } | ||
| 35 | + ?: videoPubs.firstOrNull() | ||
| 36 | + | ||
| 37 | + val videoTrack = videoPub?.track as? VideoTrack | ||
| 38 | + val videoMuted by | ||
| 39 | + if (videoPub != null) { | ||
| 40 | + videoPub::muted.flow.collectAsState() | ||
| 41 | + } else { | ||
| 42 | + remember(videoPub) { | ||
| 43 | + derivedStateOf { false } | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + if (videoTrack != null && !videoMuted) { | ||
| 35 | VideoRenderer( | 48 | VideoRenderer( |
| 36 | room = room, | 49 | room = room, |
| 37 | videoTrack = videoTrack, | 50 | videoTrack = videoTrack, |
| 51 | + mirror = mirror, | ||
| 38 | modifier = modifier | 52 | modifier = modifier |
| 39 | ) | 53 | ) |
| 40 | } else { | 54 | } else { |
-
请 注册 或 登录 后发表评论