implement ChunkedRecorder

- save initialization and data chunks as individual files
- ChunkType identifies chunks as initialization or data chunks
- add onChunkReady callback to ChunkedRecorder
This commit is contained in:
Rui Rodrigues 2024-07-15 08:46:41 +01:00
parent d9a1287b68
commit 89ecb35616
2 changed files with 36 additions and 19 deletions

View File

@ -12,14 +12,24 @@ import AVFoundation
class ChunkedRecorder: NSObject { class ChunkedRecorder: NSObject {
enum ChunkType {
case initialization
case data(index: UInt64)
}
struct Chunk {
let url: URL
let type: ChunkType
}
let outputURL: URL let outputURL: URL
let onChunkReady: ((Chunk) -> Void)
private var initSegment: Data? private var index: UInt64 = 0
private var index: Int = 0
init(url: URL) throws { init(url: URL, onChunkReady: @escaping ((Chunk) -> Void)) throws {
outputURL = url outputURL = url
self.onChunkReady = onChunkReady
guard FileManager.default.fileExists(atPath: outputURL.path) else { guard FileManager.default.fileExists(atPath: outputURL.path) else {
throw CameraError.unknown(message: "output directory does not exist at: \(outputURL.path)", cause: nil) throw CameraError.unknown(message: "output directory does not exist at: \(outputURL.path)", cause: nil)
} }
@ -45,26 +55,31 @@ extension ChunkedRecorder: AVAssetWriterDelegate {
} }
private func saveInitSegment(_ data: Data) { private func saveInitSegment(_ data: Data) {
initSegment = data let url = outputURL.appendingPathComponent("init.mp4")
save(data: data, url: url)
onChunkReady(url: url, type: .initialization)
} }
private func saveSegment(_ data: Data) { private func saveSegment(_ data: Data) {
guard let initSegment else { defer {
print("missing init segment")
return
}
let file = String(format: "%06d.mp4", index)
index += 1 index += 1
let url = outputURL.appendingPathComponent(file)
do {
let outputData = initSegment + data
try outputData.write(to: url)
print("writing", data.count, "to", url)
} catch {
print("Error--->", error)
} }
let name = String(format: "%06d.mp4", index)
let url = outputURL.appendingPathComponent(name)
save(data: data, url: url)
onChunkReady(url: url, type: .data(index: index))
}
private func save(data: Data, url: URL) {
do {
try data.write(to: url)
} catch {
ReactLogger.log(level: .error, message: "Unable to write \(url): \(error.localizedDescription)")
}
}
private func onChunkReady(url: URL, type: ChunkType) {
onChunkReady(Chunk(url: url, type: type))
} }
} }

View File

@ -78,7 +78,9 @@ class RecordingSession {
completionHandler = completion completionHandler = completion
do { do {
recorder = try ChunkedRecorder(url: url.deletingLastPathComponent()) recorder = try ChunkedRecorder(url: url.deletingLastPathComponent()) { segment in
ReactLogger.log(level: .info, message: "Chunk ready: \(segment)")
}
assetWriter = AVAssetWriter(contentType: UTType(fileType.rawValue)!) assetWriter = AVAssetWriter(contentType: UTType(fileType.rawValue)!)
assetWriter.shouldOptimizeForNetworkUse = false assetWriter.shouldOptimizeForNetworkUse = false
assetWriter.outputFileTypeProfile = .mpeg4AppleHLS assetWriter.outputFileTypeProfile = .mpeg4AppleHLS