AVPlayerViewController fadeout on completion - ios

I using AVPlayerViewController which I want to fadeout when the video ends... I can capture the end-of-video event, and I the code put together for AVPlayer works, but for the AVPlayerViewController not.
self.playerItem = AVPlayerItem(URL: videoURL)
self.player = AVPlayer(playerItem: self.playerItem)
self.playerLayer = AVPlayerLayer(player: self.player)
self.streamPlayer = AVPlayerViewController()
self.streamPlayer.player = self.player
self.streamPlayer.view.frame = CGRect(x: 128, y: 222, width: 512, height: 256)
This code works too, but goes full screen- I don't want full screen...
//self.presentViewController(self.streamPlayer, animated: true) {
// self.streamPlayer.player!.play()
//}
I use this?
self.view.addSubview(self.streamPlayer.view)
self.streamPlayer.player!.play()
The event capture ..
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "finishedPlaying:", name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
}
The event Code ...
func finishedPlaying(myNotification:NSNotification) {
let fadeOut = CABasicAnimation(keyPath: "opacity")
fadeOut.fromValue = 1.0
fadeOut.toValue = 0.0
fadeOut.duration = 8.0
fadeOut.delegate = self
fadeOut.setValue("video", forKey:"fadeOut")
fadeOut.removedOnCompletion = false
fadeOut.fillMode = kCAFillModeForwards
This line does nothing with AVPlayerViewController/With AVPlayer it fades out nicely ?
playerLayer.addAnimation(fadeOut, forKey: nil)
This simply removes AVPlayerViewController, plan B if I cannot make this work!
//self.streamPlayer.view.removeFromSuperview()
print("VIDEO finished")
}

AVPlayerViewController has it's own AVPlayerLayer and does not know about the AVPlayerLayer you created. Thus, the line
playerLayer.addAnimation(fadeOut, forKey: nil)
has no effect. You should set or animate the alpha value of AVPlayerViewController's view. For the fade animation you may want to look at the +[UIView animateWithDuration:(...)] methods and call
self.streamPlayer.view.removeFromSuperview()
in the completion handler.

Related

AVPlayer -Play sound while seeking/scrubbing

I have a uislider attached to my avplayer, is there a way to play sound from the video while scrubbing?
var player: AVPlayer?
func createPlayer() {
player = AVPlayer()
// init player with playerItem, asset, eventually add to playerLayer ...
player?.volume = 1
}
#objc func sliderValueChanged(_ slider: UISlider) {
let sliderValue = slider.value
let seekTime = CMTimeMakeWithSeconds(Double(sliderValue), preferredTimescale: 1000)
player?.seek(to: seekTime, toleranceBefore: .zero, toleranceAfter: .zero)
}
Since the UISlider's values update continuously while you interact with it, the AVPlayer doesn't get a chance to play any audio since the seek function gets executed every time the slider's value is updated.
I used this SO example from the comments with some minor tweaks to get this working
At a high level:
You need to add a second AVPlayer which will be responsible for playback during scrubbing
When you start scrubbing, start playing the video in the second AVPlayer from where the first player currently was
As soon as you start playing the second player, it should be locked from being updated by the scrubber and it should play for a short period of of time 1/4 to 1/2 a second (experiment with the time duration)
After this time has elapsed, pause the second player and open it to be updated again - this is what creates that jagged noise
Here is a small example that gave me something close to what you are after:
import UIKit
import AVKit
class AVPlayerScrubVC: UIViewController {
let playerViewController = AVPlayerViewController()
var player: AVPlayer?
var player2: AVPlayer?
var isScrubbingLocked = false
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "Player Scrub"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
configurePlayer()
configureScrubber()
}
private func configurePlayer()
{
// Add your own video URL
let videoURL
= Bundle.main.url(forResource: "sample_1",
withExtension: "mp4")
if let videoURL = videoURL {
player = AVPlayer(url: videoURL)
player2 = AVPlayer(url: videoURL)
player2?.volume = 5
playerViewController.player = player
playerViewController.showsPlaybackControls = false
// Add the AVPlayerController as a child of the current
// view controller
self.addChild(playerViewController)
// Configure the player view
let playerView = playerViewController.view
playerView?.backgroundColor = .clear
playerView?.frame = self.view.bounds
// Add the AVPlayerViewController's view as a subview
// of the current view
self.view.addSubview(playerView!)
playerViewController.didMove(toParent: self)
// Start playing the content
playerViewController.player?.play()
}
}
// Configure the UISlider
private func configureScrubber() {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 0
let videoTime
= Float(CMTimeGetSeconds((player?.currentItem?.asset.duration)!))
slider.maximumValue = videoTime
slider.addTarget(self, action: #selector(sliderValueChanged(_:)),
for: .valueChanged)
view.addSubview(slider)
view.addConstraints([
slider.leadingAnchor
.constraint(equalTo: view.leadingAnchor,
constant: 16),
slider.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor,
constant: -16),
slider.trailingAnchor
.constraint(equalTo: view.trailingAnchor,
constant: -16),
slider.heightAnchor
.constraint(equalToConstant: 70)
])
}
#objc
func sliderValueChanged(_ slider: UISlider) {
let sliderValue = slider.value
let seekTime = CMTimeMakeWithSeconds(Double(sliderValue),
preferredTimescale: 1000)
player?.seek(to: seekTime,
toleranceBefore: .zero,
toleranceAfter: .zero)
if !isScrubbingLocked {
audioScrub(to: seekTime)
}
}
func audioScrub(to currentTime: CMTime) {
DispatchQueue.main.async {
// Lock the scrubbing
self.isScrubbingLocked = true
// play the simultaneous player
self.player2?.play()
// make sure that it plays for at least 0.5 of a second
// before it stops to get that scrubbing effect
// Play around with the delay to get the results best for you
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// now that 1/2 of a second has passed
// (you can make it longer or shorter as you please)
// - pause the simultaneous player
self.player2?.pause()
// now move the simultaneous player to the same point as the
// original video player:
self.player2?.seek(to: currentTime)
// setting this variable back to true allows the process to be
// repeated as long as video is being scrubbed:
self.isScrubbingLocked = false
}
}
}
}

