You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求编写PHAsset扩展:从视频PHAsset提取指定时间帧

PHAsset 获取视频帧 Swift 扩展

以下是实现从视频类型PHAsset中提取指定时间帧的Swift扩展,包含异步推荐方案和同步备选方案:

import Photos
import AVFoundation
import UIKit

extension PHAsset {
    /// 异步获取视频指定时间点的帧(推荐使用,避免阻塞主线程)
    /// - Parameters:
    ///   - second: 目标时间点(单位:秒)
    ///   - completion: 结果回调,返回UIImage或nil
    func extractVideoFrame(at second: Float, completion: @escaping (UIImage?) -> Void) {
        guard mediaType == .video else {
            completion(nil)
            return
        }
        
        let requestOptions = PHVideoRequestOptions()
        requestOptions.isNetworkAccessAllowed = true // 允许从iCloud拉取视频
        
        PHImageManager.default().requestAVAsset(forVideo: self, options: requestOptions) { asset, _, _ in
            guard let urlAsset = asset as? AVURLAsset else {
                completion(nil)
                return
            }
            
            let imageGenerator = AVAssetImageGenerator(asset: urlAsset)
            imageGenerator.appliesPreferredTrackTransform = true // 保持视频原始方向
            let targetTime = CMTimeMakeWithSeconds(Double(second), preferredTimescale: 600)
            
            do {
                let cgImage = try imageGenerator.copyCGImage(at: targetTime, actualTime: nil)
                completion(UIImage(cgImage: cgImage))
            } catch {
                print("提取视频帧失败: \(error.localizedDescription)")
                completion(nil)
            }
        }
    }
    
    /// 同步获取视频帧(仅建议在后台线程使用,会阻塞当前线程)
    /// - Parameter second: 目标时间点(单位:秒)
    /// - Returns: 提取到的UIImage或nil
    func extractVideoFrameSync(at second: Float) -> UIImage? {
        guard mediaType == .video else { return nil }
        
        let semaphore = DispatchSemaphore(value: 0)
        var resultImage: UIImage?
        
        extractVideoFrame(at: second) { image in
            resultImage = image
            semaphore.signal()
        }
        
        semaphore.wait()
        return resultImage
    }
}

使用示例

异步调用(推荐)

// 假设你已经获取到目标PHAsset实例
let videoAsset: PHAsset = // 你的视频资源

videoAsset.extractVideoFrame(at: 2.0) { frameImage in
    DispatchQueue.main.async {
        // 在主线程更新UI
        self.previewImageView.image = frameImage
    }
}

同步调用(后台线程使用)

DispatchQueue.global(qos: .userInitiated).async {
    let frameImage = videoAsset.extractVideoFrameSync(at: 2.0)
    DispatchQueue.main.async {
        self.previewImageView.image = frameImage
    }
}

关键说明

  • isNetworkAccessAllowed:开启后可以访问存储在iCloud中的视频资源
  • appliesPreferredTrackTransform:确保生成的图片方向与视频原始方向一致,避免出现旋转错位
  • 异步方法不会阻塞主线程,适合UI相关操作;同步方法仅适合后台任务,主线程调用会导致UI卡顿
  • 需要确保已获取相册访问权限,否则无法正常读取PHAsset

内容的提问来源于stack exchange,提问作者Sanzhar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:05:26