Issue with reusing AVPlayer instance for playing video - ios

I am to play same video in two screens with keeping seek time. So that I am reusing AVPlayer instance in these screen. For first time AVPlayer works fine, but whenever it is reused frame of player animates from top-left to fit target view on start of play.
like,
https://www.dropbox.com/s/8ieb4bnb0rsn9ig/ezgif.com-gif-maker.gif?dl=0
code looks like,
if myObj._playerResult != nil {
_player = myObj._playerResult //reuse player instance
} else {
// First create an AVPlayerItem
let playerItem : AVPlayerItem = AVPlayerItem(URL: NSURL(string:sVideoUrl as String))
_player = AVPlayer(playerItem: playerItem)
}
playerController = AVPlayerViewController()
playerController?.showsPlaybackControls = false
playerController?.player = _player
playerController!.view.frame = targetView.bounds
targetView.addSubview(playerController!.view)
_player?.play()

Related

AVPlayerLayer shows black screen but sound is working

Im trying to display a local recorded video in a AVPlayerLayer which works sometimes. I can hear the audio from the recorded video but can't see the video. Sometimes both video and audio is working, sometimes only audio.
I've tried both with a AVPlayerLayer and AVPlayerViewController but the same issue occurs in both cases. So it's not because of the frames being wrong.
Example code AVPlayerViewController:
let player = AVPlayer(url: url)
let playerController = AVPlayerViewController()
playerController.player = player
self.present(playerController, animated: true) {
player.play()
}
Example code AVPlayerLayer:
let asset = AVAsset(url: url)
let item = AVPlayerItem(asset: asset)
self.player = AVPlayer(playerItem: item)
self.player?.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions(), context: nil)
self.playerLayer = AVPlayerLayer(player: self.player!)
self.playerLayer?.frame = imageView.bounds
imageView.layer.addSublayer(self.playerLayer!)
Edit 1:
When observing the status of the player, error is nil and the status is readyToPlay
Edit 2:
Works fine if the URL is remote.
Edit 3:
Seems to work if I wait a couple of seconds after the video has completed the export. Could it be something to have with the file not 100% written to the filesystem?
Edit 4:
Video of the problem, in this case it played the 3rd time.
Here's how I set a AVPlayerLayer with the video working (I think what you're missing is the videoGravity parameter).
let bundle = Bundle.main
let moviePath = bundle.path(forResource: "myVideo", ofType: "mp4")
let moviePlayer = AVPlayer(url: URL(fileURLWithPath: moviePath!))
playerLayer = AVPlayerLayer(player: moviePlayer)
playerLayer.frame = movieView.bounds
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
movieView.layer.addSublayer(playerLayer)
playerLayer.player?.play()
It's the frame height or width is equal to zero
i have the same issues as you did. the reason is in iOS 10.xx , if you export video with animationTool .
You will meet the trouble like that .
try to fix them by remove this code .
something like that
mainComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentlayer)
Hope to help you
What caused this issue for me was I was changing assets to play a new video. The problem was I was reinitializing the same AVPlayer and setting setting it to the playerLayer which was previously set
Incorrect
player = AVPlayer()
playerLayer = AVPlayerLayer(player: player)
// ...
player?.replaceCurrentItem(with: playerItem)
Correct
if player == nil {
player = AVPlayer()
playerLayer = AVPlayerLayer(player: player)
// ...
}
player?.replaceCurrentItem(with: playerItem)
Or better yet I should've just called this by itself
player?.replaceCurrentItem(with: playerItem)

Swift How to update url of video in AVPlayer when clicking on a button?

