AVPlayer seekToTime -- finished is NO -- Can't get player to play again - ios

I'm using the AVPlayer method:
- (void)seekToTime:(CMTime)time
toleranceBefore:(CMTime)toleranceBefore
toleranceAfter:(CMTime)toleranceAfter
completionHandler:(void (^)(BOOL finished))completionHandler
for scrubbing a video. toleranceBefore and toleranceAfter are both 0.
However, when the user scrubs to within about 5 seconds of the end, on certain videos, finished is always equal to NO.
The player will then be in an inconsistent state where pause and play methods do nothing. The video is frozen at the time passed to seekToTime.
This happens consistently on some videos, and not at all on others.
I have a simple if/else but I don't know what to put in the else to get the player to play again:
[_avPlayer seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC)
toleranceBefore:toleranceBefore
toleranceAfter:toleranceAfter
completionHandler:^(BOOL finished) {
if(finished){
}
else {
// How to get the player to play again??
}
}];
Like I said above, I've tried putting _avPlayer play in there but it doesn't play.

Related

iOS AVAudioPlayer can't REplay when asked to play again at the end (works before and after end of file but not a THIS specific moment)

Sorry for the weird title but AVAudioPlayer acts a bit weird when it comes to playing a sound again, right before the sound that it's playing ends.
I can play an mp3 file when the player is not playing, when the player finished playing and when the player is playing.
I cannot play an mp3 when the song that is currently playing is about to end. This is my code, I documented the important bits.
-(void) play:(int)p{
if([players[p] isPlaying]){
NSLog(#"is playing");
[players[p] setCurrentTime:0.025f]; //when it's playing, set it to the start of the
//file so it starts playing again. Usually works
if(![players[p] isPlaying]){
NSLog(#"is not playing"); //although I just told the player to play again
//it sometimes returns that it in fact does NOT play
//so it's playing in the first if condition but
//not playing in the second if condition
[players[p] prepareToPlay];
[players[p] play]; //from here I try to resurrect the player numerous times
//but everything fails, I have to call the function again to make it work
[players[p] setCurrentTime:0.025f];
if([players[p] isPlaying]){
NSLog(#"playing"); //never gets called; although I tell it to play so many times
//it just won't do
}else {
NSLog(#"NOTplaying");
[players[p] play];
}
} else {
NSLog(#"is still playing");
}
} else {
NSLog(#"not playing at all");
[players[p] play]; } //if not playing, play. Always works
}
I think this might have something to do with how the AVAudioPlayer releases the file or that it's playing but is not playing anyumore in the millisecond after that. (playing during the first if condition but stopped right after that).
The window in which this occurs can easily be reproduced with with a 5sec audio file. Just wait till it's about to end and then click "play" and the method gets executed but nothing's playing. The method works in every other case (not playing and playing but not going to finish playing).

How do I configure AVPlayer to play a video that was recorded at 120fps?

I have a simple app that records video at 120fps, saves it to the documents directory, and then uses an instance of AVPlayer to play it back.
If I set the playback rate to 1.0 (for the AVPlayer instance), the playback is a bit choppy (probably more along the lines of 60fps). However, if I interrupt the playback by pressing the "play" button (which then sets the playback rate to 1.0 again!), it plays back very smoothly.
For instance, this is what occurs when the user presses the play button (and yes, I observe the 'status' property of AVPlayer before allowing the user to play the video):
- (IBAction)playButtonPressed:(id)sender
{
[self.videoPlayer seekToTime:kCMTimeZero];
[self.videoPlayer setRate:1];
self.videoPlayer.volume = 1;
}
And this is my simple player-setup code:
- (void)setUpAVPlayer
{
self.videoURL = [[NSURL alloc] initFileURLWithPath:self.videoURL.path];
self.videoPlayer = [AVPlayer playerWithURL:self.videoURL];
[self.previewLayer setPlayer:self.videoPlayer];
// observe player status
[self.videoPlayer addObserver:self
forKeyPath:#"status"
options:0
context:nil];
}
There really is nothing unusual about what I'm doing. It plays back video fine. It's only the case where the video was recorded at 120fps that there is a playback issue.
(Also, this is running on my iPhone5s since it is the only device that supports 120fps.)

Play audio when already playing

My audio won't play unless its finished playing even if I stop it then play it.So if you play the sound and play it again before the sound clip reaches the end, won't play again. How can I make it play and start from the beginning even if its still playing.
-(void)playSound{
[avplayer stop];
[avPlayer play];
}
As per the documents -
The stop method does not reset the value of the currentTime property to 0. In other words, if you call stop during playback and then call play, playback resumes at the point where it left off.
Set the currentTime property to 0.0 before playing again.
-(void)playSound{
[avplayer stop];
avplayer.currentTime = 0.0;
[avPlayer play];
}

How can I register for when AVPlayer actually starts playing (from external source)

I'm having some trouble with registering WHEN the player is starting to play external videos (over internet) using AVPlayer. Please read the question before suggesting solutions.
I initialize the player like this:
player = [[AVPlayer alloc] initWithURL:[[NSURL alloc] initWithString:#"http://example.com/video.mp4"]];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
[playerLayer setFrame:[videoView bounds]];
[videoView.layer addSublayer:playerLayer];
This adds the player to the view correctly. I have added the following two lines of code to keep track of when the player is ready, and what the status/rate is;
[player addObserver:self forKeyPath:#"rate" options:0 context:nil];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
These two line will call the method - (void)observeValueForKeyPath:.... when something changes with the status or the rate of the AVPlayer.
So far it looks like this:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//To print out if it is 'rate' or 'status' that has changed:
NSLog(#"Changed: %#", keyPath);
if ([keyPath isEqualToString:#"rate"]) //If rate has changed:
{
if ([player rate] != 0) //If it started playing
{
NSLog(#"Total time: %f", CMTimeGetSeconds([[player currentItem] duration]));
// This NSLog is supposed to print out the duration of the video.
[self setControls];
// This method (setControls) is supposed to set play/pause-buttons
// as well as labels for the current and total time of the current video.
}
}
else if ([keyPath isEqualToString:#"status"]) // If the status changed
{
if(player.status == AVPlayerStatusReadyToPlay) //If "ReadyToPlay"
{
NSLog(#"ReadyToPlay");
[player play]; //Start the video
}
}
}
The state of the AVPlayer changes to readyToPlay almost immediately after initializing it, and I then call [player play]. When this happens, the rate changes to 1.00000, meaning it's actually playing at that rate, but the video is now just starting to buffer, not playing. The screen is black, and it takes a couple of seconds, and then it starts playing. The rate, however, indicates it starts playing before it does. The rate stays at 1.00000, not going down to 0 when start-buffering, which makes it very difficult for me to know when the player has enough information to start setting the controls (I.E time stamps etc).
The NSLog() printing out the duration of the video above prints out nan (Not A Number), which leads me to think that the item isn't ready to be played, however, the rate stays at 1.0000 until it has buffered a while, then it will actually play, still with rate at 1.0000.
It does, however, get called twice. The rate "changes" to 1.0000 twice without being anything else in between. In neither calls, the duration of the video is an available variable.
My goal is to fetch the current and total timestamp of the video as fast as possible (I.E 0:00/3:52). This will also be used to register the scrubbing of a slider (for fast-forward etc.).
These values are not ready when the player notifies me it's playing at a rate of 1.0000, twice. If I manually click "play" after a second or so (and call [player play]), then it's working. How can I register to know when the video is ready, not just 'ready to get ready'?
See addBoundaryTimeObserverForTimes:queue:usingBlock: on AVPlayer and this example from Apple.
AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"]];
[player play];
// Assumes a property: #property (strong) id playerObserver;
// Cannot use kCMTimeZero so instead use a very small period of time
self.playerObserver = [player addBoundaryTimeObserverForTimes:#[[NSValue valueWithCMTime:CMTimeMake(1, 1000)]] queue:NULL usingBlock:^{
//Playback started
[player removeTimeObserver:self.playerObserver];
}];
I think the nearest you'll get to is to observe player.currentItem.playbackLikelyToKeepUp

Find out whether AVPlayer is loading or playing mp3 from internet

I would like to use AVPlayer to stream a .mp3 from internet
[[AVPlayer alloc] initWithURL:url];
It all works apart from the fact that I am unable to tell whether player is Loading (buffering) or playing ?
I have tried to use KVO on many properties, but none seem to provide me with a simple way of determining whether the AVPlayer is currently actually playing audio or whether it is loading data. It is driving me nuts.
Thank you for your suggestions in advance
You can detect when buffering is done and playback has started by creating a periodic time observation (using addPeriodicTimeObserverForInterval:queue:usingBlock:) to detect when the play time has started to move. For example:
AVPlayer *player;
// allocate AVPlayer and start playing
__weak PlayerController *blockSelf = self;
id observerToken = [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0, NSEC_PER_SEC)
queue:nil
usingBlock:^ (CMTime time)
{
if (CMTimeGetSeconds(time) > 0.0)
{
// done buffering, should be playing now
[player removeTimeObserver:[blockSelf playerTimeObserverToken]];
[blockSelf setPlayerTimeObserverToken:observerToken:nil];
}
}];
[self setPlayerTimeObserverToken:observerToken]; // Keep a reference to the token for later use in block.

Resources