Strange behavior of AVPlayer - ios

I noticed a very odd behavior when working with AVPlayer from AVFoundation framework. I'm trying to stream (play) some mp3 radio file from internet.
So I created a new project in Xcode and added the following code to the default ViewController:
import UIKit
import AVFoundation
class ViewController: UIViewController {
let player: AVPlayer = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
let asset = AVAsset(url: URL(string: "https://rfcmedia.streamguys1.com/Newport.mp3")!);
let playerItem = AVPlayerItem(asset: asset);
player.replaceCurrentItem(with: playerItem)
player.play();
}
}
There's nothing particularly interesting going on there. I just create an AVPlayerItem instance with the radio station URL and give it to AVPlayer and then ask the player object to play the song and it works just as expected.
However if I remove the player object instantiation from the class block and put it in the viewDidLoad method the code simply doesn't work (doesn't play anything) yet it doesn't crash or spit out any kinds of errors or warning.
import UIKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let player: AVPlayer = AVPlayer()
let asset = AVAsset(url: URL(string: "https://rfcmedia.streamguys1.com/Newport.mp3")!);
let playerItem = AVPlayerItem(asset: asset);
player.replaceCurrentItem(with: playerItem)
player.play();
}
}
I can't understand this behavior. Why is that happening? Is it limited to AVPlayer or I should watch out for cases like this?

As Andrea Mugnaini explained in the comments to my post, my second example is going to cause an immediate deallocation by the ARC (Automatic Reference Counting) as we don't keep a strong reference to the AVPlayer object.
This is a common pitfall of developers new to Apple's ecosystem.

Related

AVPlayer not loading stream url but browser loads it no problem

i need some help with playing stream on really simple ios app which i've made but i can load the stream into the browser and there won't be any problem, but when i try to load it into the ios app its only loading and i don't know what i did wrong.
url of the stream: https://tv1.cdn.netbadgers.com
ViewController.swift
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func playBroadcastClicked(_ sender: Any) {
let videoURL = URL(string: "https://tv1.cdn.netbadgers.com")
let player = AVPlayer(url: videoURL!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated:true) {
playerViewController.player!.play()
}
}
}
You url is not pointing to a video file, it is actually a HTML page with a video which will never play in the iOS AVPlayer.
Find the direct link to the video, also, make sure the video is in a supported format. If you want a quick solution, just add a WKWebView to your ViewController and then load your url.

Customize AVPlayerViewController

I'm using AVPlayerViewController to play video in my application,
I don't want to make the player from scratch.
is there a way to add some other extra UI components and functionality to the AVPlayerViewController?
class AVKitViewController: AVPlayerViewController {
private let url =
URL(string:
"https://www.digikala.com/mag/wp-content/uploads/2019/09/Call-of-Duty-Modern-Warfare-Story-Trailer.mp4")
override func viewDidLoad() {
super.viewDidLoad()
let video = AVPlayer(url: url!)
player = video
player?.play()
}
}
It looks like there is no way to do such thing. the only way possible, is to use AVFoundation to create our own AVPlayer from scratch.

AVPlayerLooper not looping my local video

I'm trying to get a video to play in my macOS project and I want it to continuously loop. I was able to get it to play once but I can't figure out how to get it to loop. I'm using the AVPlayerLooper but seem to be messing up somewhere. Here is my code:
import Cocoa
import AVKit
import AVFoundation
class ViewController: NSViewController {
#IBOutlet weak var videoPlayer: AVPlayerView!
override func viewDidLoad() {
//var loop: AVPlayerLooper?
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "Background", ofType:"mp4") else {
debugPrint("Not found")
return
}
let asset = AVAsset(url: URL(fileURLWithPath: path))
let item = AVPlayerItem(asset: asset)
let queuePlayer = AVQueuePlayer(playerItem: item)
let loop = AVPlayerLooper(player: queuePlayer, templateItem: item)
videoPlayer.player = queuePlayer
videoPlayer.player?.play()
}
}
I also have an AVPlayerView in my storyboard that I have connected to my viewcontroller.
It builds and runs fine but when I run it nothing shows up. I just get a black screen.
Any help is appreciated. Thanks!
AVPlayerLoop is sort of a "top level" object that manages both the player and the player item. So you have to keep it around, e.g. in a custom property of the view controller, or it will be released before it can really do anything
Since AVPlayerLoop manages the items in the AVQueuePlayer, initialize the queue player without passing in the player item

