检测AVAsset中的当前关键帧间隔

我正在开发一个播放video的应用程序,允许用户在video中向前和向后擦除。 擦洗必须顺利进行,因此我们总是使用带有video压缩属性AVVideoMaxKeyFrameIntervalKey:@1video,以便每个帧都是一个关键帧并允许平滑的反向清理。 这很好用,并提供流畅的播放。 该应用程序使用来自各种来源的video,可以记录在Android或iOS设备上,甚至可以从网上下载并添加到应用程序中,因此我们最终得到了完全不同的编码,其中一些已经适合擦洗(每帧是一个关键帧)。 有没有办法检测video文件的关键帧间隔,以便我可以避免不必要的video处理? 我经历过很多AVFoundation的文档,并没有看到明显的方法来获取这些信息。 感谢您的帮助。

如果您可以通过使用nil outputSettings创建AVAssetReaderTrackOutput来快速解析文件而不解码图像。 您遇到的帧样本缓冲区具有包含具有有用信息的字典的附件数组,包括帧是否依赖于其他帧,或者其他帧是否依赖于它。 我会将前者解释为指示一个关键帧,虽然它给了我一些低数字(一个文件中有4%的关键帧?)。 无论如何,代码:

 let asset = AVAsset(url: inputUrl) let reader = try! AVAssetReader(asset: asset) let videoTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0] let trackReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: nil) reader.add(trackReaderOutput) reader.startReading() var numFrames = 0 var keyFrames = 0 while true { if let sampleBuffer = trackReaderOutput.copyNextSampleBuffer() { // NB: not every sample buffer corresponds to a frame! if CMSampleBufferGetNumSamples(sampleBuffer) > 0 { numFrames += 1 if let attachmentArray = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, false) as? NSArray { let attachment = attachmentArray[0] as! NSDictionary // print("attach on frame \(frame): \(attachment)") if let depends = attachment[kCMSampleAttachmentKey_DependsOnOthers] as? NSNumber { if !depends.boolValue { keyFrames += 1 } } } } } else { break } } print("\(keyFrames) on \(numFrames)") 

注意这仅适用于本地文件资产。

ps你没有说你是如何擦洗或玩耍的。 AVPlayerViewControllerAVPlayer

这是相同答案的Objective C版本。 在实现并使用它之后,应该具有所有关键帧的video将从此代码返回大约96%的关键帧。 我不知道为什么,所以我使用这个数字作为决定因素,即使我希望它更准确。 我也只是通过前600帧或video结束(以先到者为准),因为我不需要阅读整整20分钟的video来做出这个决定。

 + (BOOL)videoNeedsProcessingForSlomo:(NSURL*)fileUrl { BOOL needsProcessing = YES; AVAsset* anAsset = [AVAsset assetWithURL:fileUrl]; NSError *error; AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:anAsset error:&error]; if (error) { DLog(@"Error:%@", error.localizedDescription); return YES; } AVAssetTrack *videoTrack = [[anAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVAssetReaderTrackOutput *trackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:nil]; [assetReader addOutput:trackOutput]; [assetReader startReading]; float numFrames = 0; float keyFrames = 0; while (numFrames < 600) { // If the video is long - only parse through 20 seconds worth. CMSampleBufferRef sampleBuffer = [trackOutput copyNextSampleBuffer]; if (sampleBuffer) { // NB: not every sample buffer corresponds to a frame! if (CMSampleBufferGetNumSamples(sampleBuffer) > 0) { numFrames += 1; NSArray *attachmentArray = ((NSArray*)CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, false)); if (attachmentArray) { NSDictionary *attachment = attachmentArray[0]; NSNumber *depends = attachment[(__bridge NSNumber*)kCMSampleAttachmentKey_DependsOnOthers]; if (depends) { if (depends.boolValue) { keyFrames += 1; } } } } } else { break; } } needsProcessing = keyFrames / numFrames < 0.95f; // If more than 95% of the frames are keyframes - don't decompress. return needsProcessing; }