Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport #1624 to 1.9.x #1625

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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