Resume AVPlayer after forwardPlaybackEndTime - ios

I've created an AVPlayer and set the forwardPlaybackEndTime to make a local video stop at a given timestamp. Sure enough, the video stops at the time I've requested. All good.
Now I want the video to continue when triggered by a user action (touching a button, for example). Unfortunately, I can't seem to make that happen without the video restarting from the beginning.
I'll spare you all of the AVPlayer setup code (which is mostly taken from the AV Foundation Programming Guide), but given these variables:
AVPlayer *avPlayer;
AVPlayerItem *playerItem;
I can set the end time like so:
[playerItem setForwardPlaybackEndTime: CMTimeMake(30, 30)];
To attempt the resume, I've tried this:
[playerItem setForwardPlaybackEndTime: CMTimeMake(30, 30)];
[avPlayer setRate: 1.0];
No dice. I've also tried setting the end time and calling play. No luck. I've tried seekToTime to put the playhead at the place where the video stopped in case that would help. It doesn't.
Can someone please explain how to make this work? Thanks!

Try setting the forwardPlaybackEndTime back to the default value, kCMTimeInvalid then continue to play the video.
[playerItem setForwardPlaybackEndTime: kCMTimeInvalid];
[playerItem seekToTime: CMTimeMake(30, 30) toleranceBefore: kCMTimeZero toleranceAfter: kCMTimeZero];
[avPlayer play];

Related

Multiple AVPlayers ducking?

I have a relatively simple setup involving 1 AVPlayer looping some ambient audio in the background and a second player playing a shorter sound at certain points.
What I've observed is that when the short sound is played, I hear the ambient clip cut out for about a second while there is a staticy pop. It then proceeds to continue playing while the short sound is played at the same time. This only happens on device - it's not noticeable on the sim, which seems to point to a potential performance issue.
I can't quite figure out why the first AVPlayer has this blip. Here is the code for the ambient player:
NSString *path = [[NSBundle mainBundle] pathForResource:kAmbientTrack ofType:#"mp3"];
_ambientPlayer = [AVPlayer playerWithURL:[NSURL fileURLWithPath:path]];
[self.ambientPlayer play];
It's slightly more complex, as I also listen for a notification when it ends and the restart it, but this issue happens even during the initial play before any looping occurs.
So while that is going in the background, I play another clip like so:
self.announcementPlayer = [[AVPlayer alloc] initWithURL:[myObject audioPathUrl]];
[self.announcementPlayer play];
So nothing too unique there - just two AVPlayers playing.
The only other piece of interest is how I set up the audio session when the app launches.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
I'm doing this to allow the user to play music from other apps in the background.
I think my code is pretty straight forward, and really have no idea why I would be getting this blip.
I fixed this somewhat by changing the audio players to be AVAudioPlayers. That resolved the popping in most cases. However, I still get the ducking. Specifically, if I play an AVAudioPlayer and, while it's still playing, I start to load an AVPlayer, it cuts off the AVAudioPlayer for about a second. Here's what I'm talking about:
[myAVAudioPlayer play];
AVPlayer *player = [AVPlayer playerWithURL:[self.currentExercise videoPathUrl]];
The addition of that second line causes the first second or so of myAVAudioPlayer to be silent.

Pre-load an AVPlayer streaming video

I'm trying to play a streaming video with a AVPlayer without any network delay. Fortunately, our app has a progress screen before the screen that plays the video. I'm hoping to use some of the time on this progress screen to pre-load the video so it plays without delay on the next screen.
The most promising approach that I've come up with is to use an AVQueuePlayer to play 2 videos, the first would be a video that I play off screen (so you don't see it) and is silent, so you don't hear it. From what I've read on SO AVQueuePlayer buffers the nth+1 video when the nth video is near completion.
Here's my code to do this:
NSString *blackVideoPath = [[NSBundle mainBundle] pathForResource:#"black10-counting" ofType:#"MOV"];
AVPlayerItem *blackVideoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:blackVideoPath]];
AVPlayerItem *realVideoItem = [AVPlayerItem playerItemWithURL:_videoWithEffectsURL];
NSArray *theItems = [NSArray arrayWithObjects:blackVideoItem, realVideoItem, nil];
AVQueuePlayer *theQueuePlayer = [AVQueuePlayer queuePlayerWithItems:theItems];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[theItems firstObject]];
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:theQueuePlayer];
playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:playerLayer];
[theQueuePlayer play];
This does play my first video (for debugging I have a video with a counting soundtrack) but when reach the end and advance to the next screen (in playerItemDidReachEnd) my real video doesn't play immediately.
Just to make sure my URLs were correct I reversed blackVideoItem and realVideoItem in the list and I do hear the soundtrack to my "real" video.
I've searched SO a lot and it doesn't seem like there is a way to play a streaming video without a delay. I'd love to be wrong.
You may be able to do this without AVQueuePlayer.
As early as possible, create the AVPlayer with the remote asset.
Observe the AVPlayerItem (playback buffers, etc.) to make sure it has buffered enough data to play smoothly at the beginning. (See: Preloading video to play without delay)
Show your progress screen.
When the player item is ready, remove the progress screen
Depending on the network connection, you may not experience smooth playback the whole way through, but monitoring the player item should let you show some UI if your player is buffering.

