【问题标题】:App Crashes while getting all the Frames from a video using AVAssetImageGenerator for long videos使用 AVAssetImageGenerator 获取长视频的视频中的所有帧时应用程序崩溃
【发布时间】:2017-03-29 08:12:02
【问题描述】:

我已成功从视频中提取所有帧,并且对于较小的视频效果很好,但是当我尝试从超过 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 秒左右,一切正常。 它在模拟器上也能正常工作,但在没有任何警告或错误的情况下断开设备。 任何帮助将不胜感激,谢谢

【问题讨论】:

  • 控制台中没有内存警告吗?这可能是整个问题,因为它似乎适用于消耗更少内存的视频。如果是,请尝试使用 Instruments Memory Leaks、Profiling、Allocations 等,尝试找出可能有罪的部分并尝试为其找到解决方案。
  • @Larme 控制台中没有警告,它只是断开了设备。我也尝试了一些仪器来解决内存泄漏,但没有运气。
  • 这可能是内存问题,如果您的应用程序消耗的内存超过 450 mb,那么请在调试代码时查看 Xcode 中的内存计。
  • @MDavid 它正在使用过多的内存,但解决方案是什么。我怎样才能确保它不超过 450mb 但仍然有效。
  • image = self.generateVideoThumbs(filePath, second: divident, thumbWidth: Double(self.view.frame.size.width), generator : generator, fps : fps cache is hold by UIImage 可以改变它到 UIImage 方法 imagewithcontentsofile 我想试试这个一次

标签: ios iphone swift


【解决方案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;

再试一次。

祝你有美好的一天。

【讨论】:

    【解决方案2】:
    autoreleasepool{
    if image != nil {
    var newImage:UIImage = UIImage(CGImage: cgImage)
                    imageArray.addObject(image)
                }
    }
    

    在上面的答案中,潜水提到了它的内存问题。 只需使用此代码。我已经测试过了,效果很好。

    【讨论】:

    • 这解决了在循环中分配图像的问题,即使设置类似 image: UIImage? = .... 之后,image=nil,内存不会在循环内释放,但 autoreleasepool 解决了这个问题。
    【解决方案3】:

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

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

    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)
    }
    

    然后,您可以从 URL 中提取您的图像,并执行您计划对它们执行的任何操作。它允许您避免内存压力并以任何您想要的方式使用您的数组(复制、转发到另一个类等)。

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

    【讨论】:

      猜你喜欢
      • 2012-06-18
      • 1970-01-01
      • 1970-01-01
      • 2022-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      相关资源
      最近更新 更多