Detect when AvPlayer is stopped - ios

I'm using the AVPlayer class to read streams.
I have to monitor playback.
Here is my question : Is it possible to detect when the player is stopped by the user ?
I looked at MPMoviePlayerController. If the user stopped the video, this controller sends a notification : MPMovieFinishReasonUserExited. Is there an equivalent ?

You can monitor rate property by adding observer on the player for key rate.
A value of 0.0 means pauses the video, while a value of 1.0 play at the natural rate of the current item.
Apple documentation and this topic.
Hope this helps.

here's the swift 3 code for #Thlbaut's answer
self.avPlayer?.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions(rawValue: 0), context: nil)
then
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "rate" {
if let playRate = self.avPlayer?.rate {
if playRate == 0.0 {
print("playback paused")
} else {
print("playback started")
}
}
}
}

Related

OpenTok Session Volume Change Doesn't work with MPVolumeView

Hi I've been working on using Open Tok For a streaming session
I used MPVolumeView to vary system Sound.
Issue: MPVolumeView does work changing system Volume but that doesn't affect my OTSession Volume
Expected: I want to change "outputVolume" of AVAudioSession running with OpenTok
Code:
func startObservingVolumeChanges() {
avAudioSession.addObserver(self, forKeyPath: Observation.VolumeKey, options: [.initial, .new], context: &Observation.Context)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &Observation.Context {
if keyPath == Observation.VolumeKey, let volume = (change?[NSKeyValueChangeKey.newKey] as? NSNumber)?.floatValue {
print("Volume: \(volume)")
}
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
func stopObservingVolumeChanges() {
avAudioSession.removeObserver(self, forKeyPath: Observation.VolumeKey, context: &Observation.Context)
}
As soon as I change volume using iOS Volume buttons "Volume does get print from 0 to 1 range"
when I change value using Slider "Volume does get print from 0 to 1 range" but doesn't lower the sound of Session
Please help.
Discussed with OpenTok Developers. This is not supported attaching screenshot for same,

AVPlayer on selected rate only once

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

Progress of playbackLikelyToKeepUp

When using an AVPlayer, is there a way to get the progress of playbackLikelyToKeepUp? I was thinking I could look at loadedTimeRanges to see how much has been buffered so far, but from what I understand, the playbackLikelyToKeepUp property is some internally made prediction and does not provide a value of how much data is needed for it to be true.
To put this into perspective, what I'm trying to do is to have a progress view that reaches 100% just as the video starts playing.
To begin playing when AVPlayer will play continuously, you can observe the Key-Value changes on the playbackLikelyToKeepUp, like this:
let PlayerKeepUp = "playbackLikelyToKeepUp"
var isPlayerReady:Bool = false
and then in your Initialiser you add:
// adding the Observers for Status:
self.player?.currentItem?.addObserver(self, forKeyPath: PlayerKeepUp, options: ([NSKeyValueObservingOptions.New, NSKeyValueObservingOptions.Old]), context: &PlayerItemObserverContext)
And Finally, to track when the player is ready to play without getting stuck:
// MARK: KVO Observing Methods:
override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
switch (keyPath, context) {
case (PlayerKeepUp as String, &PlayerItemObserverContext):
if(self.player.currentItem?.playbackLikelyToKeepUp == true) {
self.isPlayerReady = true
// HERE YOU FILL UP YOUR PROGRESS VIEW :-)
self.delegate?.playerReady(self.playerURL! as String)
}
break
default:
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
Last but not least, remember to Remove your KVO observance or else you'll crash when de-allocating the player:
deinit {
// Remove observer:
self.player?.currentItem?.removeObserver(self, forKeyPath: PlayerKeepUp, context: &PlayerItemObserverContext)
}
Hope this helps :-)

Detect when camera is auto-focusing

I am working on video application. I want to discard the video frames when camera is autofocusing. During autofocus image captured become blurred and image processing for that frame become bad but once autofocus is done, image processing become excellent. Any body give me solution?
adjustingFocus property.
Indicates whether the device is currently adjusting its focus setting. (read-only)
*Notes: You can observe changes to the value of this property using Key-value observing.
iOS 4.0 and later
https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureDevice_Class/Reference/Reference.html#//apple_ref/occ/instp/AVCaptureDevice/adjustingFocus
Following is a sample code in Swift 3.x.
First a observer should be added to the selected capture device at camera initialization.
captureDevice.addObserver(self, forKeyPath: "adjustingFocus", options: [.new], context: nil)
Then observeValue method is overridden. By accessing the optional value returned by the method, autoFocussing frames can be identified.
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let key = keyPath, let changes = change else {
return
}
if key == "adjustingFocus" {
let changedValue = changes[.newKey]
if (changedValue! as! Bool){
// camera is auto-focussing
}else{
// camera is not auto-focussing
}
}
}
Example on Swift 4+
class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
//#objc var captureDevice: AVCaptureDevice?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.addObservers()
}
func addObservers() {
self.addObserver(self, forKeyPath: "captureDevice.adjustingFocus", options: .new, context: nil)
}
func removeObservers() {
self.removeObserver(self, forKeyPath: "captureDevice.adjustingFocus")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "captureDevice.adjustingFocus" {
print("============== adjustingFocus: \(self.captureDevice?.lensPosition)")
}
} //End of class
Observing adjustingFocus is not working for me. It's always no. And I find this.
Note that when traditional contrast detect auto-focus is in use, the AVCaptureDevice adjustingFocus property flips to YES when a focus is underway, and flips back to NO when it is done. When phase detect autofocus is in use, the adjustingFocus property does not flip to YES, as the phase detect method tends to focus more frequently, but in small, sometimes imperceptible amounts. You can observe the AVCaptureDevice lensPosition property to see lens movements that are driven by phase detect AF.
from Apple
I have not try it yet, I will try and update later.
Edit. I have try it, and confirm this's right.

