我正在解决一个小问题:同时获取一组有序数据块并以正确的顺序输出(具体来说,获取
.ts
文件,通过管道传递
ffmpeg
按照正确的顺序,但这是偶然的)。为了将重新排序(按顺序交付片段)和管道(让ffmpeg来做这件事)分开,我将其编写为两个对象:
struct Aggregator<ChunkType> {
private var currentIdx = 0
private var chunks: [Int:ChunkType] = [:]
private let serializer: Serializer
init(serializer: Serializer) where Serializer.ChunkType == ChunkType {
// self.serializer = serializer
}
private let queue = DispatchQueue(label: "Serial")
mutating func accept(chunk:ChunkType, forIndex idx: Int) {
// One accessor at a time
queue.sync {
chunks[idx] = chunk
// Forward zero or more chunks
while chunks[currentIdx] != nil {
//serializer.append(chunk: chunks.removeValue(forKey: currentIdx))
currentIdx += 1
}
}
}
}
以及:
protocol Serializer {
associatedtype ChunkType
func append(chunk: ChunkType)
}
有两种实现:
// For capturing the output of an Aggregator<Int> to check it's working as intended
class IntArraySerializer: Serializer {
typealias ChunkType = Int
var allInts = [Int]()
func append(chunk: ChunkType) {
allInts.append(chunk)
}
}
// For piping output through ffmpeg
class FFMPEGSerializer: Serializer {
typealias ChunkType = Data
private let ffmpegBin = "/usr/local/bin/ffmpeg"
private let ffmpeg: Process
private let stdin: Pipe
init(args: [String]) throws {
ffmpeg = Process()
ffmpeg.launchPath = ffmpegBin
ffmpeg.arguments = args
stdin = Pipe()
ffmpeg.standardInput = stdin
}
func append(chunk: Data) {
stdin.fileHandleForWriting.write(chunk)
}
}
最终目标是使用类似于:
let ffmpeg = FFMPEGSerializer(["-i", "-", "-c", "copy", "-bsf:a aac_adtstoasc", "output.mp4"])
let aggregator = Aggregator(serializer: ffmpeg)
for (idx, url) in someListOfUrls.enumerated() {
concurrentQueue.async {
fetch(url) { data in
aggregator.accept(chunk: data)
}
}
}
然而,我对相关类型、泛型和可能的类型擦除(如果这确实是这里的解决方案)的理解阻碍了我最终确定
Aggregator
,可以从各种注释行中看到。我读过一些关于类型擦除的内容(例如。
this Big Nerd Ranch article
),但我没有掌握如何或是否应该尝试将其应用到我的案例中。
归结为一句话:我想要
ChunkType
的
Serializer
通知
聚合器
。