Ios 如何从两个视频和一个包围帧中的imageView中创建一个新视频?
我有一个父视图,其中包含两个带有AVPlayerLayers的视图和一个UIImageView。我想将所有这些合并到一个新的视频中,以捕获父视图的所有内容 我查看了ReplayKit,但它没有捕获AVPlayer的内容;它不能让我访问视频;它捕获整个屏幕,而不是特定的视图或帧 我的一般方法是逐帧迭代视频,捕获帧的图像,将它们设置在我覆盖在playerLayer上的imageView中,然后使用Ios 如何从两个视频和一个包围帧中的imageView中创建一个新视频?,ios,swift,avfoundation,Ios,Swift,Avfoundation,我有一个父视图,其中包含两个带有AVPlayerLayers的视图和一个UIImageView。我想将所有这些合并到一个新的视频中,以捕获父视图的所有内容 我查看了ReplayKit,但它没有捕获AVPlayer的内容;它不能让我访问视频;它捕获整个屏幕,而不是特定的视图或帧 我的一般方法是逐帧迭代视频,捕获帧的图像,将它们设置在我覆盖在playerLayer上的imageView中,然后使用UIGraphicsGetImageFromCurrentImageContext捕获父视图的图像,然后
UIGraphicsGetImageFromCurrentImageContext
捕获父视图的图像,然后将所有这些图像制作成视频
我尝试了一些AVFoundation选项,但总体而言,它们的性能不是很好。下面是我尝试过的一些选项,总是尝试上面的模式
videoPlayer.seek(to:frame)
设置视频帧,但这种方法非常慢:以这种方式循环每15秒视频大约需要42秒AVAssetImageGenerator异步获取所有视频帧。同步生成框架,然后按上述模式迭代。这是非常内存密集,因为我有一个图像的每一帧的两个视频。我可以将工作分块以避免内存崩溃,但总的来说,这种方法仍然相当慢,并且具有这种批处理复杂性,实际上并不比第一种方法好多少
AVAssetImageGenerator.copyCGImage(at:frame,actualTime:nil)
同时获取每个帧,但这并不比第一个选项快avassetrader
并使用copyNextSampleBuffer
在每一帧中进行迭代——与上述任何选项相比,都没有真正的改进在这一点上,我想我可能不得不使用金属。有什么建议吗?这会很困难,因为您要做的是添加一个钩子,通过从视图实时捕获原始图像缓冲区流,获取已渲染的GPU的合成结果。这在CPU/GPU或内存使用方面很难有效。如果您能够以某种方式访问GPU的本机API,以更直接的方式获得合成的原始缓冲区,您可能会成功,但这很尴尬 更自然的方法是——在获得所需材料(视频、图像等)后立即进行适当的视频合成,然后将处理结果显示给用户,并根据需要进行操作(将数据转储为文件等)
简而言之,试试谷歌“ios视频合成”。AFAIK,AVFoundation提供相关功能。您可能还想查看一些库,以减少自己编写所有低级代码的麻烦。我采用了一种不同的方法,似乎可以达到目的。您可以在本文中检查工作版本,但大部分代码如下所示。代码不适合生产/非常干净,仅仅是概念证明——因此使用了
代码>、长函数、重复等
func overlapVideos() {
let composition = AVMutableComposition()
// make main video instruction
let mainInstruction = AVMutableVideoCompositionInstruction()
guard let pathUrl = Bundle.main.url(forResource: "IMG_7165", withExtension: "MOV") else {
assertionFailure()
return
}
// make first video track and add to composition
let firstAsset = AVAsset(url: pathUrl)
// timeframe will match first video for this example
mainInstruction.timeRange = CMTimeRangeMake(start: .zero, duration: firstAsset.duration)
guard let firstTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else {
assertionFailure()
return
}
try! firstTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: firstAsset.duration), of: firstAsset.tracks(withMediaType: .video)[0], at: .zero)
// add layer instruction for first video
let firstVideoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: firstTrack)
let firstMove = CGAffineTransform(translationX: 500, y: 400)
let firstScale = CGAffineTransform(scaleX: 0.1, y: 0.1)
firstVideoLayerInstruction.setTransform(firstMove.concatenating(firstScale), at: .zero)
mainInstruction.layerInstructions.append(firstVideoLayerInstruction)
// make second video track and add to composition
let secondAsset = AVAsset(url: pathUrl)
guard let secondTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else {
assertionFailure()
return
}
try! secondTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: secondAsset.duration), of: secondAsset.tracks(withMediaType: .video)[0], at: .zero)
// add layer instruction for second video
let secondVideoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: secondTrack)
let secondMove = CGAffineTransform(translationX: -100, y: -100)
let secondScale = CGAffineTransform(scaleX: 0.1, y: 0.1)
secondVideoLayerInstruction.setTransform(secondMove.concatenating(secondScale), at: .zero)
mainInstruction.layerInstructions.append(secondVideoLayerInstruction)
// make third video track and add to composition
let thirdAsset = AVAsset(url: pathUrl)
guard let thirdTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else {
assertionFailure()
return
}
try! thirdTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: thirdAsset.duration), of: thirdAsset.tracks(withMediaType: .video)[0], at: .zero)
// add layer instruction for third video
let thirdVideoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: thirdTrack)
let thirdMove = CGAffineTransform(translationX: 0, y: 1000)
let thirdScale = CGAffineTransform(scaleX: 0.1, y: 0.1)
thirdVideoLayerInstruction.setTransform(thirdMove.concatenating(thirdScale), at: .zero)
mainInstruction.layerInstructions.append(thirdVideoLayerInstruction)
// make video composition
let videoComposition = AVMutableVideoComposition()
videoComposition.instructions = [mainInstruction]
videoComposition.frameDuration = CMTimeMake(value: 1, timescale: 30)
videoComposition.renderSize = CGSize(width: 640, height: 480)
// export
let searchPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = searchPaths[0]
let filePath = documentDirectory.appending("output.mov")
let outputUrl = URL(fileURLWithPath: filePath)
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
try! fileManager.removeItem(at: outputUrl)
}
guard let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {
assertionFailure()
return
}
exporter.videoComposition = videoComposition
exporter.outputFileType = .mov
exporter.outputURL = outputUrl
exporter.exportAsynchronously {
DispatchQueue.main.async { [weak self] in
// play video, etc.
}
}
}
也许我不理解这个问题,但我会认为这对于AVMutableVideoComposition和AVAssetExportSession来说是非常微不足道的。这允许您指定层来定位视频和图像,然后将整个内容作为单个视频输出。看来这很可能会奏效。这将需要我将父视图的视图布局映射到整个视频的视图布局,这是可行的…只是有点烦人。