How to stream audio for only a known duration using swift

I'm using AVPlayer (I don't need to, but I wanna stream it and start playing as soon as possible) to play an m4a file (it's an iTunes audio preview). Only I only want it to play a part of that file.
I'm able to set a start time but not an end time.
Using a timer is not working because I'm using URL as a http address. I'm playing as it loads, without downloading the file.
I also saw solutions in Objective-C to use KVO to know when music starts playing but I'm thinking this is not the best approach since I'm using swift and also because of glitches that may occur so the song will not stop at the right moment.
You can add a addBoundaryTimeObserverForTimes to your AVPlayer as follow:
update: Xcode 8.3.2 • Swift 3.1
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player: AVPlayer!
var observer: Any!
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: "https://www.example.com/audio.mp3") else { return }
player = AVPlayer(url: url)
let boundary: TimeInterval = 30
let times = [NSValue(time: CMTimeMake(Int64(boundary), 1))]
observer = player.addBoundaryTimeObserver(forTimes: times, queue: nil) {
[weak self] time in
print("30s reached")
if let observer = self?.observer {
self?.player.removeTimeObserver(observer)
}
self?.player.pause()
}
player.play()
print("started loading")
}
}

AVPlayer Crashes on Device When Dismissing View

I'm having an issue with the AVPlayer. I've tried many solutions but it still crashes after I move to a different view.
class GuideVideo : BaseViewController{
var avPlayer: AVPlayer?
var avPlayerLayer: AVPlayerLayer?
override func viewDidLoad() {
super.viewDidLoad()
generateVideo()
}
func generateVideo () {
let videoURLWithPath = data["VideoUrl"]
let videoFilePath = NSURL(string: videoURLWithPath!)
let avAsset: AVAsset = AVAsset.assetWithURL(videoFilePath) as! AVAsset
let avPlayerItem = AVPlayerItem(asset: avAsset)
avPlayer = AVPlayer(playerItem: avPlayerItem)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer!.frame = self.videoView.bounds
self.videoView.layer.addSublayer(avPlayerLayer)
avPlayer!.play()
}
I've also tried removing the observers from it since I assume the crash is related to a nil observer.
override func viewWillDisappear(animated: Bool) {
dispatch_async(dispatch_get_main_queue(),{
if self.avPlayerLayer != nil {
self.avPlayerLayer!.player.pause()
NSNotificationCenter.defaultCenter().removeObserver(self.avPlayerLayer!)
self.avPlayerLayer!.removeFromSuperlayer()
self.avPlayerLayer = nil
}
self.avPlayer!.pause()
self.avPlayer = AVPlayer()
})
super.viewWillDisappear(animated)
}
Nothing works and the crash provides no data. Either it crashes without indicating a line or a general
Thread 1: EXC_BAD_ACCESS
* Import thing to note is that this crash only happens on the iPhone 6/6+. Our iPhone 5C handles the class well.
* I only get the crash after moving to another view controller or a different navigation stack, but a few seconds after the view had been dismissed.
Thank you, been sitting on this for the better part of 2 days now.
EDIT: The issue is apparently related to the SWReveal. It deallocates instances before their lifecycle is over.
Accepted the best solution, but the problem is related to SWReveal.
Try using the MPMoviePlayerController instead of the AVPlayer, for simple solutions, the MPMoviePlayerController has an easier API.
make the MPMoviePlayerController a global instance making it accessible through the whole app, and then initialize it when you need it.
Try playing the videos locally first and check if it solves the problem

Resources