使用Swift从视频中获取帧

9

我想在特定时间从视频中抓取帧。 我正在调用我的抓帧函数,并使用 Float64 指定的秒数作为时间。 问题是它没有抓取当前帧。 它似乎忽略了小数部分。 如果我用1.22和1.70调用该函数,它将返回相同的帧。 当涉及 Swift 时,我还很新,所以我猜我没有正确理解 CMTime 对象。 那么有人能看出问题在哪里吗?

func generateThumnail(url : NSURL, fromTime:Float64) -> UIImage {
    var asset :AVAsset = AVAsset.assetWithURL(url) as! AVAsset
    var assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
    assetImgGenerate.appliesPreferredTrackTransform = true
    var error       : NSError? = nil
    var time        : CMTime = CMTimeMakeWithSeconds(fromTime, 600)        
    var img         : CGImageRef = assetImgGenerate.copyCGImageAtTime(time, actualTime: nil, error: &error)
    var frameImg    : UIImage = UIImage(CGImage: img)!
    return frameImg
}

var grabTime = 1.22
img = generateThumnail(urlVideo, fromTime: Float64(grabTime)) 

你的代码在我的iOS Playground中运行正常:截图1截图2,所以你的问题不是因为对CMTime的误解。 - Eric Aya
谢谢您的尝试。我不知道我做错了什么。我想抓取一个4秒长的剪辑中的所有帧并将它们存储在一个数组中。但是大多数帧看起来都一样。:/也许这与视频的长度有关。 - arpo
我在某个地方读到600对于视频来说很好。我会尝试进行实验。也许FPS是一个不错的值? - arpo
1
这里有一个提示:不要将nil传递给actualTime,而是准备var actual: CMTime = CMTimeMake(0, 0)并像这样传递它:var img = assetImgGenerate.copyCGImageAtTime(time, actualTime: &actual, error: &error),然后使用以下内容检查:let inf = CMTimeCopyAsDictionary(actual, kCFAllocatorDefault) println(inf)。该字典包含用于抓取的实际属性,对于调试和查找良好的时间刻度非常有用。 - Eric Aya
1
这里有一篇有趣的帖子:https://dev59.com/o4nca4cB1Zd3GeqP9leU,提供了更详细的信息。 - Eric Aya
显示剩余3条评论
3个回答

15

感谢 @eric-d 发现了这篇文章:iOS拍摄多个屏幕截图

我设法发现添加以下内容:

    assetImgGenerate.requestedTimeToleranceAfter = kCMTimeZero;
    assetImgGenerate.requestedTimeToleranceBefore = kCMTimeZero;

传递给我的函数将完成这个技巧。

我的更新后的函数如下所示:

func generateThumnail(url : NSURL, fromTime:Float64) -> UIImage {
    var asset :AVAsset = AVAsset.assetWithURL(url) as! AVAsset
    var assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
    assetImgGenerate.appliesPreferredTrackTransform = true
    assetImgGenerate.requestedTimeToleranceAfter = kCMTimeZero;
    assetImgGenerate.requestedTimeToleranceBefore = kCMTimeZero;
    var error       : NSError? = nil
    var time        : CMTime = CMTimeMakeWithSeconds(fromTime, 600)        
    var img         : CGImageRef = assetImgGenerate.copyCGImageAtTime(time, actualTime: nil, error: &error)
    var frameImg    : UIImage = UIImage(CGImage: img)!
    return frameImg
}

var grabTime = 1.22
img = generateThumnail(urlVideo, fromTime: Float64(grabTime))

2
需要更新以适应较新版本的Swift: do { let img = try assetImgGenerate.copyCGImageAtTime(time, actualTime: nil) return UIImage(CGImage: img) } catch let error as NSError { print("图像生成失败,错误信息:(error)") return nil } - Pichirichi
嗯...花了2个小时才发现...这应该是默认设置,或者有什么原因不使用默认设置吗? - Lukas

4

针对 Swift 4.2

fileprivate func generateThumnail(url : URL, fromTime:Float64) -> UIImage? {
    let asset :AVAsset = AVAsset(url: url)
    let assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
    assetImgGenerate.appliesPreferredTrackTransform = true
    assetImgGenerate.requestedTimeToleranceAfter = CMTime.zero;
    assetImgGenerate.requestedTimeToleranceBefore = CMTime.zero;
    let time : CMTime = CMTimeMakeWithSeconds(fromTime, preferredTimescale: 600)
    if let img = try? assetImgGenerate.copyCGImage(at:time, actualTime: nil) {
        return UIImage(cgImage: img)
    } else {
        return nil
    }
}

2

我将arpo的答案应用到了我的项目中,并进行了Swift 3的更新:

fileprivate func generateThumnail(url : URL, fromTime:Float64) -> UIImage? {
    let asset :AVAsset = AVAsset(url: url)
    let assetImgGenerate : AVAssetImageGenerator = AVAssetImageGenerator(asset: asset)
    assetImgGenerate.appliesPreferredTrackTransform = true
    assetImgGenerate.requestedTimeToleranceAfter = kCMTimeZero;
    assetImgGenerate.requestedTimeToleranceBefore = kCMTimeZero;
    let time        : CMTime = CMTimeMakeWithSeconds(fromTime, 600)
    if let img = try? assetImgGenerate.copyCGImage(at:time, actualTime: nil) {
        return UIImage(cgImage: img!)
    } else {
        return nil
    }
}

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