AVPlayer video track freeze after 1 second while audio continues

Hi,

In my SwiftUI app, I'm using AVPlayer and a UIViewRepresentable view to play back a video from a network server.

There are many videos on the server, so I have a LazyHStack for the video thumbnails. Only one video is shown on screen at a time.

When the user scrolls between videos, I will create a new AVPlayerItem and replace the current player item in the AVPlayer.

The problem is: the first video plays back without any problems. But when the user scrolls over to the next video, the new video would play for 1 second and then freezes while the audio track continues. The video track would resume after a few seconds.

One note is that if I drag the video view a bit without fully scrolling to another video, the video track will resume after I released the drag. So it seems maybe somehow the AVPlayerLayer was not rendering?

Related code pieces:

struct PlayerView: UIViewRepresentable {
    let player: Player
    let width: CGFloat
    let height: CGFloat
    @Binding var updateCount: Int
    
    func makeUIView(context: Context) -> UIView {
        print("\(#function)")
        let playerLayer = AVPlayerLayer(player: player)
        let view = UIView()
        view.frame = CGRectMake(0, 0, width, height)
        playerLayer.frame = view.bounds
        view.layer.addSublayer(playerLayer)
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        print("\(#function)")
        guard let playerLayer = uiView.layer.sublayers?.first as? AVPlayerLayer else { return }
        uiView.frame = CGRectMake(0, 0, width, height)
        if updateCount > 0 {
            playerLayer.player = player
            playerLayer.frame = uiView.bounds
        }
    }
}

updateCount was added to force updateUIView called when the parent View increases the updateCount. But it seems not helping much.

My question: Is there any known issue about replacing the current item in AVPlayer in SwiftUI app?

Here is my code of replacing the current item:

        let asset = AVURLAsset(url: url)
        asset.resourceLoader.setDelegate(urlDelegate, queue: urlDelegate.resourceLoaderQueue)

        let assetKeys = ["playable", "hasProtectedContent"]
        let playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: assetKeys)
        player.replaceCurrentItem(with: playerItem)

Note that I have a custom delegate to handle the resource loader for the URLAsset. This delegate is the same for all videos. They always plays well when selected first time (i.e. without replacing another video).

One more data point: I have an ObjC version of the same design, and it worked without problems.

Replies

I tried something different: instead of wrapping UIView using UIViewRepresentable, I changed to wrap AVPlayerViewController using UIViewControllerRepresentable, and then don't use my own video controls anymore. It seems like:

  • If I share the same AVPlayer for each video item (i.e. each AVPlayerViewController), then I still have the video track freezing problem when scrolling between video items.

  • If I create a new AVPlayer for each video item, then the video track freezing problem is gone. (I'm yet to find the best way to dismiss / stop AVPlayerViewController in wrapping swiftUI when it's scrolling off screen, any suggestions?)

Finally I created a new AVPlayer for each item, i.e. no longer replacing an old item with a new item in the same AVPlayer and the issue is gone.