Let's_Create
Let's_Create

Reputation: 3613

UICollectionView gets stuck while scrolling

I have a few videos in my collectionView cell. I am fetching them from an URL and playing it using AVPlayer.

Everything works fine. But, there is a requirement to show a mute/unmute icon depending on if the video contains audio or not.

In order to check if the video contains audio or not I used this code:

(self.player?.currentItem?.asset.tracks.filter({$0.mediaType == AVMediaType.audio}).count != 0)

I got this from: How to check if AVPlayer has Video or just Audio?

As soon as I add this code my collectionView stuck for the very first time when I scroll the collection view.

I am calling it from cell's init. Tried performing it on background thread but still, I am getting the issue.

DispatchQueue.global(qos: .background).async {
    let isAudioAvailable = (self.player?.currentItem?.asset.tracks.filter({$0.mediaType == AVMediaType.audio}).count != 0)
    DispatchQueue.main.async {
        self.audioIconButton.isHidden = !isAudioAvailable
        self.player?.isMuted = isMuted
        let image = isMuted ? #imageLiteral(resourceName: "Mute_Icon") : #imageLiteral(resourceName: "Unmute_Icon")
        self.audioIconButton.setImage(image, for: .normal)
    })
}

Code to fetch from URL:

func getAVPlayerFromUrl(view: UIView) -> AVPlayer? {
    let size: CGSize = view.bounds.size.applying(CGAffineTransform(scaleX: UIScreen.main.scale, y: UIScreen.main.scale))
    guard let url = URL(string: AppConfig.shared.appConfig.video_buckets.videoURL(for: self, size: size)) else { return nil }

    let videoAsset = AVURLAsset(url: url)
    videoAsset.loadValuesAsynchronously(forKeys: ["playable"])
    let playerItem: AVPlayerItem = AVPlayerItem(asset: videoAsset)
    return AVPlayer(playerItem: playerItem)
}

Please let me know what's going wrong here. Or Is there any other way to distinguish if a video contains audio or not?

Upvotes: 0

Views: 705

Answers (1)

Cerlin
Cerlin

Reputation: 6722

The idea here is to cache the count on loading the asset instead of loading the cell

So the fetch logic will change to something similar to below code

func getAVPlayerFromUrl(view: UIView, cellIndex: Int) -> AVPlayer? {
    let size: CGSize = view.bounds.size.applying(CGAffineTransform(scaleX: UIScreen.main.scale, y: UIScreen.main.scale))
    guard let url = URL(string: AppConfig.shared.appConfig.video_buckets.videoURL(for: self, size: size)) else { return nil }

    let videoAsset = AVURLAsset(url: url)
    videoAsset.loadValuesAsynchronously(forKeys: ["playable"]) {
        // Here audioCount is the dict we are using to save the count and cellIndex is the index of the cell in collectionview
        self.audioCount[cellIndex] = videoAsset.tracks.filter({$0.mediaType == AVMediaType.audio}).count
    }
    let playerItem: AVPlayerItem = AVPlayerItem(asset: videoAsset)
    return AVPlayer(playerItem: playerItem)
}

And finally use it like

let isAudioAvailable = (self.audioCount[index] != 0)
self.audioIconButton.isHidden = !isAudioAvailable
self.player?.isMuted = isMuted
let image = isMuted ? #imageLiteral(resourceName: "Mute_Icon") : #imageLiteral(resourceName: "Unmute_Icon")
self.audioIconButton.setImage(image, for: .normal)

Upvotes: 1

Related Questions