I need to observe AVPlayer.status change.
I have an AVPlayer instance and a context variable
private var lastPlayer : AVPlayer?
private var playerStatusContext = 0
After I've set up the AVPlayer instance I add an observer like so:
// KVO status property
self.lastPlayer!.addObserver(self, forKeyPath: "status", options: [.new, .initial], context: &playerStatusContext)
Then I've overridden observeValue function like so:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
let status : AVPlayerStatus? = change?[.newKey] as? AVPlayerStatus
if(status != nil && context == &playerStatusContext)
{
// DO MY STUFF
}
}
The problem is that change is either 0 key/value dictionary or some (whatever that means) and my local status constant is always nil, hense I can't do my stuff.
Maybe I am converting change to AVPlayerStatus wrongly? Please help. Thanks.
Well, it seems like cast like this
let status : AVPlayerStatus? = change?[.newKey] as? AVPlayerStatus
does not work. The app crashed when tried checking change for nil and then force unwrap it. Using raw values helped:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
// Check status
if keyPath == "status" && context == &playerStatusContext && change != nil
{
let status = change![.newKey] as! Int
// Status is not unknown
if(status != AVPlayerStatus.unknown.rawValue)
{
// DO STUFF!!!
}
}
}
I am not sure this is the best way however.
Related
I had asked this question regarding some Apple code and making it work.
I have looked here but the answer there does not solve my problem
Although I solved that problem I am not getting the bellow error on the line shown.
Thread 5: Simultaneous accesses to 0x10b883638, but modification requires exclusive access
private var playerItemContext = 0
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// Only handle observations for the playerItemContext
print("jdslfhjkfdhaldfahjkflhajfldashkjfdshkjlas")
guard context == &playerItemContext else {
super.observeValue(forKeyPath: keyPath, of: object,change: change, context: context)
return
}
...
Why is this and how can I fix this?
Try this code:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// Only handle observations for the playerItemContext
guard context == &P2SheetViewController.playerStatusContext else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
return
}
if keyPath == #keyPath(AVPlayer.status) {
let status: AVPlayer.Status
if let statusNumber = change?[.newKey] as? NSNumber {
status = AVPlayer.Status(rawValue: statusNumber.intValue)!
} else {
status = .unknown
}
//Switch over status value
switch status {
case .readyToPlay:
break
// Player item is ready to play
case .failed:
print(".UKNOWN")
break
// Player item failed. See error.
case .unknown:
print(".UKNOWN")
break
// Player item is not yet ready.
#unknown default: //new jul 17
print(".UKNOWN")
break
}
}
}
Hope it helps
I've got an issue with AVPlayer. The case is: I've got one function, that sets the rate of AVPlayer
func applyEffect(effectNumber : Int) {
switch effectNumber {
case 0:
self.AvPlayer.setRate(Float(1.0), time: kCMTimeInvalid, atHostTime: kCMTimeInvalid)
case 1:
self.AvPlayer.setRate(Float(1.5), time: kCMTimeInvalid, atHostTime: kCMTimeInvalid)
case 2:
self.AvPlayer.setRate(Float(0.5), time: kCMTimeInvalid, atHostTime: kCMTimeInvalid)
default:
self.AvPlayer.setRate(Float(1.0), time: kCMTimeInvalid, atHostTime: kCMTimeInvalid)
}
}
For every response I download audio file into AVPlayerItem and save them as array. I've got an observer for item which calls applyEffect function if AVPlayerItem.status is changed, otherwise it applies effect immediately:
if AVCurrentItem.status != AVPlayerItemStatus.readyToPlay {
AVCurrentItem.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)
pushMessageLoading()
} else {
self.applyEffect(effectNumber: currentEffect)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "status" {
popMessageLoading()
self.applyEffect(effectNumber: currentEffect)
AVCurrentItem.removeObserver(self, forKeyPath: "status")
}
}
The AVPlayer property "automaticallyWaitsToMinimizeStalling" is set to false during the initialisation. So, first time audio plays on a requested rate, but all times after - on rate like 1.0. What's the problem?
The root of evil is simple: I've used AVPlayer.play which equals to AVPlayer.rate = 1.0, instead of this you need to use AVPlayer.rate = desired value
I am trying to track changes to the map orientation using the camera heading property.
// Register for notifications of changes to camera
if let camera = self.mapView?.camera {
self.camera = camera
camera.addObserver(self, forKeyPath: "heading", options: NSKeyValueObservingOptions.new, context: &MapViewController.myContext)
}
...
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &MapViewController.myContext {
if keyPath == "heading" {
if let heading = change?[NSKeyValueChangeKey.newKey] as? CLLocationDirection {
self.heading = heading
}
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
The camera property mode is copy so you are not observing the current camera instance just one that was copied when you called the getter. You need to observe key path of #"camera.heading" or #"camera" on the map view and hope that a new camera object is set internally when the heading changes.
I'm migrating an app to Swift 3 but Xcode is throwing an error with this function:
The error is at the case condition ("contentSize", MyObservationContext)
i'm doing this for update the content size of a uiwebview
var MyObservationContext = 0
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else {
super.observeValue(forKeyPath: nil, of: object, change: change, context: context)
return
}
switch (keyPath, context) {
case("contentSize", MyObservationContext):
webviewHeightConstraint.constant = TextoHtml.scrollView.contentSize.height
default:
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
I'm open for suggestions, thanks.
The case needs to be
case("contentSize", .some(&MyObservationContext)):
.some is to make sure the context is not nil
& gets the pointer to MyObservationContext so it can compare a pointer to a pointer.
I'm building a camera app, and I'm trying to expose the current exposure duration to the user. Since this value constantly changes until manually set, I need to use kvo to stream the values to the user. I've successfully done this with the ISO, and can observe changes to the exposureDuration, but cannot coerce the new value to a CMTime object (which is what exposureDuration is). Below is the code I'm using to try and accomplish this:
override init() {
super.init()
captureDevice = self.selectCamera()
captureDevice?.addObserver(self, forKeyPath: "ISO", options: .New, context: &isoContext)
captureDevice?.addObserver(self, forKeyPath: "exposureDuration", options: .New, context: &shutterContext)
}
deinit {
captureDevice?.removeObserver(self, forKeyPath: "ISO")
captureDevice?.removeObserver(self, forKeyPath: "exposureDuration")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
let newValue = change?[NSKeyValueChangeNewKey]
if context == &isoContext {
store.iso.value = newValue as! Float
} else if context == &shutterContext {
// The app crashes at this line.
// Thread 1: EXC_BREAKPOINT (code=1, subcode=0x100091670)
// newValue is "AnyObject" in the debug area
store.shutterSpeed.value = newValue as! CMTime
}
}
Am I doing something wrong, or is this a legitimate bug that I need to file with apple?
exposureDuration's newValue is not CMTime, but NSValue.
This is fixed code(swift3).
store.shutterSpeed.value = (newValue as! NSValue).timeValue