Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ajax/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 如何从两个视频和一个包围帧中的imageView中创建一个新视频?_Ios_Swift_Avfoundation - Fatal编程技术网

Ios 如何从两个视频和一个包围帧中的imageView中创建一个新视频?

Ios 如何从两个视频和一个包围帧中的imageView中创建一个新视频?,ios,swift,avfoundation,Ios,Swift,Avfoundation,我有一个父视图,其中包含两个带有AVPlayerLayers的视图和一个UIImageView。我想将所有这些合并到一个新的视频中,以捕获父视图的所有内容 我查看了ReplayKit,但它没有捕获AVPlayer的内容;它不能让我访问视频;它捕获整个屏幕,而不是特定的视图或帧 我的一般方法是逐帧迭代视频,捕获帧的图像,将它们设置在我覆盖在playerLayer上的imageView中,然后使用UIGraphicsGetImageFromCurrentImageContext捕获父视图的图像,然后

我有一个父视图,其中包含两个带有AVPlayerLayers的视图和一个UIImageView。我想将所有这些合并到一个新的视频中,以捕获父视图的所有内容

我查看了ReplayKit,但它没有捕获AVPlayer的内容;它不能让我访问视频;它捕获整个屏幕,而不是特定的视图或帧

我的一般方法是逐帧迭代视频,捕获帧的图像,将它们设置在我覆盖在playerLayer上的imageView中,然后使用
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来说是非常微不足道的。这允许您指定层来定位视频和图像,然后将整个内容作为单个视频输出。看来这很可能会奏效。这将需要我将父视图的视图布局映射到整个视频的视图布局,这是可行的…只是有点烦人。