Skip to content

Commit

Permalink
Merge pull request #1625 from shogo4405/feature/fix-DisplayLinkChoreo…
Browse files Browse the repository at this point in the history
…grapher-backport

backport #1624 to 1.9.x
  • Loading branch information
shogo4405 authored Nov 17, 2024
2 parents dd1e98a + 05b9da6 commit eb1d77f
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
disableMainThreadChecker = "YES"
disablePerformanceAntipatternChecker = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
13 changes: 9 additions & 4 deletions Sources/IO/MediaLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ final class MediaLink<T: MediaLinkDelegate> {
private var scheduledAudioBuffers: Atomic<Int> = .init(0)
private var presentationTimeStampOrigin: CMTime = .invalid
private var audioTime = IOAudioTime()
private var duration: TimeInterval = 0.0

func enqueue(_ buffer: CMSampleBuffer) {
guard buffer.presentationTimeStamp != .invalid else {
Expand Down Expand Up @@ -102,14 +103,17 @@ final class MediaLink<T: MediaLinkDelegate> {
})
}

private func duration(_ duraiton: Double) -> Double {
private func duration(_ timestamp: Double) -> Double {
defer {
duration += timestamp
}
if playerNode.isPlaying {
guard let nodeTime = playerNode.lastRenderTime, let playerTime = playerNode.playerTime(forNodeTime: nodeTime) else {
return 0.0
}
return TimeInterval(playerTime.sampleTime) / playerTime.sampleRate
}
return duraiton
return duration
}

private func makeBufferkQueue() {
Expand All @@ -123,11 +127,11 @@ final class MediaLink<T: MediaLinkDelegate> {

extension MediaLink: ChoreographerDelegate {
// MARK: ChoreographerDelegate
func choreographer(_ choreographer: some Choreographer, didFrame duration: Double) {
func choreographer(_ choreographer: some Choreographer, didFrame timestamp: TimeInterval, targetTimestamp: TimeInterval) {
guard let bufferQueue else {
return
}
let duration = self.duration(duration)
let duration = self.duration(targetTimestamp - timestamp)
var frameCount = 0
while !bufferQueue.isEmpty {
guard let first = bufferQueue.head else {
Expand Down Expand Up @@ -158,6 +162,7 @@ extension MediaLink: Running {
hasVideo = false
bufferingTime = kMediaLink_bufferingTime
isBuffering = true
duration = 0.0
choreographer.startRunning()
makeBufferkQueue()
isRunning.mutate { $0 = true }
Expand Down
49 changes: 19 additions & 30 deletions Sources/Screen/Choreographer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Foundation
#if os(macOS)

import CoreVideo
import Foundation

// swiftlint:disable attributes

Expand All @@ -30,11 +29,12 @@ final class DisplayLink: NSObject {
frameInterval = 1.0 / Double(preferredFramesPerSecond)
}
}
private(set) var duration = 0.0
private(set) var timestamp: CFTimeInterval = 0
private(set) var targetTimestamp: CFTimeInterval = 0
private var selector: Selector?
private var displayLink: CVDisplayLink?
private var frameInterval = 0.0
private var duration: CFTimeInterval = 0
private weak var delegate: NSObject?

deinit {
Expand All @@ -53,25 +53,24 @@ final class DisplayLink: NSObject {
guard let self else {
return kCVReturnSuccess
}
self.duration += inNow.pointee.duration
if frameInterval == 0 || frameInterval < inNow.pointee.timestamp - self.timestamp {
self.timestamp = inNow.pointee.timestamp
if frameInterval == 0 || frameInterval <= inNow.pointee.timestamp - self.timestamp {
self.timestamp = Double(inNow.pointee.timestamp)
self.targetTimestamp = self.timestamp + frameInterval
_ = self.delegate?.perform(self.selector, with: self)
self.duration = 0.0
}
return kCVReturnSuccess
}
}

func add(to runloop: RunLoop, forMode mode: RunLoop.Mode) {
guard let displayLink = displayLink, !isPaused else {
guard let displayLink, !isPaused else {
return
}
CVDisplayLinkStart(displayLink)
}

func invalidate() {
guard let displayLink = displayLink, isPaused else {
guard let displayLink, isPaused else {
return
}
CVDisplayLinkStop(displayLink)
Expand All @@ -81,11 +80,7 @@ final class DisplayLink: NSObject {
extension CVTimeStamp {
@inlinable @inline(__always)
var timestamp: Double {
Double(self.videoTime) / Double(self.videoTimeScale)
}

@inlinable @inline(__always) var duration: Double {
Double(self.videoRefreshPeriod) / Double(self.videoTimeScale)
Double(self.hostTime) / Double(self.videoTimeScale)
}
}

Expand All @@ -97,18 +92,15 @@ typealias DisplayLink = CADisplayLink
#endif

protocol ChoreographerDelegate: AnyObject {
func choreographer(_ choreographer: some Choreographer, didFrame duration: Double)
func choreographer(_ choreographer: some Choreographer, didFrame timestamp: TimeInterval, targetTimestamp: TimeInterval)
}

protocol Choreographer: Running {
var isPaused: Bool { get set }
var delegate: (any ChoreographerDelegate)? { get set }

func clear()
}

final class DisplayLinkChoreographer: NSObject, Choreographer {
private static let duration = 0.0
private static let preferredFramesPerSecond = 0

var isPaused: Bool {
Expand All @@ -121,28 +113,26 @@ final class DisplayLinkChoreographer: NSObject, Choreographer {
}
weak var delegate: (any ChoreographerDelegate)?
var isRunning: Atomic<Bool> = .init(false)
var preferredFramesPerSecond = DisplayLinkChoreographer.preferredFramesPerSecond
private var duration: Double = DisplayLinkChoreographer.duration
private var displayLink: DisplayLink? {
var preferredFramesPerSecond = DisplayLinkChoreographer.preferredFramesPerSecond {
didSet {
oldValue?.invalidate()
guard let displayLink = displayLink else {
guard let displayLink, preferredFramesPerSecond != oldValue else {
return
}
displayLink.isPaused = true
displayLink.preferredFramesPerSecond = preferredFramesPerSecond
displayLink.add(to: .main, forMode: .common)
}
}

func clear() {
duration = Self.duration
private var displayLink: DisplayLink? {
didSet {
oldValue?.invalidate()
displayLink?.isPaused = true
displayLink?.preferredFramesPerSecond = preferredFramesPerSecond
displayLink?.add(to: .main, forMode: .common)
}
}

@objc
private func update(displayLink: DisplayLink) {
delegate?.choreographer(self, didFrame: duration)
duration += displayLink.duration
delegate?.choreographer(self, didFrame: displayLink.timestamp, targetTimestamp: displayLink.targetTimestamp)
}
}

Expand All @@ -154,7 +144,6 @@ extension DisplayLinkChoreographer: Running {

func stopRunning() {
displayLink = nil
duration = DisplayLinkChoreographer.duration
isRunning.mutate { $0 = false }
}
}
2 changes: 1 addition & 1 deletion Sources/Screen/Screen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ extension Screen: Running {

extension Screen: ChoreographerDelegate {
// MARK: ChoreographerDelegate
func choreographer(_ choreographer: some Choreographer, didFrame duration: Double) {
func choreographer(_ choreographer: some Choreographer, didFrame timestamp: TimeInterval, targetTimestamp: TimeInterval) {
var pixelBuffer: CVPixelBuffer?
pixelBufferPool?.createPixelBuffer(&pixelBuffer)
guard let pixelBuffer else {
Expand Down

0 comments on commit eb1d77f

Please sign in to comment.