Swift - 从视频中获取所有帧

8

我正在遵循这段代码来获取视频中的所有帧。在此链接中,他试图在特定时间获取一帧。但是我需要获取所有帧。以下是我的代码...

var mutableVideoURL = NSURL()
var videoFrames = [UIImage]()

let asset : AVAsset = AVAsset(url: self.mutableVideoURL as URL)
let mutableVideoDuration = CMTimeGetSeconds(asset.duration)
print("-----Mutable video duration = \(mutableVideoDuration)")
let mutableVideoDurationIntValue = Int(mutableVideoDuration)
print("-----Int value of mutable video duration = \(mutableVideoDurationIntValue)")

for index in 0..<mutableVideoDurationIntValue {
   self.generateFrames(url: self.mutableVideoURL, fromTime: Float64(index))
}

    func generateFrames(url : NSURL, fromTime:Float64) {
        let asset: AVAsset = AVAsset(url: url as URL)
        let assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
        assetImgGenerate.appliesPreferredTrackTransform = true
        let time        : CMTime = CMTimeMakeWithSeconds(fromTime, 600)
        var img: CGImage?
        do {
            img = try assetImgGenerate.copyCGImage(at:time, actualTime: nil)
        } catch {
        }
        if img != nil {
            let frameImg: UIImage = UIImage(cgImage: img!)
            UIImageWriteToSavedPhotosAlbum(frameImg, nil, nil, nil)//I saved here to check
            videoFrames.append(frameImg)
            print("-----Array of video frames *** \(videoFrames)")
        } else {
              print("error !!!")
        }
    }

我用两个视频测试了这段代码(视频长度分别为5秒和3.45分钟)。这段代码对于短时长的视频(5秒)和长时长的视频(3.45分钟)都能完美运行,但是NSLog显示调试器消息:由于内存问题终止。任何帮助将不胜感激。


什么错误?你可以在catch中放置print(error.localizedDescription) - Willjay
调试器消息:由于内存问题而终止 :( - pigeon_39
那意味着内存中有太多帧,导致它崩溃。实质上,您需要以更少的内存消耗方式处理和循环遍历图像。 - Harish
1个回答

19

当需要生成多个帧时,苹果建议使用以下方法:generateCGImagesAsynchronously(forTimes:completionHandler:)

如果您仍然想按照当前的方法操作,有几个改进措施可以减少内存使用:

  • 您正在循环内部实例化AVAssetAVAssetImageGenerator,您可以只实例化一次并将其发送到generateFrames方法中。
  • 移除以下这行代码:

    UIImageWriteToSavedPhotosAlbum(frameImg, nil, nil, nil)//I saved here to check

    因为您正在将每个帧都保存到照片相册中,这会占用额外的内存。

最终结果可能如下所示:

var videoFrames:[UIImage] = [UIImage]
let asset:AVAsset = AVAsset(url:self.mutableVideoURL as URL)
let assetImgGenerate:AVAssetImageGenerator = AVAssetImageGenerator(asset:asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let duration:Float64 = CMTimeGetSeconds(asset.duration)
let durationInt:Int = Int(mutableVideoDuration)

for index:Int in 0 ..< durationInt
{
   generateFrames(
      assetImgGenerate:assetImgGenerate,
      fromTime:Float64(index))
}

func generateFrames(
   assetImgGenerate:AVAssetImageGenerator,
   fromTime:Float64)
{
   let time:CMTime = CMTimeMakeWithSeconds(fromTime, 600)
   let cgImage:CGImage?

   do
   {
      cgImage = try assetImgGenerate.copyCGImage(at:time, actualTime:nil)
   }
   catch
   {
      cgImage = nil
   }

   guard

      let img:CGImage = cgImage

   else
   {
       continue
   }

   let frameImg:UIImage = UIImage(cgImage:img)
   videoFrames.append(frameImg)
}

Swift 4.2更新

var videoUrl:URL // use your own url
var frames:[UIImage]
private var generator:AVAssetImageGenerator!

func getAllFrames() {
   let asset:AVAsset = AVAsset(url:self.videoUrl)
   let duration:Float64 = CMTimeGetSeconds(asset.duration)
   self.generator = AVAssetImageGenerator(asset:asset)
   self.generator.appliesPreferredTrackTransform = true
   self.frames = []
   for index:Int in 0 ..< Int(duration) {
      self.getFrame(fromTime:Float64(index))
   }
   self.generator = nil
}

private func getFrame(fromTime:Float64) {
    let time:CMTime = CMTimeMakeWithSeconds(fromTime, preferredTimescale:600)
    let image:CGImage
    do {
       try image = self.generator.copyCGImage(at:time, actualTime:nil)
    } catch {
       return
    }
    self.frames.append(UIImage(cgImage:image))
}

1
@BijenderSinghShekhawat,我更新了代码。希望对你有所帮助。 - vicegax
2
当我将大量图像存储在数组中时,应用程序会崩溃。 - pushpank
@pushpank 如果内存不足,可能会导致崩溃。您能展示崩溃日志或控制台中的消息吗? - vicegax
很可能是因为 fromTime 导致了崩溃。例如,getFrame(2) 不会返回 2 秒处的图像。 它将返回 2/600 处的图像,即 0.003 秒处的图像。 这是需要返回的大量图像。 - Lxrd-AJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接