应用程序崩溃时使用AVAssetImageGenerator从视频中获取所有帧以获取长视频

时间:2016-11-15 12:33:21

标签: ios iphone swift

我已经成功从视频中提取了所有帧,并且它适用于较小的视频,但是当我尝试从视频中提取超过60秒的帧时,应用程序在设备上崩溃。

我从视频中每秒提取30帧。

以下是我写的代码: -

var  videoSec = Float64(0)

 func startImageConversion(){
        let filePath = NSURL.fileURLWithPath(self.savedVideoURL)
        videoSec = self.getVideoTime(filePath)
        videoImagesArray =  self.getImagesArrayFromVideo(filePath)
        print("Images Count \(videoImagesArray.count)")
    }

// MARK: - 视频编辑器功能

    func getImagesArrayFromVideo(filePath:NSURL) -> NSMutableArray
    {
        let imageArray = NSMutableArray()
        print("Video Sec is ",videoSec)
        let vidSec = Float64(videoSec)

        let theOpts = [
            AVURLAssetPreferPreciseDurationAndTimingKey : true,
            AVURLAssetReferenceRestrictionsKey : 0 // AVAssetReferenceRestrictions.RestrictionForbidNone
        ]
        let asset = AVURLAsset(URL: filePath, options: theOpts)
        let generator = AVAssetImageGenerator(asset: asset)
        generator.maximumSize = CGSize(width: Double(self.view.frame.size.width),
                                       height: Double(self.view.frame.size.height))

        generator.appliesPreferredTrackTransform = false
        generator.requestedTimeToleranceBefore = kCMTimeZero
        generator.requestedTimeToleranceAfter = kCMTimeZero
        let vid_length:CMTime = asset.duration
        let fps = vid_length.timescale
        print("Video Lenght :- \(vid_length) FPS is :- \(fps)")


        let mainValue = Float64(vid_length.value)

        let divide = vidSec*30
        let byVal = mainValue/divide


        for i in 0.stride(through: Float64(vid_length.value), by: byVal)
        {

            var image:CGImage!//UIImage()
            let divident = Float64(i)
//            let mileSec = Float64(divident / 1000)
//            print(Sec)
            image  = self.generateVideoThumbs(filePath, second: divident,
                                              thumbWidth: Double(self.view.frame.size.width),
                                              generator : generator,
                                              fps : fps
            )

            if image != nil {

                imageArray.addObject(image)

            }
        }

        print("value of Array is ",imageArray.count)

        return imageArray

    }



 private func getVideoTime(url: NSURL) -> Float64
    {
        let videoTime = AVURLAsset(URL: url, options: nil)
        print("videoTime.preferredRate = \(videoTime.preferredRate)")
        return CMTimeGetSeconds(videoTime.duration)
    }



 private func generateVideoThumbs(url: NSURL, second: Float64, thumbWidth: Double, generator:AVAssetImageGenerator, fps: CMTimeScale) -> CGImage! {
        let thumbTime = CMTimeMake(Int64(second), fps)
        var actualTime : CMTime = CMTimeMake(0, 0)
        print("thumbTime - \(thumbTime)")
        do {
            let ref = try generator.copyCGImageAtTime(thumbTime, actualTime: &actualTime)
            print("actualTime - \(actualTime)")
            return ref//UIImage(CGImage: ref)
        }catch {
            print(error)
            return nil
        }
     }

如果视频小于60秒左右,一切正常。 它也可以在模拟器上正常工作,但在没有任何警告或错误的情况下断开设备。 enter image description here 任何帮助将不胜感激,谢谢

3 个答案:

答案 0 :(得分:2)

autoreleasepool{
if image != nil {
var newImage:UIImage = UIImage(CGImage: cgImage)
                imageArray.addObject(image)
            }
}

如上面的答案潜水中提到的那样是记忆问题。 只需使用此代码。我测试了它,它工作正常。

答案 1 :(得分:1)

Core Graphics不支持Objective C中的自动释放池,但是Swift ARC可以处理CF类型,无论如何,在某些情况下你仍然会遇到CGImage发布的问题,也许就是这种情况,你显然有问题使用imageArray的大小,我确定您的设备因out of memory SIGTERM而断开连接。 iOS模拟器在现代15英寸开发MacBook上使用共享内存,因此,一切都将在那里工作。

我建议您重写generateVideoThumbs(...)函数并使用UIImage作为返回值(您已经尝试过,因为我可以看到,只需使用UIImage返回您的解决方案):< / p>

private func generateVideoThumbs(url: NSURL, second: Float64, thumbWidth: Double, generator:AVAssetImageGenerator, fps: CMTimeScale) -> UIImage! {
        let thumbTime = CMTimeMake(Int64(second), fps)
        var actualTime : CMTime = CMTimeMake(0, 0)
        print("thumbTime - \(thumbTime)")
        do {
            let ref = try generator.copyCGImageAtTime(thumbTime, actualTime: &actualTime)
            let resultImage = UIImage.init(ref)
            print("actualTime - \(actualTime)")
            return resultImage
        }catch {
            print(error)
            return nil
        }
     }

其次,我建议您将图像保存到磁盘,并仅存储imageArray中的链接。像这样:

if image != nil {
    let data = UIImagePNGRepresentation(image)
    let filename = NSTemporaryDirectory().appendingPathComponent("\(i).png")
    try? data.write(to: filename)
    imageArray.addObject(filename)
}

然后,您可以从网址中提取图片,并执行您计划使用的任何内容。它允许您以任何您想要的方式避免内存压力并使用您的阵列(复制,转发到另一个类等)。

  

P.S。我编写的代码没有编译,但我认为这个想法很清楚。

答案 2 :(得分:1)

这是一个记忆问题。 copeCGImageAtTime在主线程中是同步的,如果你将它放入for循环就会出现问题。

使用- (void)generateCGImagesAsynchronouslyForTimes:(NSArray<NSValue *> *)requestedTimes completionHandler:(AVAssetImageGeneratorCompletionHandler)handler;

而不是- (nullable CGImageRef)copyCGImageAtTime:(CMTime)requestedTime actualTime:(nullable CMTime *)actualTime error:(NSError * _Nullable * _Nullable)outError CF_RETURNS_RETAINED;

再试一次。

度过愉快的一天。