[英]Accesing Individual Frames using AV Player
In a recent project, I have to access all frames of my video individually using AV Foundation.在最近的一个项目中,我必须使用 AV Foundation 单独访问我的视频的所有帧。 Also, if possible to acess them randomly (like an array)
此外,如果可能的话,随机访问它们(如数组)
I tried to research the question but I didn't get anything useful.我试图研究这个问题,但没有得到任何有用的信息。
Note: Is there any useful documentation to get familiar with the AV Foundation ?注意:是否有任何有用的文档来熟悉 AV Foundation ?
You can enumerate the frames of your video serially using AVAssetReader
, like this:您可以使用
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
}
}
Random access is more complicated.随机访问比较复杂。 You could use an
AVPlayer
+ AVPlayerItemVideoOutput
to get frames from any time t
, using copyPixelBufferForItemTime
, as described in this answer , but the subtlety lies in how you choose that t
.您可以使用
AVPlayer
+ AVPlayerItemVideoOutput
从任何时间t
获取帧,使用copyPixelBufferForItemTime
,如本答案所述,但微妙之处在于您如何选择t
。
If you want to sample the video at uniform intervals, then that's easy, but if you want to land on the same frames/presentation time stamps that the serial AVAssetReader
code sees, then you will probably have to preprocess the file with AVAssetReader
, to build a frame number -> presentation timestamp map.如果您想以统一的间隔对视频进行采样,那么这很容易,但是如果您想获得串行
AVAssetReader
代码看到的相同帧/演示时间戳,那么您可能必须使用AVAssetReader
预处理文件,以构建帧号 -> 演示时间戳图。 This can be fast if you skip decoding by using nil
output settings in AVAssetReaderTrackOutput
.如果您在
AVAssetReaderTrackOutput
使用nil
输出设置跳过解码,这会很快。
If you need to get the current frame while playing the video you could add an observer for the player and could get the current frame like this:如果您需要在播放视频时获取当前帧,您可以为播放器添加一个观察者,并可以像这样获取当前帧:
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)
}
}
}
}
After getting the current frame number, you could use an AVAssetImageGenerator
to retrieve the specific image.获取当前帧号后,您可以使用
AVAssetImageGenerator
来检索特定图像。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.