Why doesn't AVPlayer stop loading data?

When I watch my mac's network connectivity I can tell that a movie is still buffering even after I remove the view which had the AVPlayer on it.
Does anyone know how to force AVPlayer to stop loading data?
Maybe it's late to answer this question, but I've just solved the same one.
Just save here in case anyone need...
[self.player.currentItem cancelPendingSeeks];
[self.player.currentItem.asset cancelLoading];
There's no cancel method in AVPlayer class, but in AVPlayerItem and AVAsset class, there are!
PS: player here is AVPlayer class, not AVQueuePlayer class.
An AVPlayer is not a view. You may have "removed the view" but that does not mean you have removed or stopped the AVPlayer. I would guess that the way to do what you want is to destroy the AVPlayer itself.
I figured it out.
When you want to stop an AVPlayerItem from loading, use AVQueuePlayer's removeAllItems and then re-initialize it.
[self.avPlayer removeAllItems];
self.avPlayer = [AVQueuePlayer playerWithURL:[NSURL URLWithString:#""]];
self.avPlayer = nil;
This will stop the current item from loading -- it is the only way I found to accomplish this.

Synchronized recording/playback ios

I'm creating an app where the user is supposed to video record himself while another video is playing on the screen next to the camera view. The result will be two video files, one source, and one recording. Using AVCaptureSession etc., I have successfully managed to record a video at the same time as another video is playing on the screen. The problem is that It's not completely in sync.
This is how I have set it up right now:
-(void)playAndRecordInSync //Session is already set etc.
{
player = [AVPlayer playerWithURL:url];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
[playerLayer setFrame:leftCameraView.bounds];
[playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[leftCameraView.layer addSublayer:playerLayer];
//Will stop recording camera when source video reaches end(notification):
player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[player currentItem]];
//Start playback:
[player play];
//Start recording:
[movieOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[movieOutput stopRecording];
}
The result of this, is two videos, but the second (recorded) video is slightly longer than the source/original video. When playing them on top of each other (at the same time), then video nr. 2 has a delay of almost half a second. It feels like video2 has started recording way too soon, because of the "extra" time. It could probably also be that the playback started too late. Either way, I don't know how to make any consistency here based on this. I could probably play around a lot with sleep etc, but that would only work for my phone, as this has something to do with how fast the phone is.
Even if there is a delegate method like -(void)willStartPlaying for the AVPlayer, which I don't think there is, there would still be a problem with sync, as there's a minor wait till the return. Is there a way to fire two commands at once? Or is there another solution for this?
EDIT
I've been reading this (AVSynchronizedLayer), but I'm not sure if this is applicable to my situation, and I don't understand how to proceed.
I found out the delay was always caused by the playback starting too late, but both the recording and the playback were stopped at the same time, so I resolved this by finding v2.duration - v1.duration, and subtract the result from the beginning of v2, using AVAssetExportSession. When playing back both videos at once now, in different layers, there's still some delay from starting both, but by exporting the videos, I've confirmed that they're the same length and in sync now.

MPMoviePlayerController replay video

I want to display a button that gives the option to the user to restart a video in a MPMoviePlayerController at any time, even while the video is playing. I tried this but it has no effect:
-(IBAction)ReloadVideo:(id)sender{
[moviePlayer play];
}
Why doesn't this work?
You should call first [moviePlayer stop] this will:
stops playback of the current item and resets the playhead to the
start of the item. Calling the play method again initiates playback
from the beginning of the item.
(From apple docs)

Resources