快速叠加两个视频



我想重新挖掘这个问题。我在叠加两个视频时遇到问题。我相信这与第一个AVMutableVideoCompositionLayerInstruction的透明度有关,但我已经广泛使用了它,但没有运气。如有任何建议,我们将不胜感激!:

func overlay(video firstAsset: AVURLAsset, withSecondVideo secondAsset: AVURLAsset) {
let mixComposition = AVMutableComposition()
let firstTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
let secondTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
guard let firstMediaTrack = firstAsset.tracks(withMediaType: AVMediaType.video).first else { return }
guard let secondMediaTrack = secondAsset.tracks(withMediaType: AVMediaType.video).first else { return }
do {
try firstTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, firstAsset.duration), of: firstMediaTrack, at: kCMTimeZero)
try secondTrack?.insertTimeRange(CMTimeRangeMake(kCMTimeZero, secondAsset.duration), of: secondMediaTrack, at: kCMTimeZero)
} catch (let error) {
print(error)
}
self.width = max(firstMediaTrack.naturalSize.width, secondMediaTrack.naturalSize.width)
self.height = max(firstMediaTrack.naturalSize.height, secondMediaTrack.naturalSize.height)
let videoComposition = AVMutableVideoComposition()
videoComposition.renderSize = CGSize(width: width!, height: height!)
videoComposition.frameDuration = firstMediaTrack.minFrameDuration
let firstLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: firstMediaTrack)
let scale = CGAffineTransform(scaleX: 0.3, y: 0.3)
let move = CGAffineTransform(translationX: self.width! - ((self.width! * 0.3) + 10), y: 10)
firstLayerInstruction.setTransform(scale.concatenating(move), at: kCMTimeZero)
firstLayerInstruction.setOpacity(1.0, at: kCMTimeZero)
let secondlayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: secondMediaTrack)
secondlayerInstruction.setTransform((secondTrack?.preferredTransform)!, at: kCMTimeZero)
secondlayerInstruction.setOpacity(1.0, at: kCMTimeZero)
let combined = AVMutableVideoCompositionInstruction()
combined.timeRange = CMTimeRangeMake(kCMTimeZero, max(firstAsset.duration, secondAsset.duration))
combined.backgroundColor = UIColor.clear.cgColor
combined.layerInstructions = [firstLayerInstruction, secondlayerInstruction]
videoComposition.instructions = [combined]
let outputUrl = self.getPathForTempFileNamed(filename: "output.mov")
self.exportCompositedVideo(compiledVideo: mixComposition, toURL: outputUrl, withVideoComposition: videoComposition)
self.removeTempFileAtPath(path: outputUrl.absoluteString)
}

预期的结果是一个视频,里面有两个视频。第一层是全屏视频,第二层是位于右上角的较小视频。奇怪的是,当我在AVMutableVideoCompositionInstruction上使用,并将两个AVMutabableVideoCompositionLayerInstruction都放在其层指令中时,它就工作了!-但是FirstMediaTrack中使用的视频用于两层?玩了一段时间,然后试图实现这里详细介绍的方法,该方法对两层都有单独的说明,但这种方法只导致第一层显示全屏视频,而第二层完全不可见。

