Skip to content

Commit

Permalink
Report CaptureOptions in VideoRenderer & switch VideoView mirro…
Browse files Browse the repository at this point in the history
…r mode instantly (#366)

Fixes #362
  • Loading branch information
hiroshihorie authored Apr 22, 2024
1 parent ad06bdf commit c87dba1
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 35 deletions.
18 changes: 15 additions & 3 deletions Sources/LiveKit/Protocols/VideoRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import AVFoundation
import Foundation

@_implementationOnly import LiveKitWebRTC
Expand All @@ -30,25 +31,36 @@ public protocol VideoRenderer {
var adaptiveStreamSize: CGSize { get }

/// Size of the frame.
@objc optional
func set(size: CGSize)

@objc optional
func render(frame: VideoFrame)

// Only invoked for local tracks, provides additional capture time options
@objc optional
func render(frame: VideoFrame, videoCaptureOptions: VideoCaptureOptions?)
}

class VideoRendererAdapter: NSObject, LKRTCVideoRenderer {
private weak var target: VideoRenderer?
private weak var localVideoTrack: LocalVideoTrack?

init(target: VideoRenderer) {
init(target: VideoRenderer, localVideoTrack: LocalVideoTrack?) {
self.target = target
self.localVideoTrack = localVideoTrack
}

func setSize(_ size: CGSize) {
target?.set(size: size)
target?.set?(size: size)
}

func renderFrame(_ frame: LKRTCVideoFrame?) {
guard let frame = frame?.toLKType() else { return }
target?.render(frame: frame)
target?.render?(frame: frame)

let cameraCapturer = localVideoTrack?.capturer as? CameraCapturer
target?.render?(frame: frame, videoCaptureOptions: cameraCapturer?.options)
}

// Proxy the equality operators
Expand Down
4 changes: 2 additions & 2 deletions Sources/LiveKit/Track/Track.swift
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ extension Track {
}

videoRenderers.add(videoRenderer)
rtcVideoTrack.add(VideoRendererAdapter(target: videoRenderer))
rtcVideoTrack.add(VideoRendererAdapter(target: videoRenderer, localVideoTrack: self as? LocalVideoTrack))
}

func _remove(videoRenderer: VideoRenderer) {
Expand All @@ -384,7 +384,7 @@ extension Track {
}

videoRenderers.remove(videoRenderer)
rtcVideoTrack.remove(VideoRendererAdapter(target: videoRenderer))
rtcVideoTrack.remove(VideoRendererAdapter(target: videoRenderer, localVideoTrack: self as? LocalVideoTrack))
}
}

Expand Down
6 changes: 6 additions & 0 deletions Sources/LiveKit/Types/Options/CameraCaptureOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,10 @@ public class CameraCaptureOptions: NSObject, VideoCaptureOptions {
hasher.combine(fps)
return hasher.finalize()
}

// MARK: - CustomStringConvertible

override public var description: String {
"CameraCaptureOptions(position: \(String(describing: position))"
}
}
48 changes: 18 additions & 30 deletions Sources/LiveKit/Views/VideoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public class VideoView: NativeView, Loggable {

// MARK: - Internal

struct State: Equatable {
struct State {
weak var track: Track?
var isEnabled: Bool = true
var isHidden: Bool = false
Expand All @@ -174,6 +174,9 @@ public class VideoView: NativeView, Loggable {
var didRenderFirstFrame: Bool = false
var isRendering: Bool = false

// Only used for rendering local tracks
var videoCaptureOptions: VideoCaptureOptions? = nil

// whether if current state should be rendering
var shouldRender: Bool {
track != nil && isEnabled && !isHidden
Expand Down Expand Up @@ -236,11 +239,6 @@ public class VideoView: NativeView, Loggable {
nr.removeFromSuperview()
self._nativeRenderer = nil
}

// CapturerDelegate
if let localTrack = track as? LocalVideoTrack {
localTrack.capturer.remove(delegate: self)
}
}

// set new track
Expand All @@ -256,11 +254,6 @@ public class VideoView: NativeView, Loggable {
nr.renderFrame(frame.toRTCType())
self.setNeedsLayout()
}

// CapturerDelegate
if let localTrack = track as? LocalVideoTrack {
localTrack.capturer.add(delegate: self)
}
}
}

Expand Down Expand Up @@ -290,14 +283,21 @@ public class VideoView: NativeView, Loggable {
// https://developer.apple.com/forums/thread/105252
// nativeRenderer.asMetalView?.isPaused = !shouldAttach

var capturePositionDidUpdate = false
if let oldCameraCaptureOptions = oldState.videoCaptureOptions as? CameraCaptureOptions,
let newCameraCaptureOptions = newState.videoCaptureOptions as? CameraCaptureOptions
{
capturePositionDidUpdate = oldCameraCaptureOptions.position != newCameraCaptureOptions.position
}

// layout is required if any of the following vars mutate
if newState.isDebugMode != oldState.isDebugMode ||
newState.layoutMode != oldState.layoutMode ||
newState.mirrorMode != oldState.mirrorMode ||
newState.renderMode != oldState.renderMode ||
newState.rotationOverride != oldState.rotationOverride ||
newState.didRenderFirstFrame != oldState.didRenderFirstFrame ||
shouldRenderDidUpdate || trackDidUpdate
shouldRenderDidUpdate || trackDidUpdate || capturePositionDidUpdate
{
// must be on main
Task.detached { @MainActor in
Expand Down Expand Up @@ -444,7 +444,7 @@ public class VideoView: NativeView, Loggable {
}
}

_nativeRenderer.set(mirrored: shouldMirror())
_nativeRenderer.set(mirrored: _shouldMirror())
}
}

Expand Down Expand Up @@ -488,12 +488,11 @@ private extension VideoView {
return newView
}

func shouldMirror() -> Bool {
func _shouldMirror() -> Bool {
switch _state.mirrorMode {
case .auto:
guard let localVideoTrack = _state.track as? LocalVideoTrack,
let cameraCapturer = localVideoTrack.capturer as? CameraCapturer,
case .front = cameraCapturer.options.position else { return false }
guard let cameraCaptureOptions = _state.videoCaptureOptions as? CameraCaptureOptions,
case .front = cameraCaptureOptions.position else { return false }
return true
case .off: return false
case .mirror: return true
Expand All @@ -519,7 +518,7 @@ extension VideoView: VideoRenderer {
}
}

public func render(frame: VideoFrame) {
public func render(frame: VideoFrame, videoCaptureOptions: VideoCaptureOptions?) {
let state = _state.copy()

// prevent any extra rendering if already !isEnabled etc.
Expand Down Expand Up @@ -556,6 +555,7 @@ extension VideoView: VideoRenderer {
track?.set(videoFrame: frame)

_state.mutate {
$0.videoCaptureOptions = videoCaptureOptions
$0.didRenderFirstFrame = true
$0.isRendering = true
$0.renderDate = Date()
Expand All @@ -569,18 +569,6 @@ extension VideoView: VideoRenderer {
}
}

// MARK: - VideoCapturerDelegate

extension VideoView: VideoCapturerDelegate {
public func capturer(_: VideoCapturer, didUpdate state: VideoCapturer.CapturerState) {
if case .started = state {
Task.detached { @MainActor in
self.setNeedsLayout()
}
}
}
}

// MARK: - Internal

extension VideoView {
Expand Down

0 comments on commit c87dba1

Please sign in to comment.