When attempting to merge audio with video, I notice a loss of video transparency in the resulting merged video. How can I maintain the video's transparency?
Video details:
- AVVideoCodecKey: .hevcWithAlpha
- fileType: .mov
Audio details:
- fileType: .m4a
Merging code:
func mergeMovieWithAudio(movieUrl: URL, audioUrl: URL, success: @escaping ((URL) -> Void), failure: @escaping ((Error?) -> Void)) async {
let mixComposition: AVMutableComposition = AVMutableComposition()
var mutableCompositionVideoTrack: [AVMutableCompositionTrack] = []
var mutableCompositionAudioTrack: [AVMutableCompositionTrack] = []
let totalVideoCompositionInstruction : AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()
let aVideoAsset: AVAsset = AVAsset(url: movieUrl)
let aAudioAsset: AVAsset = AVAsset(url: audioUrl)
guard let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else { return }
guard let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid) else { return }
mutableCompositionVideoTrack.append(videoTrack)
mutableCompositionAudioTrack.append(audioTrack)
do {
guard let aVideoAssetTrack = try await aVideoAsset.loadTracks(withMediaType: .video).first else { return }
guard let aAudioAssetTrack = try await aAudioAsset.loadTracks(withMediaType: .audio).first else { return }
let aVideoTimeRange = try await aVideoAssetTrack.load(.timeRange)
try mutableCompositionVideoTrack.first?.insertTimeRange(aVideoTimeRange, of: aVideoAssetTrack, at: CMTime.zero)
try mutableCompositionAudioTrack.first?.insertTimeRange(aVideoTimeRange, of: aAudioAssetTrack, at: CMTime.zero)
videoTrack.preferredTransform = try await aVideoAssetTrack.load(.preferredTransform)
totalVideoCompositionInstruction.timeRange = aVideoTimeRange
let mutableVideoComposition: AVMutableVideoComposition = AVMutableVideoComposition()
mutableVideoComposition.frameDuration = try await aVideoAssetTrack.load(.minFrameDuration)
mutableVideoComposition.renderSize = try await aVideoAssetTrack.load(.naturalSize)
}
catch {
print(error.localizedDescription)
}
guard let outputURL = makeFileOutputURL(fileName: "movie.mov") else { return }
if let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) {
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileType.mov
exportSession.shouldOptimizeForNetworkUse = true
await exportSession.export()
switch exportSession.status {
case .failed:
if let _error = exportSession.error {
failure(_error)
}
case .cancelled:
if let _error = exportSession.error {
failure(_error)
}
default:
success(outputURL)
}
} else {
failure(nil)
}
}
func makeFileOutputURL(fileName: String) -> URL? {
do {
var cachesDirectory: URL = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
cachesDirectory.appendPathComponent(fileName)
if FileManager.default.fileExists(atPath: cachesDirectory.path) {
try FileManager.default.removeItem(atPath: cachesDirectory.path)
}
return cachesDirectory
} catch {
print(error)
return nil
}
}
Transparent video tester: https://rotato.app/tools/transparent-video