I just switched from AVAudioPlayer to AVPlayer and I'm going through my old functions and making the appropriate adjustments. The AVPlayer is being used to play remote audio files using URL's. When I select a file to play and pause everything works perfectly. However, when I want to resume playing the paused file, the player won't play even though I see the play function is being called. I set up an observer to know when the player is done playing so the play/pause button can toggle and what I noticed is that the observer is getting called after I hit pause. This shouldn't happen since the player isn't done playing right? Anyways, I set breakpoints and everything is getting called correctly. Any idea why the AVPlayer won't resume playing after being paused?
var playerItem: AVPlayerItem?
var newPlayer: AVPlayer?
var trackIDplaying: Int?
func playPausePressed(_ sender:UIButton)
{
if let selectedTrackID = trackIDplaying
{
do
{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
if selectedTrackID == track.trackId
{
//if playing, then pause. If not playing, then play
if (self.newPlayer!.rate != 0)
{
self.newPlayer!.pause()
self.isPaused = true
}
else
{
if self.newPlayer!.currentItem != nil
{
self.newPlayer!.play()
print(self.newPlayer!.currentItem)
}
}
}
else
{
//If song is playing, switch to new song
let trackURL = URL(string: track.preSignedURL!)
trackIDplaying = track.trackId
self.playerItem = AVPlayerItem(url: trackURL!)
NotificationCenter.default.addObserver(self, selector: #selector((HomeController.playerDidFinishPlaying)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.playerItem)
self.newPlayer = AVPlayer(playerItem: self.playerItem!)
self.newPlayer!.play()
}
}
catch let error1 as NSError
{
error = error1
self.newPlayer = nil
}
}
else
{
do
{
//play selected song if no other songs are playing
let trackURL = URL(string: track.preSignedURL!)
print(trackURL!)
self.playerItem = AVPlayerItem(url: trackURL!)
NotificationCenter.default.addObserver(self, selector: #selector((HomeController.playerDidFinishPlaying)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.playerItem)
self.newPlayer = AVPlayer(playerItem: self.playerItem!)
self.newPlayer!.play()
self.trackIDplaying = track.trackId
}
}
if let err = error
{
print("audio player error \(err.localizedDescription)", terminator: "")
}
}
func playerDidFinishPlaying(note: NSNotification) {
guard let indexPath = self.playingAudioIndexPath, let cell = self.audioTable.cellForRow(at: indexPath) as? AudioCell else {
return;
}
cell.playButton.isSelected = false
self.playingAudioIndexPath = nil
}
try adding observer in only viewDidLoad() or viewWillAppear() adding observer should only done in one time for these kind of problems.
Still you have any problem feel free to ask me.
Related
I have one countdown video to be played before quiz which is kept in bundle. The Video gets played properly in iPhone 6 [iOS 11.2] and quiz starts, but when I run the app in iPhone 4s [iOS 9.3] the video starts with a slight delay and at the end of video, the screen gets freeze for sometime and then the Quiz starts.
func playVideo()
{
guard let path = Bundle.main.path(forResource: "countDown", ofType:"mp4") else {
debugPrint("Video not found")
return
}
let item = AVPlayerItem(url: URL(fileURLWithPath: path))
player = AVPlayer.init(playerItem: item)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.viewVideo.bounds
player?.volume = 0
self.viewVideo.isHidden = false
player?.play()
self.viewVideo.layer.addSublayer(playerLayer)
let resetPlayer = {
self.player?.pause()
self.viewVideo.isHidden = true
// Moves to quiz
self.performSegue(withIdentifier: "toFinalQuiz", sender: self)
}
playerObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: nil) {
notification in
resetPlayer()
}
}
Try adding this line after the comment,
// Moves to quiz
DispatchQueue.main.async {
self.performSegue(withIdentifier: "toFinalQuiz", sender: self)
}
Let me know if this doesn't work...
For the delay at the start, I'd recommend to keep the video loaded and ready to play beforehand. So that you can being playing the video using just player?.play() when you need it.
I have an application that contains videos that play automatically in an UIImageView in a UITableView when the cell is visible, and all I am trying to do is allow the application to know when the video has been played for three seconds. I wrote this code.
class PostCell: UITableViewCell {
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
var post: Post? {
didSet {
updateView()
}
}
func updateView() {
self.viewcount()
if let videoUrlString = post?.videoUrl, let videoUrl = URL(string: videoUrlString) {
player = AVPlayer(url: videoUrl)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = postImageView.frame
playerLayer?.frame.size.width = postImageView.frame.size.width
playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.contentView.layer.addSublayer(playerLayer!)
player?.play()
}
func viewcount() {
if let currentitem = player?.currentItem {
if currentitem.currentTime() == CMTimeMake(3, 1) {
print ("VIDEO PLAYED FOR THREE SECONDS")
}
}
}
}
but it is not printing out my message once the video starts playing. I have searched the web for help but couldn't find anything on this subject. So could anyone please help with my issue and tell me what I am doing wrong ?
You are searching for observer of player here is how you can check and track the current position of AVPlayer
Here is function that is adding observer to cell
private func addObserversForVideoPlayer(cell:CustomCell) {
let observer = cell.player?.addPeriodicTimeObserver(forInterval: CMTime.init(seconds: 1, preferredTimescale: 1), queue: .main, using: {[weak self,weak cell] (time) in
guard let cell = cell else {return}
if cell.player?.currentItem?.status == .readyToPlay {
// print("Inside Will DISPLAY\(cell.video.currentTime)")
let timeDuration : Float64 = CMTimeGetSeconds((cell.player?.currentItem?.asset.duration)!)
cell.lblDuration.text = self?.getDurationFromTime(time: timeDuration)
let currentTime : Float64 = CMTimeGetSeconds((cell.player?.currentTime())!)
cell.lblStart.text = self?.getDurationFromTime(time: currentTime)
cell.slider.maximumValue = Float(timeDuration.rounded())
cell.slider.value = Float(currentTime.rounded())
}
})
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: cell.player?.currentItem, queue: .main, using: {[weak cell,weak self] (notification) in
if cell?.player != nil {
cell?.player?.seek(to: kCMTimeZero)
cell?.player?.play()
}
})
}
so that addPeriodicTimeObserver will notify you when the player start playing.
And NSNotification.Name.AVPlayerItemDidPlayToEndTime will notify you when your AVPlayer stops.
Note1: If your cell.player?.currentItem is nil while you are adding AVPlayerItemDidPlayToEndTime it will be cause bug see this One AVPlayer's AVPlayerItemDidPlayToEndTime action executed for all Currently playing videos , If . you don't need it don't add it :)
Note2: You should keep observer so after time you can remove it so that can not take extra load on memory
Hope it is helpful
Try calling the view count after player had started playing
func updateView() {
/// Not here Because at this time player current item is not initiated yet
/// if you use Breakpoints in viewCount code you will see it won't enter
/// in if condition created
self.viewcount() /// Comment this line
if let videoUrlString = post?.videoUrl, let videoUrl = URL(string: videoUrlString) {
player = AVPlayer(url: videoUrl)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = postImageView.frame
playerLayer?.frame.size.width = postImageView.frame.size.width
playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.contentView.layer.addSublayer(playerLayer!)
/// Player is initiated with a item to play
player?.play()
/// Call current time here
/// Now it will Enter in if Condition
/// Also try using else statement so you know Do control enter in if or in Else
self.viewcount()
}
func viewcount()
{
if let currentitem = player?.currentItem
{
///Yes Player have a item whose time can be Detected
if currentitem.currentTime() == CMTimeMake(3, 1)
{
print ("VIDEO PLAYED FOR THREE SECONDS")
}
}
else
{
/// Check do Control reach here in case 1 When you are calling before player.play()
}
}
I have Imported AVFoundation and added Audio to my app but if the App enters the background mode the audio doesn't stop
var backgroundMusic: AVAudioPlayer? = {
guard let url = Bundle.main.url(forResource: "Mining by Moonlight", withExtension: "mp3") else {
return nil
}
do {
let player = try AVAudioPlayer(contentsOf: url)
player.numberOfLoops = -1
return player
} catch {
return nil
}
}()
In viewDidLoad is the code:
backgroundMusic?.prepareToPlay()
backgroundMusic?.play()
Can anyone help me?
make sure that Audio and AirPlay is not enabled in your background tasks
Use NSNotificationCenter in your controller class
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector(“stopSong:"), name:UIApplicationDidEnterBackgroundNotification, object: nil)
If Your app is running in background than use this method
func stopSong(notification : NSNotification) {
backgroundMusic?.stop()
}
I'm creating my first app. I have an app with music playing in the background with the following code:
var backgroundMusicPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
//background Music
func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(filename)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}
playBackgroundMusic("Starship.wav")
}
So what should I do in order to stop/mute the background music when I switch to another ViewController? Should I do this my FirstViewController or SecondViewController?
Obviously, I don't want the sound to be off in the SecondViewController as I have other stuff that will be playing there.
To mute sound I simply mute the volume.
backgroundMusicPlayer.volume = 0
and set it to normal if I want sound
backgroundMusicPlayer.volume = 1
If you just want to pause music you can call
backgroundMusicPlayer.pause()
To resume you call
backgroundMusicPlayer.resume()
If you want to stop music and reset it to the beginning you say this
backgroundMusicPlayer.stop()
backgroundMusicPlayer.currentTime = 0
backgroundMusicPlayer.prepareToPlay()
Did you also consider putting your music into a singleton class so its easier to play music in your different viewControllers.
Not sure this is what you are looking for as your question is a bit vague.
I am playing video using AVPlayer. When I go in background player is paused and when it is brought in foreground player is played. But still video is not appearing though it contains URL in its current Item. I am stuck and not getting and solution.Please help me to resolve. Thanks in advance.
Below code is used to play initially:-
let item1 = AVPlayerItem.init(URL: NSURL(string:path))
player = AVPlayer(playerItem: item1)
layer?.player = player;
player?.play()
Below code is used to pause and resume:-
func pausePlayerOnBackgroundAppearance()
{
if(player != nil)
{
player?.pause()
}
}
func resumePlayerOnForegroundAppearance()
{
if(player != nil)
{
player?.play()
}
}
If I am seeking some time to play video at some time where it was paused before then also it is not playing video
func pausePlayerOnBackgroundAppearance()
{
if(player != nil){
let currentItem:AVPlayerItem = player!.currentItem
currentTime = CMTimeGetSeconds(currentItem.currentTime())
player?.pause()
}
}
func resumePlayerOnForegroundAppearance()
{
if(player != nil){
player!.seekToTime(CMTimeMake(Int64(currentTime!), 1))
player?.play()
}
}