Swift AVFoundation多视频拼接:保留变换及多视频轨道使用咨询
Absolutely! You definitely can use multiple video-type AVMutableCompositionTrack instances within a single AVMutableComposition—this is actually the recommended approach for handling scenarios where different clips need distinct transforms (like front-camera mirroring). Your earlier issue with lost transform data when using a single track happens because a single track can only apply one transform context (or gets overwritten by subsequent clips), but multi-track setups let you isolate and customize transforms for each clip.
Here's a step-by-step implementation to fix your problem:
1. Create Separate Video Tracks for Different Clip Types
First, create distinct composition tracks to hold your normal video and mirrored front-camera video. This keeps their transform settings isolated:
let composition = AVMutableComposition() // Create two separate video tracks guard let normalVideoTrack = composition.addMutableTrack( withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid ), let mirroredVideoTrack = composition.addMutableTrack( withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid ) else { fatalError("Failed to create composition tracks") }
2. Insert Clips into Their Respective Tracks
Add your normal video to one track and the mirrored front-camera video to the other, making sure to set correct time ranges to avoid gaps or overlaps:
// Assume you have your source assets: normalAsset (first video) and mirroredAsset (front-camera clip) guard let normalSourceTrack = normalAsset.tracks(withMediaType: .video).first, let mirroredSourceTrack = mirroredAsset.tracks(withMediaType: .video).first else { return } // Insert normal video at the start of its track try! normalVideoTrack.insertTimeRange( CMTimeRange(start: .zero, duration: normalAsset.duration), of: normalSourceTrack, at: .zero ) // Insert mirrored video right after the normal video ends let mirroredStartTime = normalAsset.duration try! mirroredVideoTrack.insertTimeRange( CMTimeRange(start: .zero, duration: mirroredAsset.duration), of: mirroredSourceTrack, at: mirroredStartTime )
3. Configure Track-Specific Transforms with Video Composition Instructions
This is the critical part you were likely missing. Use AVMutableVideoComposition and AVMutableVideoCompositionLayerInstruction to apply unique transforms to each track:
let videoComposition = AVMutableVideoComposition() videoComposition.renderSize = CGSize(width: 1920, height: 1080) // Match your desired output resolution videoComposition.frameDuration = CMTime(value: 1, timescale: 30) // Set your target frame rate // Preserve the original transform for the normal video track let normalLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: normalVideoTrack) normalLayerInstruction.setTransform(normalSourceTrack.preferredTransform, at: .zero) // Apply mirror transform (flip horizontally) to the front-camera track, concatenated with its original preferred transform let mirroredTransform = CGAffineTransform(scaleX: -1, y: 1) .concatenating(mirroredSourceTrack.preferredTransform) let mirroredLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: mirroredVideoTrack) mirroredLayerInstruction.setTransform(mirroredTransform, at: mirroredStartTime) // Assemble the main instruction and attach layer instructions let mainInstruction = AVMutableVideoCompositionInstruction() mainInstruction.timeRange = CMTimeRange( start: .zero, duration: CMTimeAdd(normalAsset.duration, mirroredAsset.duration) ) mainInstruction.layerInstructions = [normalLayerInstruction, mirroredLayerInstruction] videoComposition.instructions = [mainInstruction]
4. Use the Video Composition During Export
When setting up your export session, assign the custom videoComposition to ensure the transforms are applied correctly:
guard let exportSession = AVAssetExportSession( asset: composition, presetName: AVAssetExportPresetHighestQuality ) else { fatalError("Failed to create export session") } exportSession.outputURL = // Your output file URL (e.g., in the documents directory) exportSession.outputFileType = .mov exportSession.videoComposition = videoComposition // This is key! exportSession.exportAsynchronously { // Handle export completion (success/failure) here DispatchQueue.main.async { if exportSession.status == .completed { print("Export finished successfully!") } else { print("Export failed: \(exportSession.error?.localizedDescription ?? "Unknown error")") } } }
Why This Works
When using a single track, the track's transform is overwritten by the last clip you add, which is why your first video's transform was lost. With multiple tracks, each clip's transform is managed independently via layer instructions, giving you full control over how each clip is rendered.
You can even extend this setup to adjust clip opacity, layer order, or apply other transforms to individual clips if needed.
内容的提问来源于stack exchange,提问作者fsoc

