7

Im trying to figure out how to retrieve a videos frame rate via AVPlayer. AVPlayerItem has a rate variable but it only returns a value between 0 and 2 (usually 1 when playing). Anybody have an idea how to get the video frame rate?

Cheers

user346443
  • 4,672
  • 15
  • 57
  • 80

7 Answers7

18

Use AVAssetTrack's nominalFrameRate property.

Below method to get FrameRate : Here queuePlayer is AVPlayer

-(float)getFrameRateFromAVPlayer
{
  float fps=0.00;
  if (self.queuePlayer.currentItem.asset) {
    AVAssetTrack * videoATrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] lastObject];
    if(videoATrack)
    {
        fps = videoATrack.nominalFrameRate;
    }
  }
  return fps;
}
Paresh Navadiya
  • 38,095
  • 11
  • 81
  • 132
5

Swift 4 version of the answer:

let asset = avplayer.currentItem.asset

let tracks = asset.tracks(withMediaType: .video)

let fps = tracks?.first?.nominalFrameRate

Remember to handle nil checking.

Amos
  • 2,222
  • 1
  • 26
  • 42
2

There seems to be a discrepancy in this nominalFrameRate returned for the same media played on different versions of iOS. I have a video I encoded with ffmpeg at 1 frame per second (125 frames) with keyframes every 25 frames and when loading in an app on iOS 7.x the (nominal) frame rate is 1.0, while on iOS 8.x the (nominal) frame rate is 0.99. This seems like a very small difference, however in my case I need to navigate precisely to a given frame in the movie and this difference screws up such navigation (the movie is an encoding of a sequence of presentation slides). Given that I already know the frame rate of the videos my app needs to play (e.g. 1 fps) I can simply rely on this value instead of determining the frame rate dynamically (via nominalFrameRate value), however I wonder WHY there is such discrepancy between iOS versions as far as this nominalFrameRate goes. Any ideas?

DolphinDream
  • 1,235
  • 1
  • 9
  • 5
  • I am getting the same behaviour on Yosemite. I have some sample videos at 10 fps and 20 fps. Mavericks reports this correctly. However, on Yosemite, these get reported as 9.9010 and 18.1818 respectively. – Dinesh Iyer Aug 12 '15 at 15:44
  • Did you solve this? If you encoded the video with 125 frames per second, shouldn't the correct frame rate be 125 and not 1.0 (on iOS 7)? – Crashalot Mar 30 '16 at 14:57
  • @DineshIyer would it be invalid to round up the frame rate to the nearest integer? – Crashalot Mar 30 '16 at 14:58
  • @Crashalot: I do not believe that rounding up is correct in all cases. 29.97 is a common frame rate and rounding up in that case will be wrong. I was surprised why this happens only for Mavericks and higher. – Dinesh Iyer Apr 04 '16 at 13:10
2

The rate value on AVPlayer is the speed relative to real time to which it's playing, eg 0.5 is slow motion, 2 is double speed.

As Paresh Navadiya points out a track also has a nominalFrameRate variable however this seems to sometimes give strange results. the best solution I've found so far is to use the following:

CMTime frameDuration = [myAsset tracksWithMediaType:AVMediaTypeVideo][0].minFrameDuration;
float fps = frameDuration.timescale/(float)frameDuration.value;

The above gives slightly unexpected results for variable frame rate but variable frame rate has slightly odd behavior anyway. Other than that it matches ffmpeg -i in my tests.

EDIT ----

I've found sometimes the above gives time kCMTimeZero. The workaround I've used for this is to create an AVAssetReader with a track output,get the pts of the first frame and second frame then do a subtraction of the two.

alwaysmpe
  • 595
  • 8
  • 17
1

I don't know anything in AVPlayer that can help you to calculate the frame rate.

AVPlayerItem rate property is the playback rate, nothing to do with the frame rate.

The easier options is to obtain a AVAssetTrack and read its nominalFrameRate property. Just create an AVAsset and you'll get an array of tracks.

Or use AVAssetReader to read the video frame by frame, get its presentation time and count how many frames are in the same second, then average for a few seconds or the whole video.

djromero
  • 19,551
  • 4
  • 71
  • 68
  • `nominalFrameRate` is the easiest way. Starting from an AVPlayer, you can get its `currentItem`, then the item's `tracks`, then every track's `assetTrack`, then each asset track's `nominalFrameRate`. Note that movies can have multiple video tracks, and some (audio-only) have no video tracks at all. – Peter Hosey Mar 20 '13 at 04:30
1

This is not gonna work anymore, API has changed, and this post is old. :( The swift 4 answer is also cool, this is answer is similar.

You get the video track from the AVPlayerItem, and you check the FPS there. :)

private var numberOfRenderingFailures = 0
func isVideoRendering() -> Bool {
    guard let currentItem = player.currentItem else { return false }

    // Check if we are playing video tracks
    let isRendering = currentItem.tracks.contains { ($0.assetTrack?.mediaType == .video) && ($0.currentVideoFrameRate > 5) }
    if isRendering {
        numberOfRenderingFailures = 0
        return true
    }
    numberOfRenderingFailures += 1
    if numberOfRenderingFailures < 5 {
        return true
    }
    return false
}
Boris
  • 178
  • 1
  • 7
0

In case of playing a Live stream player.currentItem?.asset doesn't have video tracks at all so you need to get FPS from player.currentItem?.tracks and AVPlayerItemTrack.currentVideoFrameRate property, e.g.:

var currentVideoFrameRate: Float {
  if let track = player.currentItem?.tracks.first(where: { $0.assetTrack?.mediaType == .video }) {
    return track.currentVideoFrameRate
  }
  return 0
}
iUrii
  • 11,742
  • 1
  • 33
  • 48