Skip to content

Commit

Permalink
Frame skipping
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroshihorie committed Jan 12, 2025
1 parent d13efc0 commit 7c9f7b1
Showing 1 changed file with 35 additions and 17 deletions.
52 changes: 35 additions & 17 deletions Sources/LiveKit/Track/Capturers/VideoCapturer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class VideoCapturer: NSObject, Loggable, VideoCapturerProtocol {
public let delegates = MulticastDelegate<VideoCapturerDelegate>(label: "VideoCapturerDelegate")
public let rendererDelegates = MulticastDelegate<VideoRenderer>(label: "VideoCapturerRendererDelegate")

private let processingQueue = DispatchQueue(label: "io.livekit.videocapturer.processing")

/// Array of supported pixel formats that can be used to capture a frame.
///
/// Usually the following formats are supported but it is recommended to confirm at run-time:
Expand Down Expand Up @@ -75,6 +77,7 @@ public class VideoCapturer: NSObject, Loggable, VideoCapturerProtocol {
var startStopCounter: Int = 0
var dimensions: Dimensions? = nil
weak var processor: VideoProcessor? = nil
var isFrameProcessingBusy: Bool = false
}

let _state: StateSync<State>
Expand Down Expand Up @@ -230,31 +233,46 @@ extension VideoCapturer {
device: AVCaptureDevice?,
options: VideoCaptureOptions)
{
var rtcFrame: LKRTCVideoFrame = frame
guard var lkFrame: VideoFrame = frame.toLKType() else {
log("Failed to convert a RTCVideoFrame to VideoFrame.", .error)
if _state.isFrameProcessingBusy {
log("Frame processing hasn't completed yet, skipping frame...", .warning)
return
}

// Apply processing if we have a processor attached.
if let processor = _state.processor {
guard let processedFrame = processor.process(frame: lkFrame) else {
log("VideoProcessor didn't return a frame, skipping frame.", .warning)
processingQueue.async { [weak self] in
guard let self else { return }

// Mark as frame processing busy.
_state.mutate { $0.isFrameProcessingBusy = true }
defer {
_state.mutate { $0.isFrameProcessingBusy = false }
}

var rtcFrame: LKRTCVideoFrame = frame
guard var lkFrame: VideoFrame = frame.toLKType() else {
log("Failed to convert a RTCVideoFrame to VideoFrame.", .error)
return
}
lkFrame = processedFrame
rtcFrame = processedFrame.toRTCType()
}

// Resolve real dimensions (apply frame rotation)
set(dimensions: Dimensions(width: rtcFrame.width, height: rtcFrame.height).apply(rotation: rtcFrame.rotation))
// Apply processing if we have a processor attached.
if let processor = _state.processor {
guard let processedFrame = processor.process(frame: lkFrame) else {
log("VideoProcessor didn't return a frame, skipping frame.", .warning)
return
}
lkFrame = processedFrame
rtcFrame = processedFrame.toRTCType()
}

// Resolve real dimensions (apply frame rotation)
set(dimensions: Dimensions(width: rtcFrame.width, height: rtcFrame.height).apply(rotation: rtcFrame.rotation))

delegate?.capturer(capturer, didCapture: rtcFrame)
delegate?.capturer(capturer, didCapture: rtcFrame)

if rendererDelegates.isDelegatesNotEmpty {
rendererDelegates.notify { renderer in
renderer.render?(frame: lkFrame)
renderer.render?(frame: lkFrame, captureDevice: device, captureOptions: options)
if rendererDelegates.isDelegatesNotEmpty {
rendererDelegates.notify { renderer in
renderer.render?(frame: lkFrame)
renderer.render?(frame: lkFrame, captureDevice: device, captureOptions: options)
}
}
}
}
Expand Down

0 comments on commit 7c9f7b1

Please sign in to comment.