以下是适用于我的代码,我是根据本教程编写的。我发现关键是将背景设置为清晰(在这个线程上找到(。它也有一个规模,因为我正在试验缩小一个视频。

import AVFoundation
import AVKit
import Photos
var myurl: URL?
func newoverlay(video firstAsset: AVURLAsset, withSecondVideo secondAsset: AVURLAsset) {

// 1 - Create AVMutableComposition object. This object will hold your AVMutableCompositionTrack instances.
let mixComposition = AVMutableComposition()
// 2 - Create two video tracks
guard let firstTrack = mixComposition.addMutableTrack(withMediaType: .video,
preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
do {
try firstTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: firstAsset.duration),
of: firstAsset.tracks(withMediaType: .video)[0],
at: CMTime.zero)
} catch {
print("Failed to load first track")
return
}
guard let secondTrack = mixComposition.addMutableTrack(withMediaType: .video,
preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
do {
try secondTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: secondAsset.duration),
of: secondAsset.tracks(withMediaType: .video)[0],
at: CMTime.zero)
} catch {
print("Failed to load second track")
return
}
// 2.1
let mainInstruction = AVMutableVideoCompositionInstruction()
mainInstruction.timeRange = CMTimeRangeMake(start: CMTime.zero, duration: CMTimeAdd(firstAsset.duration, secondAsset.duration))
// 2.2
let firstInstruction = ViewController.videoCompositionInstruction(firstTrack, asset: firstAsset)
let scale = CGAffineTransform(scaleX: 0.3, y: 0.3)
let move = CGAffineTransform(translationX: 10, y: 10)
firstInstruction.setTransform(scale.concatenating(move), at: CMTime.zero)
let secondInstruction = ViewController.videoCompositionInstruction(secondTrack, asset: secondAsset)
// 2.3
mainInstruction.layerInstructions = [firstInstruction, secondInstruction]
let mainComposition = AVMutableVideoComposition()
mainComposition.instructions = [mainInstruction]
mainComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
let width = max(firstTrack.naturalSize.width, secondTrack.naturalSize.width)
let height = max(firstTrack.naturalSize.height, secondTrack.naturalSize.height)
mainComposition.renderSize = CGSize(width: width, height: height)
mainInstruction.backgroundColor = UIColor.clear.cgColor

// 4 - Get path
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .short
let date = dateFormatter.string(from: Date())
let url = documentDirectory.appendingPathComponent("mergeVideo-(date).mov")
// Check exists and remove old file
FileManager.default.removeItemIfExisted(url as URL)
// 5 - Create Exporter
guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
exporter.outputURL = url
exporter.outputFileType = AVFileType.mov
exporter.shouldOptimizeForNetworkUse = true
exporter.videoComposition = mainComposition

// 6 - Perform the Export
exporter.exportAsynchronously() {
DispatchQueue.main.async {
print("Movie complete")
self.myurl = url as URL
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url as URL)
}) { saved, error in
if saved {
print("Saved")
}
}
self.playVideo()
}
}
}

func playVideo() {
let player = AVPlayer(url: myurl!)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
print("playing...")
}


static func videoCompositionInstruction(_ track: AVCompositionTrack, asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction {
let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
let assetTrack = asset.tracks(withMediaType: AVMediaType.video)[0]
let transform = assetTrack.preferredTransform
let assetInfo = orientationFromTransform(transform)
var scaleToFitRatio = UIScreen.main.bounds.width / assetTrack.naturalSize.width
if assetInfo.isPortrait {
scaleToFitRatio = UIScreen.main.bounds.width / assetTrack.naturalSize.height
let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
instruction.setTransform(assetTrack.preferredTransform.concatenating(scaleFactor), at: CMTime.zero)
} else {
let scaleFactor = CGAffineTransform(scaleX: scaleToFitRatio, y: scaleToFitRatio)
var concat = assetTrack.preferredTransform.concatenating(scaleFactor)
.concatenating(CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.width / 2))
if assetInfo.orientation == .down {
let fixUpsideDown = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
let windowBounds = UIScreen.main.bounds
let yFix = assetTrack.naturalSize.height + windowBounds.height
let centerFix = CGAffineTransform(translationX: assetTrack.naturalSize.width, y: yFix)
concat = fixUpsideDown.concatenating(centerFix).concatenating(scaleFactor)
}
instruction.setTransform(concat, at: CMTime.zero)
}
return instruction
}
static func orientationFromTransform(_ transform: CGAffineTransform) -> (orientation: UIImage.Orientation, isPortrait: Bool) {
var assetOrientation = UIImage.Orientation.up
var isPortrait = false
if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
assetOrientation = .right
isPortrait = true
} else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
assetOrientation = .left
isPortrait = true
} else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
assetOrientation = .up
} else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
assetOrientation = .down
}
return (assetOrientation, isPortrait)
}

}

extension FileManager {
func removeItemIfExisted(_ url:URL) -> Void {
if FileManager.default.fileExists(atPath: url.path) {
do {
try FileManager.default.removeItem(atPath: url.path)
}
catch {
print("Failed to delete file")
}
}
}
}

最新更新