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

fixed an issue with the handling of CADisplayLink's duration. #1624

Merged
merged 1 commit into from
Nov 16, 2024
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
12 changes: 11 additions & 1 deletion HaishinKit/Sources/HKStream/MediaLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation

final actor MediaLink {
static let capacity = 90
static let startedAt: TimeInterval = 0.0

var dequeue: AsyncStream<CMSampleBuffer> {
AsyncStream<CMSampleBuffer> { continutation in
Expand All @@ -16,6 +17,7 @@ final actor MediaLink {
oldValue?.finish()
}
}
private var startedAt: TimeInterval = MediaLink.startedAt
private var presentationTimeStampOrigin: CMTime = .invalid
private lazy var displayLink = DisplayLinkChoreographer()
private weak var audioPlayer: AudioPlayerNode?
Expand Down Expand Up @@ -45,6 +47,13 @@ final actor MediaLink {
func setAudioPlayer(_ audioPlayer: AudioPlayerNode?) {
self.audioPlayer = audioPlayer
}

private func getCurrentTime(_ currentTime: TimeInterval) async -> TimeInterval {
if startedAt == 0 {
startedAt = currentTime
}
return await audioPlayer?.currentTime ?? (currentTime - startedAt)
}
}

extension MediaLink: AsyncRunner {
Expand All @@ -56,11 +65,12 @@ extension MediaLink: AsyncRunner {
isRunning = true
displayLink.startRunning()
Task {
startedAt = MediaLink.startedAt
for await currentTime in displayLink.updateFrames where isRunning {
guard let storage else {
continue
}
let currentTime = await audioPlayer?.currentTime ?? currentTime
let currentTime = await getCurrentTime(currentTime)
var frameCount = 0
while !storage.isEmpty {
guard let first = storage.head else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import Foundation

#if os(macOS)

import CoreVideo
import Foundation

// swiftlint:disable attributes
// CADisplayLink is deprecated, I've given up on making it conform to Sendable.
Expand All @@ -12,7 +10,7 @@ final class DisplayLink: NSObject, @unchecked Sendable {

var isPaused = false {
didSet {
guard let displayLink = displayLink, oldValue != isPaused else {
guard let displayLink, oldValue != isPaused else {
return
}
if isPaused {
Expand Down Expand Up @@ -64,14 +62,14 @@ final class DisplayLink: NSObject, @unchecked Sendable {
}

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 @@ -96,19 +94,7 @@ import QuartzCore
typealias DisplayLink = CADisplayLink
#endif

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

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

func clear()
}

final class DisplayLinkChoreographer: NSObject {
private static let currentTime = 0.0
private static let preferredFramesPerSecond = 0

var updateFrames: AsyncStream<TimeInterval> {
Expand All @@ -127,18 +113,20 @@ final class DisplayLinkChoreographer: NSObject {
private(set) var isRunning = false
private var displayLink: DisplayLink? {
didSet {
guard displayLink != oldValue else {
return
}
displayLink?.preferredFramesPerSecond = preferredFramesPerSecond
displayLink?.isPaused = false
displayLink?.add(to: .main, forMode: .common)
oldValue?.invalidate()
}
}
private var currentTime: TimeInterval = DisplayLinkChoreographer.currentTime
private var continutation: AsyncStream<TimeInterval>.Continuation?

@objc
private func update(displayLink: DisplayLink) {
continutation?.yield(currentTime)
currentTime += displayLink.duration
continutation?.yield(displayLink.timestamp)
}
}

Expand All @@ -156,7 +144,6 @@ extension DisplayLinkChoreographer: Runner {
return
}
displayLink = nil
currentTime = DisplayLinkChoreographer.currentTime
isRunning = false
}
}