使用AV播放器访问单个帧

时间:2016-09-19 10:19:54

标签: ios swift video avfoundation avplayer

在最近的一个项目中,我必须使用AV Foundation单独访问我视频的所有帧。此外,如果可能的话,随机访问它们(如数组)

我试图研究这个问题,但我没有得到任何有用的东西。

注意:是否有任何有用的文档可以熟悉AV基金会?

2 个答案:

答案 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使用tas 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 检索特定图像。