在最近的一个项目中,我必须使用AV Foundation单独访问我视频的所有帧。此外,如果可能的话,随机访问它们(如数组)
我试图研究这个问题,但我没有得到任何有用的东西。
注意:是否有任何有用的文档可以熟悉AV基金会?
答案 0 :(得分:3)
您可以使用AVAssetReader
按顺序枚举视频的帧,如下所示:
let asset = AVAsset(URL: inputUrl)
let reader = try! AVAssetReader(asset: asset)
let videoTrack = asset.tracksWithMediaType(AVMediaTypeVideo)[0]
// read video frames as BGRA
let trackReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings:[String(kCVPixelBufferPixelFormatTypeKey): NSNumber(unsignedInt: kCVPixelFormatType_32BGRA)])
reader.addOutput(trackReaderOutput)
reader.startReading()
while let sampleBuffer = trackReaderOutput.copyNextSampleBuffer() {
print("sample at time \(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))")
if let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
// process each CVPixelBufferRef here
// see CVPixelBufferGetWidth, CVPixelBufferLockBaseAddress, CVPixelBufferGetBaseAddress, etc
}
}
随机访问更复杂。您可以使用AVPlayer
+ AVPlayerItemVideoOutput
使用t
,as described in this answer随时获取copyPixelBufferForItemTime
的框架,但细微之处在于您如何选择t
{1}}。
如果您想以均匀的间隔对视频进行采样,那么这很简单,但如果您希望使用序列AVAssetReader
代码看到的相同帧/演示时间标记,那么您将可能必须使用 AVAssetReader
预处理文件,以构建帧编号 - >演示文稿时间戳地图。如果您使用nil
中的AVAssetReaderTrackOutput
输出设置跳过解码,这可能会很快。
答案 1 :(得分:0)
如果您需要在播放视频时获取当前帧,您可以为播放器添加一个观察者,并可以像这样获取当前帧:
var player: AVPlayer?
var playerController = AVPlayerViewController()
var videoFPS: Int = 0
var currentFrame: Int = 0
var totalFrames: Int?
func configureVideo() {
guard let videoURL = "" else { return }
player = AVPlayer(url: videoURL)
playerController.player = player
guard player?.currentItem?.asset != nil else {
return
}
let asset = self.player?.currentItem?.asset
let tracks = asset!.tracks(withMediaType: .video)
let fps = tracks.first?.nominalFrameRate
self.videoFPS = lround(Double(fps!))
self.getVideoData()
}
func getVideoData() {
self.player?.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1,timescale: Int32(self.videoFPS)), queue: DispatchQueue.main) {[weak self] (progressTime) in
if let duration = self!.player?.currentItem?.duration {
let durationSeconds = CMTimeGetSeconds(duration)
let seconds = CMTimeGetSeconds(progressTime)
if self!.totalFrames == nil {
self!.totalFrames = lround(Double(self!.videoFPS) * durationSeconds)
}
DispatchQueue.main.async {
if self!.totalFrames != self!.currentFrame {
self!.currentFrame = lround(seconds*Double(self!.videoFPS))
} else {
print("Video has ended!!!!!!!!")
}
print(self!.currentFrame)
}
}
}
}
获取当前帧数后,您可以使用 AVAssetImageGenerator
检索特定图像。