1

I am making an iOS app that can be compared to instagram with a feed of videos (no images). I have a table showing custom table view cells, each table view cell contains a video playing using AVKit. I have 6 cells loading at the moment, and it is using an unbelievable amount of memory to show them. How does an app like facebook, instagram, or vine show that many videos without using an insane amount of memory?

Below is the code that I am currently using.

var cells: [VideoCell] = []
    var cellPostkeys: [String] = []

    @IBOutlet weak var feedTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        feedTableView.delegate = self
        feedTableView.dataSource = self
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 250
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return videoURLs.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let url = videoURLs[indexPath.row]
        if let cell = tableView.dequeueReusableCell(withIdentifier: "testcell") as? VideoCell {
            if (cellPostkeys.contains(url)) {
                let index = cellPostkeys.firstIndex(of: url)
                cells[index!] = cell
            } else {
                cells.append(cell)
                cellPostkeys.append(url)
            }
            cell.playvid(url: url)
            return cell
        } else {
            return VideoCell()
        }
    }
}

And this is the code for my custom table view cell

class VideoCell: UITableViewCell {

    @IBOutlet weak var videoView: UIView!


    var player : AVPlayer!
    var avPlayerLayer : AVPlayerLayer!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        //
    }

    func stopvid() {
        self.player = nil
        self.avPlayerLayer = nil
    }

    func playvid(url: String) {
        self.player = AVPlayer(url: URL(string: url)!)
        self.avPlayerLayer = AVPlayerLayer(player: self.player)
        self.avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        self.player.automaticallyWaitsToMinimizeStalling = false
        avPlayerLayer.frame = videoView.layer.bounds;
        self.videoView.layer.addSublayer(self.avPlayerLayer)

        self.player.play()

        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { [weak self] _ in
            self?.player?.seek(to: CMTime.zero)
            self?.player?.play()
        }
    }

}

I understand that loading and playing 6 videos at once is obviously going to be incredibly memory taxing, so I tried only playing 1 video and the other 5 cells being empty, but that was also using about 100+ mb of memory once I started scrolling up and down. Any and all help would be greatly appreciated.

Will Cohen
  • 23
  • 5
  • This question is tagged Firebase but I don't see anything related to Firebase in the question. Also, it's unclear where the videos are stored, in what format, and what the size is of each video (both in pixel size and actual Mb size). That can be a huge factor in an answer so can you update the question with a bit more detail about that? – Jay Aug 14 '19 at 19:55

1 Answers1

3

Try using the Memory Debugger in Xcode. Check if there are any classes that seem to only increase in number. It might be leaking because you create a new AVPlayer each time and never stop the previous one.

You don't have to instantiate AVPlayer each time.

private lazy var player: AVPlayer = AVPlayer(playerItem: nil)

private lazy var playerLayer: AVPlayerLayer = {
    let playerLayer = AVPlayerLayer(player: self.player)
    playerLayer.videoGravity = .resizeAspectFill
    return playerLayer
}()

override func awakeFromNib() {
    super.awakeFromNib()
    self.videoView.layer.addSublayer(self.playerLayer)
}

override func layoutSubviews() {
    super.layoutSubviews()
    avPlayerLayer.frame = videoView.layer.bounds
}

Now in stopvid() you can either just pause or call player.replaceCurrentItem(with: nil)

And in playvid(url: String) you use something like:

let playerItem = AVPlayerItem(url: URL(string: url)!)
player.placeCurrentItem(with: playerItem)
player.play()

It should be noted I haven't been able to run this code since I'm on a Windows machine right now, but according to documentation it should work.