problems while playing video from s3 using AVplayer

Hi all i am trying to play a video from s3 using Avplayer. Now if i play the video, the video starts playback after the whole video is buffered. so I added player.automaticallyWaitsToMinimizeStalling = false, but now the video automatically pauses
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
var player: AVPlayer!
var item : AVPlayerItem!
override func viewDidLoad() {
super.viewDidLoad()
item = AVPlayerItem(url: URL(string: "https://cent-churchconnect.s3-ap-southeast-2.amazonaws.com/cent-churchconnect/testAdmin/eb8cc8b5-80e0-468a-a2c9-979cf1b5ac76_toystory.mp4")!)
player = AVPlayer(playerItem: item)
let controller = AVPlayerViewController()
present(controller, animated: true) { _ in }
controller.player = player
addChildViewController(controller)
view.addSubview(controller.view)
controller.view.frame = CGRect(x: 0, y: 50, width: self.view.frame.size.width, height: 300)
controller.player = player
controller.showsPlaybackControls = true
if #available(iOS 10.0, *) {
player.automaticallyWaitsToMinimizeStalling = false
player.play()
} else {
// Fallback on earlier versions
}
}
}
I had the same issue below steps worked for me,
Try to use method func playImmediately(atRate:) and make sure the property player.automaticallyWaitsToMinimizeStalling = false is set properly.
Use method func playImmediately(atRate:) instead of func play()
I used the cocoa pod https://github.com/piemonte/Player.
You can use the delegate methods to control the playback.

Can't swipe the UIScrollView when AVPlayer is running

Hi everyone I am trying to swipe between two pages through a scrollview and it works fine but when I try to swipe while a video is playing it won't work here is my code for my ViewControllers
MainVeiwController
override func viewDidLoad() {
super.viewDidLoad()
scrollView.isPagingEnabled=true
let settings = SettingsViewController(nibName: "SettingsViewController", bundle: nil)
self.addChildViewController(settings)
self.scrollView.addSubview(settings.view)
settings.didMove(toParentViewController: self)
let diamond = DiamondViewController(nibName: "DiamondViewController", bundle: nil)
var frame1 = diamond.view.frame
frame1.origin.x = self.view.frame.size.width
diamond.view.frame=frame1
self.addChildViewController(diamond)
self.scrollView.addSubview(diamond.view)
diamond.didMove(toParentViewController: self)
self.scrollView.contentSize = CGSize(width: self.view.frame.size.width * 2, height: self.view.frame.size.height-70)
scrollView.contentOffset = CGPoint(x: scrollView.frame.size.width, y: 0)
}
Second ViewController
var playerViewController = AVPlayerViewController()
var playerView = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
let fileURL = NSURL(fileURLWithPath: "PATHTOVIDEO")
playerView=AVPlayer(url: fileURL as URL)
playerViewController.showsPlaybackControls=false;
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerView.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(1, 100);
self.playerView.seek(to: t1)
self.playerView.play()
}
playerViewController.player = playerView
self.present(playerViewController,animated:true){
self.playerViewController.player?.play()
}
}
Now what I want to know is how can I swipe between the pages even though the video is playing is there a workaround or something
Try to embed your AVPlayerViewController in your SecondViewController instead of presenting it.
Code Example:
let url = // whatever
let player = AVPlayer(URL:url)
let av = AVPlayerViewController()
av.player = player
av.view.frame = // whatever
self.addChildViewController(av)
self.view.addSubview(av.view)
av.didMoveToParentViewController(self)
Code from:
AVPlayer with playback controls of avplayerviewcontroller

