Get the number of frames in a video through AVFoundation

I am trying to find the number of frames in the video that I just opened, without decoding all the frames.

Open with AVAsset , and then get AVAssetTrack for the video. What's next?

+6
source share
4 answers

One relatively expensive way to do this is to use AVAssetReader to read all frames and count them in the process.

 let asset = AVAsset(url: url) let assetTrack = asset.tracksWithMediaType(AVMediaTypeVideo).first! let assetReader = try! AVAssetReader(asset: self) let assetReaderOutputSettings = [ kCVPixelBufferPixelFormatTypeKey as String: NSNumber(unsignedInt: kCVPixelFormatType_32BGRA) ] let assetReaderOutput = AVAssetReaderTrackOutput(track: assetTrack, outputSettings: assetReaderOutputSettings) assetReaderOutput.alwaysCopiesSampleData = false assetReader.addOutput(assetReaderOutput) assetReader.startReading() var frameCount = 0 var sample: CMSampleBufferRef? = assetReaderOutput.copyNextSampleBuffer() while (sample != nil) { frameCount++ sample = assetReaderOutput.copyNextSampleBuffer() } // now you have frame count print(frameCount) 
+5
source

You can do it:

 float durationInSeconds = CMTimeGetSeconds(asset.duration); float framesPerSecond = assetTrack.nominalFrameRate; float numberOfFrames = durationInSeconds * framesPerSecond; 
+4
source

This has been upgraded to Swift 4.2 and converted to a URL extension.

 import AVFoundation extension URL { var videoFrameCount: Int? { let asset = AVAsset(url: self) guard let assetTrack = asset.tracks(withMediaType: .video).first else { return nil } var assetReader: AVAssetReader? do { assetReader = try AVAssetReader(asset: asset) } catch { print(error.localizedDescription) return nil } let assetReaderOutputSettings = [ kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32BGRA) ] let assetReaderOutput = AVAssetReaderTrackOutput(track: assetTrack, outputSettings: assetReaderOutputSettings) assetReaderOutput.alwaysCopiesSampleData = false assetReader?.add(assetReaderOutput) assetReader?.startReading() var frameCount = 0 var sample: CMSampleBuffer? = assetReaderOutput.copyNextSampleBuffer() while (sample != nil) { frameCount += 1 sample = assetReaderOutput.copyNextSampleBuffer() } return frameCount } } 

If he can convert the URL to an AVAsset type video , he will continue. Otherwise, nil is returned.

Then AVAssetReader is created. If this step fails, it will return nil again.

Since everything is set up correctly, he will now begin to analyze the output and count the frames. While the sample being produced, the cycle will continue to increase. Once the samples are no longer created, it completes the loop and returns a value for frameCount .

+2
source

Although solutions using AVAssetReader + AVAssetReaderTrackOutput are correct, I recommend creating AVAssetReaderTrackOutput with nil output settings. So AVAssetReader does not decompress frames, and you will get the result much faster.

Here is an example implementation:

 int getNumberOfFrames(NSURL *url) { AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVAssetReaderTrackOutput *readerVideoTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:nil]; AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:asset error:nil]; [assetReader addOutput:readerVideoTrackOutput]; [assetReader startReading]; int nframes = 0; for (;;) { CMSampleBufferRef buffer = [readerVideoTrackOutput copyNextSampleBuffer]; if (buffer == NULL) { break; } CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(buffer); CMMediaType mediaType = CMFormatDescriptionGetMediaType(formatDescription); if (mediaType == kCMMediaType_Video) { nframes++; } CFRelease(buffer); } return nframes; } 
0
source

All Articles