AVPlayer keeps adding streams instead of replacing - ios

I am triggering a video play from a URL. It's just a button, opening the AVPlayerController full screen. If people close it, the can go back to the another item, with possibly a video. There they can click that video to start, however, when they do so, I can hear the audio of the previous player, of the other VC playing in the together with this one. This keeps layering up. How can I avoid this?
This is my class for the videoplayer
import UIKit
import AVFoundation
import AVKit
class simpleVideoPlayer: UIViewController {
var playerController = AVPlayerViewController()
var player:AVPlayer?
var inputVideoUrl: String? = nil
func setupVideo() {
self.player = AVPlayer()
self.playerController.player = self.player
}
func playNext(url: URL) {
let playerItem = AVPlayerItem.init(url: url)
self.playerController.player?.replaceCurrentItem(with: playerItem)
self.playerController.player?.play()
}
func setupVideoUrl(url: String) {
inputVideoUrl = url
}
}
This is in my viewcontroller. It's first getting a URL of a possible advert from my server, if that failed, then it wil just load the "default" video.
let SimpleVideo = simpleVideoPlayer()
#objc func handleTap(gestureRecognizer: UIGestureRecognizer)
{
ApiVideoAdvertService.sharedInstance.fetchVideoAdvert { (completion: VideoAdvert) in
let advertUrl = URL(string: completion.video_adverts_url)
var url = URL(string: (self.article?.video_link?.files[0].link_secure)!)
var showAdvert: Bool = false
if (advertUrl != nil && UIApplication.shared.canOpenURL(advertUrl!)) {
url = advertUrl
showAdvert = true
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
if (showAdvert) {
NotificationCenter.default.addObserver(self, selector: #selector(self.finishVideo),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.SimpleVideo.playerController.player?.currentItem)
}
appDelegate.window?.rootViewController?.present(self.SimpleVideo.playerController, animated: true, completion: {
self.SimpleVideo.setupVideo()
if (showAdvert) {
self.SimpleVideo.playerController.setValue(true, forKey: "requiresLinearPlayback")
}
self.SimpleVideo.playNext(url: url!)
})
}
#objc func finishVideo() {
let url = URL(string: (article?.video_link?.files[0].link_secure)!)
SimpleVideo.playerController.setValue(false, forKey: "requiresLinearPlayback")
SimpleVideo.playNext(url: url!)
}

Removing the observer inside finishVideo did it.
#objc func finishVideo() {
NotificationCenter.default.removeObserver(self)
let url = URL(string: (article?.video_link?.files[0].link_secure)!)
SimpleVideo.playerController.setValue(false, forKey: "requiresLinearPlayback")
SimpleVideo.playNext(url: url!)
}

Related

why video is not getting played only showing loader in swift

I am trying to play the video from the web url it showing buffering loader only but not getting played I have used the following code.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let url = URL(string: "https://sandbox-api.digiboxx.com/uploads/E2D6024483AB4B04/1602945683_sample_640x360.mp4")!
playVideo(url: url)
}
func playVideo(url: URL) {
let player = AVPlayer(url: url)
let vc = AVPlayerViewController()
vc.player = player
self.present(vc, animated: true) { vc.player?.play() }
}
I tried your URL just in the browser (Safari) on my computer and nothing plays. So I think something is wrong with the URL or the file at the other end.
I tested your link and the video is loading, so I don't know why your code isn't working ...
This is how I use VideoPlayer in Swift :
import UIKit
import AVKit
class MediaManager: UIViewController {
var player = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
play(url: "yourUrl")
}
func play (url: String) {
player = AVPlayer(url: URL(string: url)!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.view.present(playerViewController, animated: false) { self.player.play() }
}
}
Try with my code and tell me if it's okay for you

Swift how to pause audio

So I am creating a simple radio station app. I have coded the following and got the play / pause button switching text. I just can't work out how to access the player in the button when the func is inside ViewDidLoad. I am sure I need to set global variables?
override func viewDidAppear(_ animated: Bool) {
guard let url = URL(string: "http://stream.radiomedia.com.au:8003/stream") else {
return
}
// Create a new AVPlayer and associate it with the player view
let player = AVPlayer(url: url)
player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = true
// Create a new AVPlayerViewController and pass it a reference to the player.
let playerViewController = AVPlayerViewController()
playerViewController.contentOverlayView?.backgroundColor = UIColor.white
playerViewController.view.frame = CGRect (x:100, y:100, width:200, height:200)
playerViewController.player = player
self.addChild(playerViewController)
self.view.addSubview(playerViewController.view)
playerViewController.didMove(toParent: self)
playerViewController.player = player
player.play()
}
#IBAction func playVideo(_ sender: Any) {
if(playButton == false){
playButton = true;
(sender as! UIButton).setTitle("Pause", for: [])
player.pause() // does not work
}else
{
playButton = false;
(sender as! UIButton).setTitle("Play", for: [])
player.play() // does not work
}
}
class YourVC: UIViewController {
var player = AVPlayer()
}
just declare the player inside your class so that other functions can access it.
and in your viewDidApper(), change:
let player = AVPlayer(url: url)
to
self.player = AVPlayer(url: url)
then you can call player.pause() and player.play() from anywhere inside your Class.
import UIKit
import AVFoundation
class AudioPlayerVC: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("wakeUp", ofType: ".mp3"))
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
#IBAction func onPressPlayPaush(_ sender: UIButton) {
if(sender == false){
sender = true
audioPlayer.pause()
}else
{
sender = false
audioPlayer.play()
}
}

