AVPlayer buffering, pausing notification, and poster frame - iphone

I have some questions related to AVPlayer which are:
When we pause the AVPlayer through [player pause] does the AVPlayer keep buffering the video from the network or does it just stop? I couldn't get any info related to this in apple's documentation. Also, is it possible to force the AVPlayer to keep buffering while in pause, so that if we have the paused video is in waiting for the first video to be ended then we wouldn't find any gap in between the videos?
On pausing the AVPlayer can we have any event on [player pause].
Can we show still image on AVPlayer for some seconds?
Thanks

1) AVPlayer will buffer the video in several cases, none cleary documented. I'd say you can expect buffering when you init the video, and when you replace the current item.
You can observe currentItem.loadedTimeRanges to know what's going on. That property will tell you which video time ranges has been loaded.
Also, there is a few other currentItem properties that may help you: playbackLikelyToKeepUp, playbackBufferFull and playbackBufferEmpty.
Achieving a perfect gapless playback is not easy.
/* player is an instance of AVPlayer */
[player addObserver:self
forKeyPath:#"currentItem.loadedTimeRanges"
options:NSKeyValueObservingOptionNew
context:kTimeRangesKVO];
In observeValueForKeyPath:ofObject:change:context::
if (kTimeRangesKVO == context) {
NSArray *timeRanges = (NSArray *)[change objectForKey:NSKeyValueChangeNewKey];
if (timeRanges && [timeRanges count]) {
CMTimeRange timerange = [[timeRanges objectAtIndex:0] CMTimeRangeValue];
NSLog(#" . . . %.5f -> %.5f", CMTimeGetSeconds(timerange.start), CMTimeGetSeconds(CMTimeAdd(timerange.start, timerange.duration)));
}
}
2) Just keep an eye on player.rate.
[player addObserver:self
forKeyPath:#"rate"
options:NSKeyValueObservingOptionNew
context:kRateDidChangeKVO];
Then in your observeValueForKeyPath:ofObject:change:context::
if (kRateDidChangeKVO == context) {
NSLog(#"Player playback rate changed: %.5f", player.rate);
if (player.rate == 0.0) {
NSLog(#" . . . PAUSED (or just started)");
}
}
3) You can build a movie of a given length using a still image but it's easier to use a regular UIImageView on top of the player. Hide/show it when needed.
Sample project: feel free to play with the code I wrote to support my answer.

Related

How to use a activity indicator before the video starts playing using MPMoviePlayerController?

I need to play a video in my UIViewcontroller using MPMoviePlayerController. So before playing the video i need to show an activity Indicator View before the video is buffered. Once the video starts playing i need to remove the Activity Indicator. I am not able to find out on how to get notified as soon as the video starts playing. Any suggestion on this would be of great help. Thanks.
Your probably looking for something like this:
- (void)viewDidAppear:(BOOL)animated {
NSLog(#"VIEW DID LOAD");
// Register to receive a notification that the movie is now in memory and ready to play
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
}
-(void)movieLoadStateDidChange:(id)sender{
NSLog(#"STATE CHANGED");
if(MPMovieLoadStatePlaythroughOK ) {
NSLog(#"State is Playable OK");
NSLog(#"Enough data has been buffered for playback to continue uninterrupted..");
aiv.hidden = YES;
[aiv stopAnimating];
}
}
I also found that from this link which may help you out too: http://www.sdkboy.com/?p=48
if(MPMovieLoadStatePlaythroughOK ) { - this check is incorect. It's always TRUE.
Take a look:
Can a UIActivityIndicator be displayed over a MPMoviePlayerController in full screen mode?

Should I stop current AVPlayer instance when playing music from another URL?

I just created AVPlayer and it plays music well. I have two questions
How to play another music from another URL (should I stop current player?)
How to show current time of the song in UISlider (actually is it a method that called when the song is playing?)
Use -[AVPlayer replaceCurrentItemWithPlayerItem] to replace the current playing item reusing the player instance. You can create an item with an URL or with an asset.
In order to know when a given item finishes playing use the notification AVPlayerItemDidPlayToEndTimeNotification.
Use -[AVPlayer addPeriodicTimeObserverForInterval] to perform some action periodically while the player is playing. See this example:
[self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil
usingBlock:^(CMTime time) {
<# your code will be called each 1/10th second #>
}];
1) If you used - (id)initWithURL:(NSURL *)URL then you should stop player with pause, dealloc it and create new instance.
AVPlayer *player = [AVPlayer alloc] initWithURL:[NSURL URLWithString:#"http:/someurl.com"]];
[player play];
[player pause];
[player release];
player = [AVPlayer alloc] initWithURL:[NSURL URLWithString:#"http:/someurl2.com"]];
[player pause];
[player release];
If you used playerWithURL, then just call the same line again.
2). The easiest is the get duration of the current item https://stackoverflow.com/a/3999238/619434 and then update the UISlider with that value. You can use NSTimer to periodically check the duration.
self.player.currentItem.asset.duration

Touch Drag Inside triggers sound repeatedly & Touch Up Inside doesn't work

I'm trying to do an app where a short sound sample is supposed to be played while the person using the app is dragging his/her finger(s) across the screen. When the finger(s) are lifted away from the screen - the sound will stop.
This is the function that triggers the sound:
-(IBAction) playSound:(id)sender{
NSString *soundPath = [[NSBundle mainBundle]pathForResource:#"sound" ofType:#"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundPath];
newPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:fileURL error:nil];
newPlayer.numberOfLoops = -1;
[newPlayer play];
}
This is the function that stops the sound:
-(IBAction) stopSound{
if ([newPlayer isPlaying]) {
[newPlayer stop];
}
[newPlayer release];
}
I first started out using the Touch Down event, which looped the sound seamlessly. At this point the stopSound worked together with the "Touch Up Inside" event.
The point, as I said, is the sound is supposed to be generated when the user drags his/her finger(s) across the screen. But when I tried to the tie the playSound function to the "Touch Drag Inside" the sound loops repeatedly over itself, instead of one at the time, making it hell to use. The stopSound function doesn't work after i changed to the Drag Event.
I tried to use NSTimer to create some kind of way to handle the loops, but without any success.
My advice would be to create to generate a method that loops your audio clip (whatever duration, overlap, pausing, etc you want). Setup the method so that when you call it to play, it plays the way you want it to indefinitely.
The method might look like:
-(void) beginOrContinuePlayingAudioLoop:(BOOL)shouldPlay {
//if touchesMoved called, shouldPlay will be YES
//if touchesEnded called, shouldPlay will be NO
if (shouldPlay == NO) {
//cease playing
currentlyPlaying = NO;
}
if (currentlyPlaying == YES) {
return;
} else {
//begin playing audio
currentlyPlaying = YES;
}
}
In your ViewController class, define a BOOL (currentlyPlaying) that indicates whether the audio loop is currently playing.
As opposed to using canned IBAction gesture recognizers, consider overriding the more generic touch responder calls, touchesMoved and touchesEnded on your view.
In touchesMoved, set your VC's BOOL to YES and then fire your audio looping method to start playing. The "playing" method should always check and see if the audio is already playing, and only start playing
Also place a check in your method that returns/exits your method when the audio loop is already playing, this will avoid overlapping.
In touchesEnded, kill your audio via whatever method you choose, and reset the BOOL to NO.

How to trigger event for chapter markers when playing a quicktime movie?

I'm trying to create an iPad app where i need to playback a quicktime movie which contains some chapter markers. When each marker is reached i need a small overlay to be shown on top of the video.
Is there a way to trigger an event/function each time a marker is reached? And if so, how?
I'm not interested in having to develop an entire movieplayer with codec handling from scratch, since this is out of my comfortzone - So I'm hoping this is possible using MPMoviePlayer or something similar.
Any help is greatly appreciated! :)
Register to receive the following notification:
#define MPAVControllerTimeDidJumpNotification #"MPAVControllerTimeDidJumpNotification"
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleTimeChanged:) name:MPAVControllerTimeDidJumpNotification object:nil];
-(void)handleTimeChanged:(NSNotification *)notification
{
static int i = 0;
NSDictionary * userInfo = notification.userInfo;
int lastPositionInSeconds = [[userInfo valueForKey:#"MPAVControllerTimeParameter"] intValue];
if(lastPositionInSeconds > markers[i])
{
i++;
[self showOverlay: i];
}
}
Also register to receive MPMoviePlayerPlaybackDidFinishNotification notification to stop listening for the MPAVControllerTimeDidJumpNotification notification.

How to stream a remote mp3 without using a full screen player? e.g. shoutcast

how do I play a remote .mp3 in an iPhone app with full control over the playback controls and visuals shown? I know MPMoviePlayerController can stream .mp3s but it takes over the whole screen.
If you could just point me in the direction of an appropriate guide or class reference that would be great.
I dont think AVAudioPlayer supports streaming mp3s. From my experience, i am reasonably confident in saying that it only plays local files.
I have played remote mp3s using AVPlayer.
self.player = [[AVPlayer alloc] initWithURL:fileURL];
//inits the AVPlayer object.
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
// You need to watch for for when the player obeject becomes ready to play, using key-value observation
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if(object == player && [keyPath isEqualToString:#"status"])
{
playButton.enabled = YES;
playButton.titleLabel.text = #"Play";
.....
//do whatever you need to do here to get the object ready to play.
}
}
a simple [player play] will start playing the file.
//metadata is available at
player.currentItem.asset.commonMetadata
//and time played can be calculated by dividing x by y
UInt64 x = self.player.currentTime.value;
int32_t y = self.player.currentTime.timescale;
int secs = x/y;
You might want to use AVAudioPlayer and create your own controls.
I don't know if MPMediaPlayer can play a streamed mp3, but it can play local media in an "app" player instead of an "ipod" player (which takes over the screen). MPMediaPlayer is similar to AVAudioPlayer in that you can play with or without any UI, which you can create. Play, pause, stop, rewind, forward, etc.