I have a video player in an IOS App and I want to update the video when I click on a button, but I do not see how to manage this.
(Note : it's not a list of video within a queue)
Here is the code for adding the AVPlayer:
let url = NSURL.fileURLWithPath(path)
let player = AVPlayer(URL: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
..
..
self.ViewForVideo.addSubview(playerViewController.view)
self.addChildViewController(playerViewController)
player.play()
I have done like this : each time I want to change the video I create a new AVPlayer and affect it to the playerViewController.player like this
let url = NSURL.fileURLWithPath(path)
let player = AVPlayer(URL: url)
playerViewController.player = player

AVPlayer automatically zoom in video in IOS + Swift

I've used AVPlayer and AVPlayerViewController.
let playerController = AVPlayerViewController()
var player = AVPlayer()
self.player = AVPlayer(URL: NSURL(fileURLWithPath:videoPath))
self.playerController.player = self.player
self.playerController.showsPlaybackControls = false
self.addChildViewController(self.playerController)
self.view.addSubview(self.playerController.view)
self.playerController.view.frame = ...
I can play video with controls but when video finish I have to hide controls.
I found the event if video finished and at that time I hide the control
func playerDidFinishPlaying(note: NSNotification) {
self.playerController.showsPlaybackControls = false
}
Problem: If user zoom out video and video finished then hide control so Is there any possibilty to back to original frame
Thank you so much in adv.

MPMoviePlayerController switching videos

I'm playing a video:
let path = NSBundle.mainBundle().pathForResource("Video", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = moviePlayer {
player.view.frame = self.view.bounds
player.prepareToPlay()
player.scalingMode = .AspectFill
player.controlStyle = .None
self.view.addSubview(player.view)
}
and I want to replace this with another video when a button is pressed:
#IBAction func aButtonIsPressed(sender: AnyObject) {
let path = NSBundle.mainBundle().pathForResource("Another Video", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = moviePlayer {
player.view.frame = self.view.bounds
player.prepareToPlay()
player.scalingMode = .AspectFill
player.controlStyle = .None
self.view.addSubview(player.view)
}
}
When I tap the button the video plays, but there seems to be a delay adding the new video, which gives it a choppy transition. Is there any way to minimize this delay? I could try adding another MPMoviePlayerController to preload the second video maybe but from some testing I found the line causing the chop is here.
self.view.addSubview(player.view)
Can I keep the first video playing until the line above 'didFinish'? That would be great.
You are creating new MPMoviePlayerController object that's why you are getting this issue, you should use same object and which is already playing an object and just set contentURL property to new resource's URL,
If you set this property while a movie is playing, that movie pauses
and the new movie begins loading. The new movie starts playing at the
beginning
For more detail visit this link

How to show paused video instead of black screen when starting AVPlayer

I am able to successfully create a player but was annoyed with the initial black screen. I decided to overlay a UIImageView and hide it once the player started. This worked, but I didn't want the hassle of creating and maintaining images for all my videos.
I was able to achieve the exact results I wanted by playing and immediately pausing the player after instantiating it. The only issue was that sometimes the state of the player was getting recorded incorrectly, so when I went to start the player again, the status was listed as already "playing" even though the player was paused.
I starting looking into using AVPlayerItem seekToTime but haven't found any feasible solutions. Is there a "non hacky" way of achieving this?
If you're using an AVPlayerViewController, this is a perfect use of the player controller's contentOverlayView property. This is a UIView between the player layer and the controls exposed exactly for this purpose:
First, create the screenshot:
let asset = AVAsset(URL: URL(string: "")!) // link to some video
let imageGenerator = AVAssetImageGenerator(asset: asset)
let screenshotTime = CMTime(seconds: 1, preferredTimescale: 1)
if let imageRef = try? imageGenerator.copyCGImageAtTime(screenshotTime, actualTime: nil) {
let image = UIImage(CGImage: imageRef)
// see part 2 below
}
Now, add the image as a subview of the contentOverlayView in the player controller:
// in the same try block
let imageView = UIImageView(image: image)
let playerVC = AVPlayerViewController()
let playerItem = AVPlayerItem(asset: asset)
playerVC.player = AVPlayer(playerItem: playerItem)
self.presentViewController(playerVC, animated: true) {
playerVC.contentOverlayView?.addSubview(imageView)
// adjust the frame of your imageView to fit on the player controller's contentOverlayView
}
Then, remove the imageView subview when the player starts playing the asset, or when buffering completes.
The AVPlayerLayer associated with your player has a readyForDisplay property.
You can try to make your host view an observer for this value, and do a refresh as soon as it is set to true.
It is set to true when the first frame is ready to be rendered, so before the player has enough data to play and set its status to readyToPlay.

Resources