【问题标题】:Accesing Individual Frames using AV Player使用 AV Player 访问单个帧
【发布时间】:2016-09-19 10:19:54
【问题描述】:

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

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

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

【问题讨论】:

  • 我可以回答你的任何问题。但是您必须更多地解释您的问题/问题..
  • 问题是我不知道如何访问视频的各个帧。因为我需要自己在每一帧中应用一些魔法算法。
  • 你想分帧播放视频???
  • 是的,我现在知道了。您想要访问视频帧。对吗??
  • 你会得到图片。

标签: ios swift video avfoundation avplayer


【解决方案1】:

您可以使用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,使用copyPixelBufferForItemTimeas described in this answer,但微妙之处在于您如何选择t

如果您想以均匀的间隔对视频进行采样,那么这很容易,但如果您想使用串行 AVAssetReader 代码看到的相同帧/演示时间戳,那么您可能必须对文件进行预处理 AVAssetReader,构建帧号 -> 演示时间戳图。如果您使用AVAssetReaderTrackOutput 中的nil 输出设置跳过解码,这会很快。

【讨论】:

  • 这会提供我视频的所有帧吗?
  • AVAssetReader 方法可以。如果您知道所有帧时间戳,那么“随机访问”方法也应该知道。
  • 谢谢,这很有帮助。一个问题,您能否提供有关 CMSampleBufferGetImageBuffer 的文档,因为我仍然不明白。另外,有没有办法在本地保存它们以便我检查发生了什么?
  • CMSampleBuffer 头文件中有一些信息。它返回一个CVPixelBufferRef,您可以从中获取宽度、高度和指向像素的指针。对于CVPixelBuffer->UIImage,请参阅stackoverflow.com/q/8072208/22147
  • 谢谢。如果我有任何问题,我会写信给你。
【解决方案2】:

如果您需要在播放视频时获取当前帧,您可以为播放器添加一个观察者,并可以像这样获取当前帧:

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 来检索特定图像。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多