In swift, how to detect touch while playing video in AVPlayerViewController

I have programmatically added an AVPlayerViewController to a UIViewController. I am able to receive the notification when the player is finished playing (playerDidFinishPlaying). I would also like to know if a user has touched the screen while the video is playing and I have not found any related notifications.
The solution is to create a Base class of AVPlayerViewController and override touches​Began(_:​with:​) method:
Swift 2:
Custom Base Class:
class CustomAVPlayerViewController: AVPlayerViewController {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("touchesBegan")
}
}
ViewController:
let videoURL = NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(URL: videoURL!)
let playerViewController = CustomAVPlayerViewController()
playerViewController.player = player
self.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
Swift 3:
Custom Base Class:
class CustomAVPlayerViewController: AVPlayerViewController {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("touchesBegan")
}
}
View Controller:
let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerViewController = CustomAVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
Don't forget to import AVKit and import AVFoundation.
Each time you tap on the playerViewController, "touchesBegan" will be printed.
I had this same problem. The contentOverlayView is only available in tvos, so that was not an option.
I ended up adding a UIView over the UIImageView that I added the AVPlayer to. I set the background color to clear on the UIView, so it's not visible, but can receive gestures. This provides a target for the tap gesture recognizer.
I resolve the same. Subclassing AVPlayerViewController will work on iOS 11.4.1 but not on iOS 12 an above. So the solution for this is add subview on playerviewcontroller contentoverlayview and then on that subview you can add any gesture or button for detecting the touch. Here is the code snippet for the same::
// This notification is added for continuous playing of the video you can remove this in case you need video is played only once.
private func playVideo() {
guard let path = Bundle.main.path(forResource: "BG01", ofType:"mp4") else {
debugPrint("video.m4v not found")
return
}
self.player = AVPlayer(url: URL(fileURLWithPath: path))
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: kCMTimeZero)
self?.player?.play()
}
let playerController : AVPlayerViewController? = AVPlayerViewController()
let btn : UIButton = UIButton()
btn.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
btn.addTarget(self, action: #selector(touchDetect), for: .touchUpInside)
btn.backgroundColor = UIColor.clear
playerController?.contentOverlayView?.addSubview(btn)
// playerController?.homeVCProtocolDelegate = self as HomeVCProtocol
playerController?.player = player
playerController?.showsPlaybackControls = false
self.player?.play()
present(playerController!, animated: false) {
self.player?.play()
}
}
#objc func touchDetect()
{
// Here you will get the call
}

Video background in background view

I have a local .mp4 video that I would like to add as a background video. I've created a function:
func playVideo() {
let path = NSBundle.mainBundle().pathForResource("dronefootagecompressed", ofType:"mp4")
let url = NSURL.fileURLWithPath(path!)
self.moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = self.moviePlayer {
player.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
player.view.sizeToFit()
player.scalingMode = MPMovieScalingMode.Fill
player.fullscreen = true
player.controlStyle = MPMovieControlStyle.None
player.movieSourceType = MPMovieSourceType.File
player.repeatMode = MPMovieRepeatMode.One
player.play()
self.view.addSubview(player.view)
}
}
which is then called in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
playVideo()
}
The video is showing up, but it seems like it's covering everything else (text labels, titles, everything), or so I assume it is. Can I make the video be in the background (if that even is the issue)?
Thanks.
EDIT:
When I call playVideo() in viewDidAppear, the video shows up for about 2 seconds before going to a black background. The buttons are still visible. Is the video not loading properly?
i think putting it in viewDidAppear() method will solve the thing

Resources