Swift 4 & Xcode 10. Play video on app launch, when complete, reveal view controller

Firstly, I'm totally new to Xcode 10 and Swift 4, and I've searched here but haven't found code that works.
What I'm after:
On launching app to play a video which is stored locally (called "launchvideo").
On completion of video to display/move to a UIviewcontroller with a storyboard ID of "menu"
So far I have my main navigation controller with it's linked view controller.
I'm guessing I need a UIview to hold the video to be played in on this page?
Is there someone who can help a new guy out?
Thanks
Firstly change your launch screen storyboard to Main storyboard from project settings in General tab.
Create one view controller with following name and write code to implement AVPlayer to play video.
import UIKit
import AVFoundation
class VideoLaunchVC: UIViewController {
func setupAVPlayer() {
let videoURL = Bundle.main.url(forResource: "Video", withExtension: "mov") // Get video url
let avAssets = AVAsset(url: videoURL!) // Create assets to get duration of video.
let avPlayer = AVPlayer(url: videoURL!) // Create avPlayer instance
let avPlayerLayer = AVPlayerLayer(player: avPlayer) // Create avPlayerLayer instance
avPlayerLayer.frame = self.view.bounds // Set bounds of avPlayerLayer
self.view.layer.addSublayer(avPlayerLayer) // Add avPlayerLayer to view's layer.
avPlayer.play() // Play video
// Add observer for every second to check video completed or not,
// If video play is completed then redirect to desire view controller.
avPlayer.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 1) , queue: .main) { [weak self] time in
if time == avAssets.duration {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
self?.navigationController?.pushViewController(vc, animated: true)
}
}
}
//------------------------------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
}
//------------------------------------------------------------------------------
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.setupAVPlayer() // Call method to setup AVPlayer & AVPlayerLayer to play video
}
}
Main.Storyboard:
Project Launch Screen File:
See following video also:
https://youtu.be/dvi0JKEpNTc
You have to load a video on launchvideoVC, like below way in swift 4 and above
import AVFoundation
import AVKit
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
initVideo()
}
func initVideo(){
do {
try AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(error)
}
let path = Bundle.main.path(forResource: "yourlocalvideo", ofType:"mp4");
player = AVPlayer(url: NSURL(fileURLWithPath: path!) as URL)
NotificationCenter.default.addObserver(self, selector: #selector(launchvideoVC.itemDidFinishPlaying(_:)), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
DispatchQueue.main.async(execute: {() -> Void in
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.view.bounds
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = 1
self.view.layer.addSublayer(playerLayer)
self.player?.seek(to: CMTime.zero)
self.player?.play()
})
}
#objc func itemDidFinishPlaying(_ notification: Notification?) {
//move to whatever UIViewcontroller with a storyboard ID of "menu"
}
I think it's may help you.
Happy coding :)
First you make a new view controller with view and change your launch screen storyboard to Main storyboard from project settings in General tab.
And also add your video in folder.
Then just add my below code to your launch screenView controller:
import UIKit
import MediaPlayer
import AVKit
class LaunchViewController: UIViewController {
fileprivate var rootViewController: UIViewController? = nil
var player: AVPlayer?
var playerController = AVPlayerViewController()
override func viewDidLoad() {
super.viewDidLoad()
showSplashViewController()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func playVideo() {
let videoURL = NSURL(string: "videoplayback")
player = AVPlayer(url: videoURL! as URL)
let playerController = AVPlayerViewController()
playerController.player = player
self.addChildViewController(playerController)
// Add your view Frame
playerController.view.frame = self.view.frame
// Add subview in your view
self.view.addSubview(playerController.view)
player?.play()
}
private func loadVideo() {
//this line is important to prevent background music stop
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
} catch { }
let path = Bundle.main.path(forResource: "videoplayback", ofType:"mp4")
let filePathURL = NSURL.fileURL(withPath: path!)
let player = AVPlayer(url: filePathURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.frame
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = -1
self.view.layer.addSublayer(playerLayer)
player.seek(to: kCMTimeZero)
player.play()
}
func showSplashViewControllerNoPing() {
if rootViewController is LaunchViewController {
return
}
loadVideo()
}
/// Simulates an API handshake success and transitions to MapViewController
func showSplashViewController() {
showSplashViewControllerNoPing()
delay(6.00) {
self.showMenuNavigationViewController()
}
}
public func delay(_ delay:Double, closure:#escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
/// Displays the MapViewController
func showMenuNavigationViewController() {
guard !(rootViewController is Home) else { return }
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nav = storyboard.instantiateViewController(withIdentifier: "homeTab") as! Home
nav.willMove(toParentViewController: self)
addChildViewController(nav)
if let rootViewController = self.rootViewController {
self.rootViewController = nav
rootViewController.willMove(toParentViewController: nil)
transition(from: rootViewController, to: nav, duration: 0.55, options: [.transitionCrossDissolve, .curveEaseOut], animations: { () -> Void in
}, completion: { _ in
nav.didMove(toParentViewController: self)
rootViewController.removeFromParentViewController()
rootViewController.didMove(toParentViewController: nil)
})
} else {
rootViewController = nav
view.addSubview(nav.view)
nav.didMove(toParentViewController: self)
}
}
override var prefersStatusBarHidden : Bool {
switch rootViewController {
case is LaunchViewController:
return true
case is Home:
return false
default:
return false
}
}
}

Sync video in AVPlayerLayer and AVPlayerViewController

I'm working with AVPlayerto show the playing video using a URL in it. There are 2 parts to it:
1. Firstly, I've embedded the AVPlayer to a view's sublayer using AVPlayerLayer, i.e.
var player: AVPlayer?
func configure() {
let urlString = "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
if let url = URL(string: urlString) {
self.player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()
}
}
The above code is working fine and the video is playing.
2. Secondly, on a UIButton tap, I'm presenting a AVPlayerViewController using the same AVPlayer instance that I created earlier, i.e.
#IBAction func onTapVideoButton(_ sender: UIButton) {
self.player?.pause()
let controller = AVPlayerViewController()
controller.player = self.player
self.present(controller, animated: true) {
self.player?.play()
}
}
The problem I'm facing here is, after the AVPlayerViewController opens, the video stops playing but the audio still plays on.
What I want is to sync the video in both 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.

Remote mp3 file taking a lot of time to play in swift ios

i'm in trouble. i want to play a remote mp3 file in my app. but mp3 file taking a lot of time (approx 5-6 minute) to play. why ?
Anyone can suggest what should i do ?
import UIKit
import AVFoundation
class TestViewController: UIViewController, AVAudioPlayerDelegate {
var player:AVAudioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func play(sender: AnyObject) {
let url = "http://www.example.com/song.mp3"
let fileURL = NSURL(string: url.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()))
let soundData = NSData.dataWithContentsOfURL(fileURL, options: nil, error: nil)
var error: NSError?
self.player = AVAudioPlayer(data: soundData, error: &error)
if player == nil
{
if let e = error
{
println(e.localizedDescription)
}
}
player.volume = 1.0
player.delegate = self
player.prepareToPlay()
player.play()
}
}
Thanks in advance.
Use AVPlayer instead of AVAudioPlayer for streaming audio. It can be as simple as this --
let urlString = "http://www.example.com/song.mp3"
let url = NSURL(string: urlString)
var avPlayer = AVPlayer(URL: url)
avPlayer.play()
You can also set an observer on the AVPlayer.status property to manage its changing status. Check out:
https://stackoverflow.com/a/13156942/2484290 (objective c)
And of course the AVPlayer docs are here:
https://developer.apple.com/LIBRARY/ios/documentation/AVFoundation/Reference/AVPlayer_Class/index.html#//apple_ref/occ/cl/AVPlayer
Try using my example where you can work with the cache and the remote file.
player.automaticallyWaitsToMinimizeStalling = false // by default true, so It is important to solve your problem in a mp3 file remotely
This feature is available iOS 10.0+
var player: AVPlayer!
enum AudioType: String {
case remote
case cache
}
#IBAction func remotePressed(_ sender: Any) {
playAudio(type: .remote, fileURL: "http://www.example.com/song.mp3")
}
#IBAction func cachePressed(_ sender: Any) {
if let fileURL = Bundle.main.path(forResource: "jordan", ofType: "mp3") {
playAudio(type: .cache, fileURL: fileURL)
}
}
private func playAudio(type: AudioType, fileURL: String) {
let url = type == .cache ? URL.init(fileURLWithPath: fileURL) : URL.init(string: fileURL)
let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
player = AVPlayer(playerItem: playerItem)
player.automaticallyWaitsToMinimizeStalling = false//It's important
player.play()
}
Its may be late.
Yes. what #lemon lime pomelo has said. you should use AVPlayer instead of AVAudioPlayer.
Also you can use MediaPlayer.
import UIKit
import MediaPlayer
#IBAction func playSong(sender: AnyObject) {
print("Song")
self.playAudio("yourAudioURL.mp3")
}
func playAudio(URL:String){
let movieurl:NSURL? = NSURL(string: "\(URL)")
if movieurl != nil {
self.movie = MPMoviePlayerViewController(contentURL: movieurl!)
}
if self.movie != nil {
self.presentViewController(self.movie!, animated: true, completion: nil)
self.movie?.moviePlayer.play()
}
}
It is slow because your program downloads all the song before starting playing

Resources