Sync Video in Avplayerlayer and Avplayerviewcontroller

Sync video in AVPlayerLayer and AVPlayerViewController

I think there is a problem when sharing a player already created to the AVPlayerViewController. I'm not sure why is stoping but it wont happen if you create a new AVPlayer for that controller. A way to sync your player and your AVPlayerViewController could be like this:

First, you create a Notification Name that you'll use when the AVPlayerViewController is dismiss (apple does not give you a way to know when the user dismiss the AVPlayerViewController):

extension Notification.Name {
static let avPlayerDidDismiss = Notification.Name("avPlayerDidDismiss")
}

Then, you extend AVPlayerViewController to post this notification when is going to be dismiss and to send the time when the user left the video:

extension AVPlayerViewController {

open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let seekTime = player?.currentTime() {
let userInfo = ["seekTime": seekTime]
NotificationCenter.default.post(name: .avPlayerDidDismiss, object: nil, userInfo: userInfo)
}
}

}

And in your ViewController you observe that notification, get the seekTime you want to go and use it to setup your avPlayer:

class ViewController: UIViewController {
var player: AVPlayer?

deinit {
NotificationCenter.default.removeObserver(self)
}

override func viewDidLoad() {
super.viewDidLoad()
configure()
NotificationCenter.default.addObserver(self, selector: #selector(avPlayerDidDismiss), name: .avPlayerDidDismiss, object: nil)
}

func configure() {
self.player = getPlayer()
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
self.player?.play()
}

@IBAction func onTapVideoButton(_ sender: UIButton) {
self.player?.pause()
let controllerPlayer = getPlayer()
controllerPlayer.currentItem?.seek(to: self.player!.currentTime(), completionHandler: nil)
let controller = AVPlayerViewController()
controller.player = controllerPlayer
self.present(controller, animated: true, completion: {
controller.player?.play()
})
}

func getPlayer() -> AVPlayer {
let url = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!
return AVPlayer(url: url)
}

@objc
private func avPlayerDidDismiss(_ notification: Notification) {
if let seekTime = notification.userInfo?["seekTime"] as? CMTime {
player?.currentItem?.seek(to: seekTime, completionHandler: nil)
player?.play()
}
}
}

Cons: It will send the notification for every AVPlayerViewController. You use add you as an observer when you need this info. Hope it can help.

How to sync multiple AVPlayer while play the same local video

Create just one AVPlayer and create multiple AVPlayerLayers from that.

The layers will be synchronized.

Two AVPlayer videos out of sync (Swift)

If you want to achieve the sync, you should load the videos separately with AVPlayer and observe the AVPlayerItemStatus property of each player. Only when all of the players have the status .readyToPlay you can loop through the players and set the .rate property.

Edit:

You can also synchronize them by using setRate(_:time:atHostTime:). Don't forget begin loading media data using preroll(atRate:completionHandler:) before calling setRate. Basically:

  • wait for readyToPlay
  • preroll(atRate:completionHandler:) when all players are ready
  • setRate(_:time:atHostTime:) when all players were prerolled

How to sync AVPlayer and MTKView

I custom the BBMetalVideoSource as follow then it worked:

  1. Create a delegate in BBMetalVideoSource to get the current time of the audio player with which we want to sync
  2. In func private func processAsset(progress:, completion:), I replace this block of code if useVideoRate { //... } by:

    if useVideoRate {
    if let playerTime = delegate.getAudioPlayerCurrentTime() {
    let diff = CMTimeGetSeconds(sampleFrameTime) - playerTime
    if diff > 0.0 {
    sleepTime = diff
    if sleepTime > 1.0 {
    sleepTime = 0.0
    }
    usleep(UInt32(1000000 * sleepTime))
    } else {
    sleepTime = 0
    }
    }
    }

This code help us resolve both problems: 1. No audio when preview video effect, and 2. Sync audio with video.

AVPlayer audio out of sync after seek

I resolved it by switching from mp4 to m3u8.
m3u8 is a playlist information file so I was then able to stream from cloudinary instead.

How to sync custom audio with video in iOS?

I shall answer my own question.
There is a thing called MTAudioProcessingTap. It allows for doing audio manipulation stuff just like an audio unit but inside AVPlayer.
There's a link for more details: https://chritto.wordpress.com/2013/01/07/processing-avplayers-audio-with-mtaudioprocessingtap/.=



Related Topics



Leave a reply



Submit