-
-
Notifications
You must be signed in to change notification settings - Fork 690
Expand file tree
/
Copy pathMediaLink.swift
More file actions
104 lines (96 loc) · 3.04 KB
/
MediaLink.swift
File metadata and controls
104 lines (96 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import CoreMedia
import Foundation
final actor MediaLink {
static let capacity = 90
static let duration: TimeInterval = 0.0
var dequeue: AsyncStream<CMSampleBuffer> {
AsyncStream { continutation in
self.continutation = continutation
}
}
private(set) var isRunning = false
private var storage: TypedBlockQueue<CMSampleBuffer>?
private var continutation: AsyncStream<CMSampleBuffer>.Continuation? {
didSet {
oldValue?.finish()
}
}
private var duration: TimeInterval = MediaLink.duration
private var presentationTimeStampOrigin: CMTime = .invalid
private lazy var displayLink = DisplayLinkChoreographer()
private weak var audioPlayer: AudioPlayerNode?
init() {
do {
storage = try .init(capacity: Self.capacity, handlers: .outputPTSSortedSampleBuffers)
} catch {
logger.error(error)
}
}
func enqueue(_ sampleBuffer: CMSampleBuffer) {
guard isRunning else {
return
}
if presentationTimeStampOrigin == .invalid {
presentationTimeStampOrigin = sampleBuffer.presentationTimeStamp
}
do {
try storage?.enqueue(sampleBuffer)
} catch {
logger.error(error)
}
}
func setAudioPlayer(_ audioPlayer: AudioPlayerNode?) {
self.audioPlayer = audioPlayer
}
private func getCurrentTime(_ timestamp: TimeInterval) async -> TimeInterval {
defer {
duration += timestamp
}
return await audioPlayer?.currentTime ?? duration
}
}
extension MediaLink: AsyncRunner {
// MARK: AsyncRunner
func startRunning() {
guard !isRunning else {
return
}
isRunning = true
duration = 0.0
displayLink.startRunning()
Task {
for await currentTime in displayLink.updateFrames {
guard let storage else {
continue
}
let currentTime = await getCurrentTime(currentTime.targetTimestamp - currentTime.timestamp)
var frameCount = 0
while !storage.isEmpty {
guard let first = storage.head else {
break
}
if first.presentationTimeStamp.seconds - presentationTimeStampOrigin.seconds <= currentTime {
continutation?.yield(first)
frameCount += 1
_ = storage.dequeue()
} else {
if 2 < frameCount {
logger.info("droppedFrame: \(frameCount)")
}
break
}
}
}
}
}
func stopRunning() {
guard isRunning else {
return
}
continutation = nil
displayLink.stopRunning()
presentationTimeStampOrigin = .invalid
try? storage?.reset()
isRunning = false
}
}