Make SampleWriter.init nonisolated so it can load tracks

This commit is contained in:
Sami Samhuri 2024-08-12 19:01:21 -07:00
parent 996251cfde
commit 8f049aed95
No known key found for this signature in database
5 changed files with 19 additions and 19 deletions

View file

@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
7B7AE3092C36615700DB7391 /* SampleWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7AE3082C36615700DB7391 /* SampleWriter.swift */; }; 7B7AE3092C36615700DB7391 /* SampleWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7AE3082C36615700DB7391 /* SampleWriter.swift */; };
7B9867982C6AF57C001353BC /* AVAsset+sending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC8B2C3BB0180090B757 /* AVAsset+sending.swift */; };
7B9BC00E2C305D2C00C160C2 /* SJSAssetExportSession.docc in Sources */ = {isa = PBXBuildFile; fileRef = 7B9BC00D2C305D2C00C160C2 /* SJSAssetExportSession.docc */; }; 7B9BC00E2C305D2C00C160C2 /* SJSAssetExportSession.docc in Sources */ = {isa = PBXBuildFile; fileRef = 7B9BC00D2C305D2C00C160C2 /* SJSAssetExportSession.docc */; };
7B9BC0142C305D2C00C160C2 /* SJSAssetExportSession.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B9BC0092C305D2C00C160C2 /* SJSAssetExportSession.framework */; }; 7B9BC0142C305D2C00C160C2 /* SJSAssetExportSession.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B9BC0092C305D2C00C160C2 /* SJSAssetExportSession.framework */; };
7B9BC0192C305D2C00C160C2 /* SJSAssetExportSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9BC0182C305D2C00C160C2 /* SJSAssetExportSessionTests.swift */; }; 7B9BC0192C305D2C00C160C2 /* SJSAssetExportSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9BC0182C305D2C00C160C2 /* SJSAssetExportSessionTests.swift */; };
@ -16,7 +17,6 @@
7BC5FC772C3B8C5A0090B757 /* SendableWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC762C3B8C5A0090B757 /* SendableWrapper.swift */; }; 7BC5FC772C3B8C5A0090B757 /* SendableWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC762C3B8C5A0090B757 /* SendableWrapper.swift */; };
7BC5FC792C3B90F70090B757 /* AutoDestructingURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC782C3B90F70090B757 /* AutoDestructingURL.swift */; }; 7BC5FC792C3B90F70090B757 /* AutoDestructingURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC782C3B90F70090B757 /* AutoDestructingURL.swift */; };
7BC5FC8A2C3BAA150090B757 /* ExportSession+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC892C3BAA150090B757 /* ExportSession+Error.swift */; }; 7BC5FC8A2C3BAA150090B757 /* ExportSession+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC892C3BAA150090B757 /* ExportSession+Error.swift */; };
7BC5FC8C2C3BB0180090B757 /* AVAsset+sending.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC8B2C3BB0180090B757 /* AVAsset+sending.swift */; };
7BC5FC902C3BB2030090B757 /* AudioOutputSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC8F2C3BB2030090B757 /* AudioOutputSettings.swift */; }; 7BC5FC902C3BB2030090B757 /* AudioOutputSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC8F2C3BB2030090B757 /* AudioOutputSettings.swift */; };
7BC5FC922C3BB4BD0090B757 /* VideoOutputSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC912C3BB4BD0090B757 /* VideoOutputSettings.swift */; }; 7BC5FC922C3BB4BD0090B757 /* VideoOutputSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC912C3BB4BD0090B757 /* VideoOutputSettings.swift */; };
7BC5FC942C3BC3AD0090B757 /* CMTime+seconds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC932C3BC3AD0090B757 /* CMTime+seconds.swift */; }; 7BC5FC942C3BC3AD0090B757 /* CMTime+seconds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC5FC932C3BC3AD0090B757 /* CMTime+seconds.swift */; };
@ -95,7 +95,6 @@
children = ( children = (
7BC5FC8F2C3BB2030090B757 /* AudioOutputSettings.swift */, 7BC5FC8F2C3BB2030090B757 /* AudioOutputSettings.swift */,
7BC5FC912C3BB4BD0090B757 /* VideoOutputSettings.swift */, 7BC5FC912C3BB4BD0090B757 /* VideoOutputSettings.swift */,
7BC5FC8B2C3BB0180090B757 /* AVAsset+sending.swift */,
7BC5FC932C3BC3AD0090B757 /* CMTime+seconds.swift */, 7BC5FC932C3BC3AD0090B757 /* CMTime+seconds.swift */,
7B9BC0272C30612C00C160C2 /* ExportSession.swift */, 7B9BC0272C30612C00C160C2 /* ExportSession.swift */,
7BC5FC892C3BAA150090B757 /* ExportSession+Error.swift */, 7BC5FC892C3BAA150090B757 /* ExportSession+Error.swift */,
@ -110,6 +109,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7BC5FC782C3B90F70090B757 /* AutoDestructingURL.swift */, 7BC5FC782C3B90F70090B757 /* AutoDestructingURL.swift */,
7BC5FC8B2C3BB0180090B757 /* AVAsset+sending.swift */,
7BC5FC812C3B9E3D0090B757 /* Resources */, 7BC5FC812C3B9E3D0090B757 /* Resources */,
7BC5FC762C3B8C5A0090B757 /* SendableWrapper.swift */, 7BC5FC762C3B8C5A0090B757 /* SendableWrapper.swift */,
7B9BC0182C305D2C00C160C2 /* SJSAssetExportSessionTests.swift */, 7B9BC0182C305D2C00C160C2 /* SJSAssetExportSessionTests.swift */,
@ -233,7 +233,6 @@
7B7AE3092C36615700DB7391 /* SampleWriter.swift in Sources */, 7B7AE3092C36615700DB7391 /* SampleWriter.swift in Sources */,
7BC5FC902C3BB2030090B757 /* AudioOutputSettings.swift in Sources */, 7BC5FC902C3BB2030090B757 /* AudioOutputSettings.swift in Sources */,
7B9BC0282C30612C00C160C2 /* ExportSession.swift in Sources */, 7B9BC0282C30612C00C160C2 /* ExportSession.swift in Sources */,
7BC5FC8C2C3BB0180090B757 /* AVAsset+sending.swift in Sources */,
7BC5FC922C3BB4BD0090B757 /* VideoOutputSettings.swift in Sources */, 7BC5FC922C3BB4BD0090B757 /* VideoOutputSettings.swift in Sources */,
7B9BC00E2C305D2C00C160C2 /* SJSAssetExportSession.docc in Sources */, 7B9BC00E2C305D2C00C160C2 /* SJSAssetExportSession.docc in Sources */,
7BC5FC8A2C3BAA150090B757 /* ExportSession+Error.swift in Sources */, 7BC5FC8A2C3BAA150090B757 /* ExportSession+Error.swift in Sources */,
@ -246,6 +245,7 @@
files = ( files = (
7B9BC0192C305D2C00C160C2 /* SJSAssetExportSessionTests.swift in Sources */, 7B9BC0192C305D2C00C160C2 /* SJSAssetExportSessionTests.swift in Sources */,
7BC5FC792C3B90F70090B757 /* AutoDestructingURL.swift in Sources */, 7BC5FC792C3B90F70090B757 /* AutoDestructingURL.swift in Sources */,
7B9867982C6AF57C001353BC /* AVAsset+sending.swift in Sources */,
7BC5FC772C3B8C5A0090B757 /* SendableWrapper.swift in Sources */, 7BC5FC772C3B8C5A0090B757 /* SendableWrapper.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View file

@ -45,12 +45,12 @@ actor SampleWriter {
private var videoInput: AVAssetWriterInput? private var videoInput: AVAssetWriterInput?
private var isCancelled = false private var isCancelled = false
init( nonisolated init(
asset: sending AVAsset, asset: sending AVAsset,
audioOutputSettings: sending [String: (any Sendable)], audioOutputSettings: sending [String: (any Sendable)],
audioMix: AVAudioMix?, audioMix: sending AVAudioMix?,
videoOutputSettings: sending [String: (any Sendable)], videoOutputSettings: sending [String: (any Sendable)],
videoComposition: AVVideoComposition, videoComposition: sending AVVideoComposition,
timeRange: CMTimeRange? = nil, timeRange: CMTimeRange? = nil,
optimizeForNetworkUse: Bool = false, optimizeForNetworkUse: Bool = false,
outputURL: URL, outputURL: URL,
@ -67,10 +67,14 @@ actor SampleWriter {
let writer = try AVAssetWriter(outputURL: outputURL, fileType: fileType) let writer = try AVAssetWriter(outputURL: outputURL, fileType: fileType)
writer.shouldOptimizeForNetworkUse = optimizeForNetworkUse writer.shouldOptimizeForNetworkUse = optimizeForNetworkUse
let audioTracks = try await asset.sendTracks(withMediaType: .audio) let audioTracks = try await asset.loadTracks(withMediaType: .audio)
try Self.validateAudio(tracks: audioTracks, outputSettings: audioOutputSettings, writer: writer) // Audio is optional so only validate output settings when it's applicable.
let videoTracks = try await asset.sendTracks(withMediaType: .video) if !audioTracks.isEmpty {
try Self.validateVideo(tracks: videoTracks, outputSettings: videoOutputSettings, writer: writer) try Self.validateAudio(outputSettings: audioOutputSettings, writer: writer)
}
let videoTracks = try await asset.loadTracks(withMediaType: .video)
guard !videoTracks.isEmpty else { throw Error.setupFailure(.videoTracksEmpty) }
try Self.validateVideo(outputSettings: videoOutputSettings, writer: writer)
Self.warnAboutMismatchedVideoSize( Self.warnAboutMismatchedVideoSize(
renderSize: videoComposition.renderSize, renderSize: videoComposition.renderSize,
settings: videoOutputSettings settings: videoOutputSettings
@ -85,8 +89,8 @@ actor SampleWriter {
self.duration = duration self.duration = duration
self.timeRange = timeRange ?? CMTimeRange(start: .zero, duration: duration) self.timeRange = timeRange ?? CMTimeRange(start: .zero, duration: duration)
try setUpAudio(audioTracks: audioTracks) try await setUpAudio(audioTracks: audioTracks)
try setUpVideo(videoTracks: videoTracks) try await setUpVideo(videoTracks: videoTracks)
} }
func writeSamples() async throws { func writeSamples() async throws {
@ -281,11 +285,9 @@ actor SampleWriter {
// MARK: Input validation // MARK: Input validation
private static func validateAudio( private static func validateAudio(
tracks: [AVAssetTrack],
outputSettings: [String: any Sendable], outputSettings: [String: any Sendable],
writer: AVAssetWriter writer: AVAssetWriter
) throws { ) throws {
guard !tracks.isEmpty else { return } // Audio is optional so this isn't a failure.
guard !outputSettings.isEmpty else { throw Error.setupFailure(.audioSettingsEmpty) } guard !outputSettings.isEmpty else { throw Error.setupFailure(.audioSettingsEmpty) }
guard writer.canApply(outputSettings: outputSettings, forMediaType: .audio) else { guard writer.canApply(outputSettings: outputSettings, forMediaType: .audio) else {
throw Error.setupFailure(.audioSettingsInvalid) throw Error.setupFailure(.audioSettingsInvalid)
@ -293,11 +295,9 @@ actor SampleWriter {
} }
private static func validateVideo( private static func validateVideo(
tracks: [AVAssetTrack],
outputSettings: [String: any Sendable], outputSettings: [String: any Sendable],
writer: AVAssetWriter writer: AVAssetWriter
) throws { ) throws {
guard !tracks.isEmpty else { throw Error.setupFailure(.videoTracksEmpty) }
guard writer.canApply(outputSettings: outputSettings, forMediaType: .video) else { guard writer.canApply(outputSettings: outputSettings, forMediaType: .video) else {
throw Error.setupFailure(.videoSettingsInvalid) throw Error.setupFailure(.videoSettingsInvalid)
} }

View file

@ -5,7 +5,7 @@
// Created by Sami Samhuri on 2024-07-07. // Created by Sami Samhuri on 2024-07-07.
// //
public import AVFoundation import AVFoundation
public struct VideoOutputSettings { public struct VideoOutputSettings {
public enum H264Profile { public enum H264Profile {

View file

@ -1,6 +1,6 @@
// //
// AVAsset+sending.swift // AVAsset+sending.swift
// SJSAssetExportSession // SJSAssetExportSessionTests
// //
// Created by Sami Samhuri on 2024-07-07. // Created by Sami Samhuri on 2024-07-07.
// //

View file

@ -5,7 +5,7 @@
// Created by Sami Samhuri on 2024-06-29. // Created by Sami Samhuri on 2024-06-29.
// //
import AVFoundation internal import AVFoundation
@testable import SJSAssetExportSession @testable import SJSAssetExportSession
import Testing import Testing