How to get video size for HLS stream inside AVPlayer?
Asked Answered
K

4

25

I am trying to get video resolution when playing hls stream. I have typical player init:

let urlAsset = AVURLAsset(URL: currentVideoUrl)
 self.player=AVPlayer(playerItem: AVPlayerItem(asset:urlAsset))
 .......

I use KVO and i try to get video size when i get .ReadyToPlay status for AVPlayerItem:

    func resolutionSizeForVideo() {

    guard let videoTrack = self.player.currentItem?.asset.tracksWithMediaType(AVMediaTypeVideo).first else
    { return
    }

    let size = CGSizeApplyAffineTransform(videoTrack.naturalSize, videoTrack.preferredTransform)
    let frameSize = CGSize(width: fabs(size.width), height: fabs(size.height))
    print ("video size: \(frameSize)")

}

The problem is that tracksWithMediaType() always returns empty array (but works for non-stream files, e.g. for .mov).

How can i get size (CGRect) of the HLS video playing inside AVPlayer?

Krell answered 21/6, 2016 at 11:20 Comment(2)
What do you mean by size? The actual resolution of the video, or the rect of the player layer?Flossieflossy
Video resolution, yes (edited the question).Krell
T
16

Tracks will always return nil when using HLS. If you have a UIView subclass that overrides its layerClass with an AVPlayerLayer for playing the video you can get the size with

playerView.layer.videoRect

This is the size of just the video and not the entire layer.

Alternatively you can use KVO to observe the presentationSize of the item

player.addObserver(self, forKeyPath: "currentItem.presentationSize", options: [.Initial, .New], context: nil)
Trompe answered 30/6, 2016 at 12:53 Comment(5)
I tried with KVO (presentationSize) before and it didn't return video resolution.Krell
You can use player.currentItem.presentationSize getter any time, KVO is just for convenience. This will give you the resolution. Output from this command in a periodic observer is print(self?.mediaPlayer?.currentItem?.presentationSize) --> Optional((853.333312988281, 480.0)) Trompe
Made a comment saying that presentationSize was not the native size of the asset and that it was actually the size of the video in the player, but that doesn't seem correct. In my app the size that continues to get printed out is 1280x720 even when switching between landscape and portrait. Removed that previous comment since it was technically incorrect.Coacher
Just wanted to point something here - a KVO on currentItem.presentationSize is the correct way to do it , but it could still return CGSizeZero if the player item is NOT ready to play.Goods
This doesn't provide access prior to display.Clark
M
15

Are you able to log at least the video info using this method?

extension AVAsset{
    func videoSize()->CGSize{
        let tracks = self.tracks(withMediaType: AVMediaType.video)
        if (tracks.count > 0){
            let videoTrack = tracks[0]
            let size = videoTrack.naturalSize
            let txf = videoTrack.preferredTransform
            let realVidSize = size.applying(txf)
            print(videoTrack)
            print(txf)
            print(size)
            print(realVidSize)
            return realVidSize
        }
        return CGSize(width: 0, height: 0)
    }

}

let videoAssetSource = AVAsset.init(URL: videoURL)
print("size:",videoAssetSource.videoSize())
Malinin answered 5/7, 2016 at 3:38 Comment(2)
The value returned for tracksWithMediaType(AVMediaTypeVideo) on an m3u8 asset will be an empty array, so this code would result in an index out of bounds exception.Coacher
For HLS this let tracks = self.tracks(withMediaType: AVMediaType.video) will be an empty array.Raines
C
5

You can concisely make it as so:

import AVKit

extension AVAsset {

    var videoSize: CGSize? {
        tracks(withMediaType: .video).first.flatMap {
            tracks.count > 0 ? $0.naturalSize.applying($0.preferredTransform) : nil
        }
    }
}

Clark answered 26/3, 2021 at 0:23 Comment(0)
S
0

We can use a new implementation since iOS 16 because previous is deprecated (since iOS 16 and newer versions).

import AVKit
import Foundation

extension AVAsset {
    func videoSize() async throws -> CGSize? {
        guard let track = try await loadTracks(withMediaType: .video).first else { return nil }
        let size = try await track.load(.naturalSize)
        return size
    }
}
Sigismond answered 29/11, 2023 at 12:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.