使用 AV 播放器访问单个帧
Accesing Individual Frames using AV Player
在最近的项目中,我必须使用 AV Foundation 单独访问视频的所有帧。此外,如果可能的话随机访问它们(如数组)
我试图研究这个问题,但没有得到任何有用的信息。
注意:是否有任何有助于熟悉 AV Foundation 的文档?
您可以使用 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
获取帧,使用 copyPixelBufferForItemTime
、,但微妙之处在于您如何选择 t
=14=].
如果你想以统一的间隔对视频进行采样,那很容易,但如果你想在序列 AVAssetReader
代码看到的相同 frames/presentation 时间戳上着陆,那么你将可能必须用 和 AVAssetReader
预处理文件,以构建帧编号 -> 表示时间戳图。如果您使用 AVAssetReaderTrackOutput
.
中的 nil
输出设置跳过解码,这会很快
如果您需要在播放视频时获取当前帧,您可以为播放器添加一个观察者,并可以像这样获取当前帧:
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
检索特定图像。
在最近的项目中,我必须使用 AV Foundation 单独访问视频的所有帧。此外,如果可能的话随机访问它们(如数组)
我试图研究这个问题,但没有得到任何有用的信息。
注意:是否有任何有助于熟悉 AV Foundation 的文档?
您可以使用 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
获取帧,使用 copyPixelBufferForItemTime
、t
=14=].
如果你想以统一的间隔对视频进行采样,那很容易,但如果你想在序列 AVAssetReader
代码看到的相同 frames/presentation 时间戳上着陆,那么你将可能必须用 和 AVAssetReader
预处理文件,以构建帧编号 -> 表示时间戳图。如果您使用 AVAssetReaderTrackOutput
.
nil
输出设置跳过解码,这会很快
如果您需要在播放视频时获取当前帧,您可以为播放器添加一个观察者,并可以像这样获取当前帧:
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
检索特定图像。