Set opacity of AVPlayerLayer - ios

I have seen quite a few posts of people wanting to play videos with transparent backgrounds. That is not what I am trying to accomplish here.
I am trying to set the opacity of the whole layer. Below is how I am setting the opacity of an image layer. I have tried the same approach to set opacity for the video layer but nothing happened when I attempted to fade
#IBAction func imageFadeAction(_ sender: Any) {
let rounded = Int(round(imageFader.value))
imageLabel.text = "\(rounded.description)%"
let fade = imageFader.value / 100
SecondScreen.main?.backgroundImage.alpha = CGFloat(fade)
SecondScreen.main?.viewDidLoad()
}
This is how I am playing the video and generating the layer
func playVideo() {
let videoURL = SecondScreen.videoPath
self.player = AVPlayer(url: videoURL!)
self.avpController = AVPlayerLayer(player: player)
avpController.frame = self.view.bounds
self.view.layer.addSublayer(avpController)
player.play()
}
Can anyone tell me if its possible or point me in the right direction

To answer my own question, Use a container view. play your video in a separate ViewController and embed it in the container view. THEN you can set the opacity of the container view.
I don't know if this is the correct way to do it, but it works for me and what I needed. If someone has a better answer please feel free to share

You can set its opacity to 0. something like this:
guard let path = Bundle.main.path(forResource: "SomeVideo", ofType: ".Someformat") else {
return nil
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
let playerLayer = AVPlayerLayer(player: player)
playerLayer.opacity = 0
.
.
.

Related

Stop AVPlayer video when a new UITableViewCell is selected

I want to show a video accordingly to the selected cell in my tableview. I want to show the video in a view in my vc.
The following code is showing the correct videos when the cells are tapped but whenever i select a new cell the audio from the old video is still there. How can i remove it ?
Setting up the view in my vc:
func setupView(for url: URL) {
let player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.videoView.bounds
self.videoView.layer.addSublayer(playerLayer)
player.play()
}
didSelectRow code:
let selectedExercise = exercises[indexPath.row]
setupView(for: URL(string: selectedExercise.videoUrl)!)
My guess is that i should pause/stop the previous video before showing the new one. But i don't know how to.
Thanks a lot!
It's happend because you create new instance of AVPlayer every tap.
To solve your problem, you need to move
let player = AVPlayer(url: url)
outside of setupView() and call it only once. You could move it in init() for example.
For updating asset (video url) in setupView() you could use
player.replaceCurrentItem(with: AVPlayerItem(url: url))
.

AVPlayer full screen issue only for iPhone x

I created a splash video using Av player view controller. its is coming properly for all the devices except I Phone X. I tried changing video gravity frame and everything but it won't work. any Idea about this? here is the sample code :
guard let videoPath = Bundle.main.path(forResource: "Redtaxi-splash", ofType:"mov") else {
return
}
let videoURL = URL(fileURLWithPath: videoPath)
let player = AVPlayer(url: videoURL)
playerViewController = AVPlayerViewController()
playerViewController?.player = player
playerViewController?.showsPlaybackControls = false
playerViewController?.view.frame = view.frame
playerViewController?.view.backgroundColor = .white
playerViewController?.view.contentMode = .scaleAspectFill
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(note:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
view.addSubview((playerViewController?.view)!)
playerViewController?.player?.play()
I fixed this by giving the video gravity for the player view controller :
playerViewController.videoGravity = .resizeAspectFill
this will fix the issue by using the video gravity as aspect fill for the avplayer view controller. here is the documentation for video gravity:
The video gravity determines how the video content is scaled or stretched within the player layer’s bounds.
Swift 4
playerViewController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue

Play several videos using AVPlayer in a view

I have an UIView which contains some some texts and views. in each view I need to play a video with AVPlayer , but my problem is only the first view shows the video :
Here is my code :
func playVideo(name:String , onView:UIView) {
let path = Bundle.main.path(forResource: name, ofType: "mp4")
let videoURL = URL(fileURLWithPath: path!)
let player = AVPlayer(url: videoURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = onView.frame
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
onView.layer.addSublayer(playerLayer)
player.play()
}
Using the function :
override func viewDidLoad() {
super.viewDidLoad()
//ScrollView
scrollView.contentSize = menuView.frame.size
scrollView.addSubview(menuView)
//Play videos
playVideo(name: "pizza", onView: videoView1)
playVideo(name: "salad", onView: videoView2)
playVideo(name: "fries", onView: videoView3)
}
when I run the app video only play in videoView1 , any suggestion why this happens ?
I also put my code in viewWillAppear , viewDidLayoutSubviews
According to Apple doc:
class AVPlayer
Note
AVPlayer is intended for playing a single media asset at a time.
I just found an alternative solution , I replaced UIView with UIContainerView and then link them with Embed to other view controller and play video in each view controller. Now it works perfectly as I expected.

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)

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