1

Can anyone help in creating a pool of AVPlayer's to be reused in a tableView? So the idea is to have a pool of AVPlayer's, let's say 3, and continue to reuse them as the user scrolls through the table.

Here's a quick synopsis of the app and what we want to build:

  • Each video is an mp4 and is only 5 seconds long. So each cell will play a 5 second video and loop it.
  • All videos are local, they will be downloaded to disk before the table is even shown to the user. This will help in terms of smooth scroll performance of the tableView.

Right now I am creating too many AVPlayer's and not reusing them which is having a bad effect on performance such as scrolling is a bit choppy. Also, Apple does not allow an infinite amount of AVPlayer's to be created.

Any ideas? Thanks

Update 1:

import UIKit
import VIMVideoPlayer

var cache = NSCache<NSString, VIMVideoPlayerView>()

class FeedTableViewCell: UITableViewCell {
// MARK: - Properties

@IBOutlet weak var containerView: UIView!

static let reuseIdentifier = "FeedTableViewCell"

var video: Video? {
    didSet {
        if let cachedVideoPlayerView = cache.object(forKey: video!.preview!.remoteURL as NSString)  {
            // We have a cached video player view!
            containerView.addSubview(cachedVideoPlayerView)
        } else {
            // There is nothing cached.
            let previewURL = FileManager.applicationDocumentsDirectory.appendingPathComponent(video!.preview!.fileName!)
            let newVideoPlayer = VIMVideoPlayer()
            newVideoPlayer.setURL(previewURL)
            newVideoPlayer.isLooping = true
            newVideoPlayer.isMuted = true
            newVideoPlayer.disableAirplay()

            let newVideoPlayerView = VIMVideoPlayerView()
            newVideoPlayerView.frame = contentView.bounds
            newVideoPlayerView.delegate = self
            newVideoPlayerView.setVideoFillMode(AVLayerVideoGravityResizeAspectFill)
            newVideoPlayerView.player = newVideoPlayer
            containerView.addSubview(newVideoPlayerView)
            cache.setObject(newVideoPlayerView, forKey: video!.preview!.remoteURL as NSString)
        }
    }
}

// MARK: - Life Cycle

override func awakeFromNib() {
    super.awakeFromNib()

    print("AWAKE FROM NIB CELL")
}

override func prepareForReuse() {
    super.prepareForReuse()

}
}

// MARK: - VIMVideoPlayerViewDelegate
extension FeedTableViewCell: VIMVideoPlayerViewDelegate {
func videoPlayerViewIsReady(toPlayVideo videoPlayerView: VIMVideoPlayerView!) {
    videoPlayerView.player.play()
}
}
JEL
  • 1,540
  • 4
  • 23
  • 51
  • Possible duplicate of [AVPlayer.play() in UITableViewCell briefly blocks UI](http://stackoverflow.com/questions/35496766/avplayer-play-in-uitableviewcell-briefly-blocks-ui) – max_ Nov 19 '16 at 21:30
  • hi JEL, did you have perfect solution in the end. I'm doing same code with you and sucked in problems same with you . can you help me , thanks a lot – ShineWang Nov 22 '21 at 10:16

1 Answers1

0

Extending your idea of using a pool, instead of reinventing the wheel, why not leverage UITableViewCell's current implementation of reusability? At any given time, n 'reusable' UITableViewCells exist, acting as your pool.

If each one of these cells contains a single AVPlayer subview, then the management is done for you by the table view. Therefore in the UITableViewCell's reuse identifier, stop the player (if needed), update its MP4 (from an in-memory cache ideally), and start it again.

If needed, you can cache the position of the video when the cell disappears in order to make it seem like the video never stopped playing while scrolling was in progress.

-- As a side note, theoretically this will work, but has not been tested with a live app. The obvious caveat is the size of the video being loaded into the AVPlayer on the main thread, while attempting to maintain 60fps.

Edit:

Please see https://stackoverflow.com/a/35514126/556479 for more info.

Community
  • 1
  • 1
max_
  • 24,076
  • 39
  • 122
  • 211
  • Please see my update with some code. It looks that by your answer, the tableView creates a certain amount of `AVPlayer`'s already and what I need to do is reuse them by replacing the current item, with the new item -- is this correct? Also, in my current code above, I am not doing this it seems, what I'm doing is creating a new player many times. – JEL Nov 19 '16 at 21:37
  • Btw, the `VIMVideoPlayerView` is simply a wrapper around an `AVPlayer`, it's a library from Vimeo. – JEL Nov 19 '16 at 21:38
  • @JEL exactly, instead of creating a new VIMVideoPlayerView for each cell, instantiate it once in each cell, and tell the view to stop in the `prepareForReuse` function. – max_ Nov 19 '16 at 21:40
  • @JEL be aware that your code will crash if the `video` variable is nil. You should ensure that you're handling that case with a guard/if let – max_ Nov 19 '16 at 21:41
  • Ok I think I'm starting to understand this more now. What I'm confused about still is the reusability of the `AVPlayer`'s because I don't know how to replace the current video with the new video. Where and how is this replacement done? Could you explain how this would be done or post some sample code? Thanks! – JEL Nov 19 '16 at 21:43
  • @JEL I'm not sure as I haven't used it before, but the linked question is using an `AVPlayerViewController`, and the replacement is done by updating the `player` property. – max_ Nov 19 '16 at 21:46
  • Hm I see, unfortunately you can't use `AVPlayerViewController` in a tableView cell. It would have to be an `AVPlayer`/`AVPlayerLayer`. I'm stuck on how to replace the videos with new ones -- this is what I was creating new AVPlayer's every time because I did not understand how to replace the videos – JEL Nov 19 '16 at 21:54
  • @JEL there's a function called `replaceCurrentItemWithPlayerItem`. http://stackoverflow.com/questions/33424261/ios-swift-avplayer-embeded-in-uitableviewcell – max_ Nov 19 '16 at 21:56
  • Ah I see, I will accept your answer. Thank you! However, there seems to be another issue which is a separate question - how do you replace the video with no lag. The link you pointed to above has an answer but is not accepted so not sure if it will work. Do you have any suggestions here? – JEL Nov 19 '16 at 22:08
  • @JEL I suggest trying the two methods outlined in the two linked questions. Other than that I'd try using facebook's AsyncDisplayKit and see if that helps you. – max_ Nov 19 '16 at 22:10
  • Thank you for all the help! Really appreciate it. I'll look to implement what you said. – JEL Nov 19 '16 at 22:14
  • @JEL I am trying to achieve the same thing but there is a lag when changing videos in AVPlayer, did you found out any solution other than AsyncDisplayKit. – Hassan Khan May 14 '20 at 06:07