AVPlayer, notification for play/pause state?

I'm searching for a way to get notified the exact moment when AVPlayer starts playing. There's the "rate" property, but currently I am checking it periodically with an NSTimer to get updates.
I tried KVO, but apparently it's not KVO compliant.
I know that there are events when the player ENDED. But i'm talking about pause here.
I also KVO subscribed to AVPlayerItem's "status", but it's showing me when the HTTP asset has finished caching, no play/pause. I also started collecting all calls of play/pause, requesting an instant UI update afterwards, but it takes some more runloops before AVPlayer really starts playing. I'd just love to update my button instantly.
Why do you say that "rate" is not KVO complaint?
It works for me.
Here is what I did:
- (void)viewDidLoad
{
...
[self.player addObserver:self forKeyPath:#"rate" options:0 context:nil];
}
And then:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"rate"]) {
if ([self.player rate]) {
[self changeToPause]; // This changes the button to Pause
}
else {
[self changeToPlay]; // This changes the button to Play
}
}
}
For iOS 10 onwards You can check new property of AVPlayer timeControlStatus.
if(avPlayerObject.timeControlStatus==AVPlayerTimeControlStatusPaused)
{
//Paused mode
}
else if(avPlayerObject.timeControlStatus==AVPlayerTimeControlStatusPlaying)
{
//Play mode
}
AVPalyer as default observer to track the current duration of the video ,when you pause or resume the video you can get paused time by using one global variable (inside observer update that variable)
CMTime interval = CMTimeMake(1, 1);
//The capture of self here is coming in with your implicit property access of self.currentduration - you can't refer to self or properties on self from within a block that will be strongly retained by self.
//You can get around this by creating a weak reference to self before accessing timerDisp inside your block
__weak typeof(self) weakSelf = self;
self.timeObserverToken = [_player addPeriodicTimeObserverForInterval:interval queue:NULL usingBlock: ^(CMTime time)
{
_currentDuration = (int)CMTimeGetSeconds (_player.currentTime);
if(!_isPlaying)
{
_pausedDuration = _currentDuration;
}
}
If you're targeting iOS 13 and up, you can pull this off elegantly using Combine:
cancellable = myAVPlayerInstance.publisher(for: \.timeControlStatus)
.sink { [unowned self] status in
...
}
where status is any case of AVPlayer.TimeControlStatus
player = AVPlayer(url: URL(fileURLWithPath: path))
player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.new, context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "rate" {
if player.rate > 0 {
print("video started")
}
}
}
in swift
Add an observer to your AVPlayer object's rate value:
player.addObserver(self, forKeyPath: "rate", options: [], context: nil)
And override the method that will be called when the rate changes:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "rate", let player = object as? AVPlayer {
if player.rate == 1 {
print("Playing")
} else {
print("Paused")
}
}
}
Need to add an observer to AVPlayer object's rate value:
player?.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.new, context: nil)
Override below method to observe changes in rate property
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "rate" {
if let status = player?.timeControlStatus {
switch status{
case .paused:
//Paused mode
print("paused")
case .waitingToPlayAtSpecifiedRate:
//Resumed
print("resumed")
case .playing:
//Video Ended
print("ended")
#unknown default:
print("For future versions")
}
}
}
}

Resources