一尘不染

如何使用AVAssetWriter快速整理视频?

swift

我目前正在制作一个小型应用程序,该程序可以使Mac上的网络摄像头延时播放,将捕获的帧保存为png,并且正在考虑将捕获的帧导出为单个视频。

我使用CGImage处理原始图像,并将它们设置在数组中,但是我不确定从那里开始。根据我自己的研究,我不得不以某种方式使用AVAssetWriter和AVAssetWriterInput。

我在这里浏览了一下,阅读了苹果文档并搜索了谷歌。但是所有指南等都在obj-c中,而不是快捷的,这使得它真的很难理解(因为我没有Obj-C的经验)。

任何帮助将不胜感激。

非常感谢,卢克。


阅读 147

收藏
2020-07-07

共1个答案

一尘不染

我在Swift中解决了同样的问题。从UIImage数组开始,尝试一下(有点长:-),但是可以):

var choosenPhotos: [UIImage] = [] *** your array of UIImages ***
var outputSize = CGSizeMake(1280, 720)

func build(outputSize outputSize: CGSize) {
    let fileManager = NSFileManager.defaultManager()
    let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    guard let documentDirectory: NSURL = urls.first else {
        fatalError("documentDir Error")
    }

    let videoOutputURL = documentDirectory.URLByAppendingPathComponent("OutputVideo.mp4")

    if NSFileManager.defaultManager().fileExistsAtPath(videoOutputURL.path!) {
        do {
            try NSFileManager.defaultManager().removeItemAtPath(videoOutputURL.path!)
        } catch {
            fatalError("Unable to delete file: \(error) : \(__FUNCTION__).")
        }
    }

    guard let videoWriter = try? AVAssetWriter(URL: videoOutputURL, fileType: AVFileTypeMPEG4) else {
        fatalError("AVAssetWriter error")
    }

    let outputSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : NSNumber(float: Float(outputSize.width)), AVVideoHeightKey : NSNumber(float: Float(outputSize.height))]

    guard videoWriter.canApplyOutputSettings(outputSettings, forMediaType: AVMediaTypeVideo) else {
        fatalError("Negative : Can't apply the Output settings...")
    }

    let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: outputSettings)
    let sourcePixelBufferAttributesDictionary = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(unsignedInt: kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String: NSNumber(float: Float(outputSize.width)), kCVPixelBufferHeightKey as String: NSNumber(float: Float(outputSize.height))]
    let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)

    if videoWriter.canAddInput(videoWriterInput) {
        videoWriter.addInput(videoWriterInput)
    }

    if videoWriter.startWriting() {
        videoWriter.startSessionAtSourceTime(kCMTimeZero)
        assert(pixelBufferAdaptor.pixelBufferPool != nil)

        let media_queue = dispatch_queue_create("mediaInputQueue", nil)

        videoWriterInput.requestMediaDataWhenReadyOnQueue(media_queue, usingBlock: { () -> Void in
            let fps: Int32 = 1
            let frameDuration = CMTimeMake(1, fps)

            var frameCount: Int64 = 0
            var appendSucceeded = true

            while (!self.choosenPhotos.isEmpty) {
                if (videoWriterInput.readyForMoreMediaData) {
                    let nextPhoto = self.choosenPhotos.removeAtIndex(0)
                    let lastFrameTime = CMTimeMake(frameCount, fps)
                    let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)

                    var pixelBuffer: CVPixelBuffer? = nil
                    let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferAdaptor.pixelBufferPool!, &pixelBuffer)

                    if let pixelBuffer = pixelBuffer where status == 0 {
                        let managedPixelBuffer = pixelBuffer

                        CVPixelBufferLockBaseAddress(managedPixelBuffer, 0)

                        let data = CVPixelBufferGetBaseAddress(managedPixelBuffer)
                        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
                        let context = CGBitmapContextCreate(data, Int(self.outputSize.width), Int(self.outputSize.height), 8, CVPixelBufferGetBytesPerRow(managedPixelBuffer), rgbColorSpace, CGImageAlphaInfo.PremultipliedFirst.rawValue)

                        CGContextClearRect(context, CGRectMake(0, 0, CGFloat(self.outputSize.width), CGFloat(self.outputSize.height)))

                        let horizontalRatio = CGFloat(self.outputSize.width) / nextPhoto.size.width
                        let verticalRatio = CGFloat(self.outputSize.height) / nextPhoto.size.height
                        //aspectRatio = max(horizontalRatio, verticalRatio) // ScaleAspectFill
                        let aspectRatio = min(horizontalRatio, verticalRatio) // ScaleAspectFit

                        let newSize:CGSize = CGSizeMake(nextPhoto.size.width * aspectRatio, nextPhoto.size.height * aspectRatio)

                        let x = newSize.width < self.outputSize.width ? (self.outputSize.width - newSize.width) / 2 : 0
                        let y = newSize.height < self.outputSize.height ? (self.outputSize.height - newSize.height) / 2 : 0

                        CGContextDrawImage(context, CGRectMake(x, y, newSize.width, newSize.height), nextPhoto.CGImage)

                        CVPixelBufferUnlockBaseAddress(managedPixelBuffer, 0)

                        appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime)
                    } else {
                        print("Failed to allocate pixel buffer")
                        appendSucceeded = false
                    }
                }
                if !appendSucceeded {
                    break
                }
                frameCount++
            }
            videoWriterInput.markAsFinished()
            videoWriter.finishWritingWithCompletionHandler { () -> Void in
                print("FINISHED!!!!!")
            }
        })
    }
}
2020-07-07