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 AVPlayerLayer
s 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 readysetRate(_:time:atHostTime:)
when all players were prerolled
How to sync AVPlayer and MTKView
I custom the BBMetalVideoSource as follow then it worked:
- Create a delegate in BBMetalVideoSource to get the current time of the audio player with which we want to sync
In func
private func processAsset(progress:, completion:)
, I replace this block of codeif 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
Check the Position of the Xcuielement on Screen While Testing iOS Application Using Xctest
Uicollectionview - Horizontal Paging with One Cell at a Time
Filemanager and Urlsfordirectory Error in Swift 3 Xcode 8
Sprite Frame Animation Cocos2D 3.0
Responseserializer 'Cannot Call Value of Non-Function Type 'Nshttpurlresponse'' with Swift 3
Why Does 'Position:Fixed' Not Work When Viewed in an 'Iframe' Using an iPhone or iOS Device
What Exactly Does ': Class' Do in a Protocol Declaration
Exceeding Max Text("") Concatenation Length - Swiftui -
Add Udid in Current Provisioning Profile
Retina Display Vs Normal Display Color Difference
Proper Sequence to Get Registration Token for Gcm Push Notification on iOS? Is Gcm Unreliable
Is There a Way of Automatically Writing Custom Values to The Bundle's .Plist During a Build Phase
Passing Values from One View Controller to Another in Swift
iOS Swift Streaming App Does Not Play Music in Background Mode
iOS 8 Code Working on iPhone 5S But Not iPhone 5
How to Fix Broken Transform-Origin on iOS11 and Macos10.12 Safari