Adding activity indicator while AVplayer gets ready to play music - ios

I developed an app for radio and it's working.
I want to put an activity indicator view so that when you touch the play it starts the activity indicator view and when the audio starts, the activity indicator stops.

This will add the activity view to the center of the view. Add this code in the "IBAction" where u handle the play button.
UIActivityIndicatorView *activityView=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityView.center=self.view.center;
[activityView startAnimating];
[self.view addSubview:activityView];
[self.view userInteractionEnabled:NO]; //to avoid touch events when activity is on
And to stop the activity indicator use
[activityView stopAnimating];
[activityView removeFromSuperview];
[self.view userInteractionEnabled:YES];
This is from this link about AV Foundation. and this answer ->
Using KVO, it's possible to be notified for changes of the player status:
player = [AVPlayer playerWithURL:fileURL];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
In the PLAY button's event add the code to start the UIActivityIndicator
This method will be called when the status changes:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == player && [keyPath isEqualToString:#"status"]) {
if (player.status == AVPlayerStatusReadyToPlay) {
//DISABLE THE UIACTIVITY INDICATOR HERE
} else if (player.status == AVPlayerStatusFailed) {
// something went wrong. player.error should contain some information
}
}
}

Related

Resume AVPlayer stream playback last sample

I am trying to use the native player (AVPlayer) to reproduce a live stream on iOS. However, I have trouble resuming the playback. When I stop the playback and resume it after few seconds, the playback starts from the moment I paused instead of reproducing the current (last) sample of the live stream.
Is there a way to get the last sample, o configure AVPlayer to reproduce from last sample when tapping on Play Button?
My solution is based on denying user to keep the player paused. This is, destroying the player each time playback is resumed. And creating a new instance each time, playback is intended to be resumed.
According to Apple recommendation, the only solution to know if the AVPlayer was stopped is to add KVO. This is:
- (void)setupPlayer {
AVPlayer *player = [AVPlayer playerWithURL:streamURL];
AVPlayerViewController *playerViewController = [[AVPlayerViewController alloc] init];
playerViewController.player = player;
self.playerViewController = playerViewController;
[self configureConstraintsForView:self.playerViewController.view]; //Add Player to View
[self setupObservers];
[player play];
}
- (void)setupObservers {
[self.playerViewController.player addObserver:self
forKeyPath:#"rate"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
if ([keyPath isEqualToString:#"rate"] && self.playerViewController.player.rate == CGPointZero.x) {
[self.playerViewController.view removeFromSuperview];
[self.playerViewController.player removeObserver:self forKeyPath:#"rate"];
[self.playerViewController.player pause];
self.playerViewController = nil;
}
}
Then, when user wants to re-engage the player, just call -(void)setupPlayer which start the playback from the last live sample.

AVPlayer takes long time to start audio play

I have a music player which streams audio from server, using AVPlayer, it plays a audio well, but after completion of an audio , when i click play button for new audio it takes time to start new audio almost 35-40 sec. Can anyone help me?
Use observer to know the player state.
Using KVO, it's possible to be notified for changes of the player status:
playButton.enabled = NO;
player = [AVPlayer playerWithURL:fileURL];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
This method will be called when the status changes:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == player && [keyPath isEqualToString:#"status"]) {
if (player.status == AVPlayerStatusReadyToPlay) {
playButton.enabled = YES;
} else if (player.status == AVPlayerStatusFailed) {
// something went wrong. player.error should contain some information
}
}
}

Now that MPMoviePlayerPlaybackDidFinishReasonUserInfoKey is deprecated, what is the best way to find why playback ended?

In iOS9, the MPMoviePlayer classes have all been deprecated in favor of AVPlayer. I have an existing app using MPMoviePlayerPlaybackDidFinishReasonUserInfoKey to determine how to log events on how the video player ended. How do I do the same with AVPlayer?
The following are the ending reason keys:
MPMovieFinishReasonPlaybackEnded
MPMovieFinishReasonPlaybackError
MPMovieFinishReasonUserExited
There is no equivalent to MPMoviePlayerPlaybackDidFinishReasonUserInfoKey and MPMoviePlayerPlaybackDidFinishNotification in AVKit. To accomplish the same functionality in AVKit, you must listen to three notifications separately, rather than one notification with different possible reasons.
MPMovieFinishReasonPlaybackEnded >>> AVPlayerItemDidPlayToEndTimeNotification
MPMovieFinishReasonPlaybackError >>> AVPlayerItemFailedToPlayToEndTimeNotification
MPMovieFinishReasonUserExited. No conversion. There are multiple ways to detect that the user has killed the player. One is detecting a modal has closed.
If you want to know if the video is playing or not, you can do a KVO:
[self.player addObserver:self forKeyPath:#"rate" options:0 context:nil];
Then add this method:
- (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
}
}
}
Try, in viewDidLoad:
AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:#"yoururl"]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
AVPlayer* player = [[[AVPlayer alloc] initWithPlayerItem:playerItem] autorelease];
[player play]
And
-(void)itemDidFinishPlaying:(NSNotification *) notification {
// Will be called when AVPlayer finishes playing playerItem
}

How do I correctly use AVPlayer so it doesn't show white screen before playing video?

Every time, when I try to playing a megabyte video using AVPlayer, it initially shows a white screen for a second and then starts the video.
Why is this happening if the video is already cached? Is there a way to stop this from happening, so that it goes straight to the video without displaying a white screen?
I tried using AVPlayer's isReady to check the status of AVPlayer and play video only when it's ready, but it still displays the white screen.
Also every time when I try to get the video duration of the video that's about to play through AVPlayer I keep getting 0.0 seconds initially, so I am not able to add a timer to the video either because I can't get the video duration because it keeps displaying a white screen for a second.
Firstly, AVPlayer doesn't show any white screen, its your background which is white. So, basically your AVPlayer is starting late. I guess you press a UIButton and then it loads the file in AVPlayer and immediately start playing it. Thats where the problem is. It may take some time for the AVPlayer to buffer enough data and be ready to play the file. Using KVO, it is possible to be notified for changes of the player status.
So first you need to disable the play button, load the AVPlayer and add an observer:
play.enabled = NO;
player = [AVPlayer playerWithURL:URL];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
Then enable it after checking AVPlayerStatusReadyToPlay:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
if (object == player && [keyPath isEqualToString:#"status"]) {
if (player.status == AVPlayerStatusReadyToPlay) {
play.enabled = YES;
}
}
}
I know this is an old question, but I get the same issue even when properly detecting when the AVPlayer is ready to play.
I wanted it to play over an image so that there was a smooth transition between an initial static image, and then moving video.
The trick for me was to set a clear background with:
AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
[controller.view setBackgroundColor:[UIColor clearColor]];
This way, if I toggle the visibility of the player when it's ready to play, I never see a black or white screen, because the player has a clear background, making for a smooth transition!
Blancos is right. AVPlayer is taking time to achieve state AVPlayerStatusReadyToPlay.
So, initialize the player with url and play it only when it is AVPlayerStatusReadyToPlay.
player = [AVPlayer playerWithURL:URL];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (object == player && [keyPath isEqualToString:#"status"]) {
if (player.status == AVPlayerStatusReadyToPlay) {
player.play()
}
}
}
I had the same problem. To avoid adding a KVO, I just set the AVPlayer up when the url is set like so...
var urlToUse: NSURL?
{
didSet
{
guard let urlToUse = urlToUse else { return }
replayPlayer = AVPlayer(URL: urlToUse)
}
}
That way the AVPlayer status will be ready when needed.

How to display ToolBar Buttons on MPMoviePlayerController

I am trying to display UIToolBar with BarButtonItems on MPMoviePlayerController. Not sure how will I implement it.
I am trying to play the video file when user taps on one of the cell of UITableView. At that time I would like to give an option to user to share the video on FB or tweeter.
Not sure how will I display the share BarButtonItem on MPMoviePlayerController. I am trying to implement something similar to the photo app that comes with iPhone.
Can anyone please help me out?
Thank you!
MPMovieplayer is not the right choice for this purpose. You can create a custom movie player with AVPlayer(found under AVFoundation.framework) that would serve your purpose. Create any normal ViewController in your project and add an AVPlayer with code something like below:
-(void)viewDidLoad {
//prepare player
self.videoPlayer = [AVPlayer playerWithURL:<# NSURL for the video file #>];
self.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndPause;
//prepare player layer
self.videoPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.videoPlayer];
self.videoPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.videoPlayerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:self.videoPlayerLayer];
//add player item status notofication handler
[self addObserver:self forKeyPath:#"videoPlayer.currentItem.status" options:NSKeyValueObservingOptionNew context:NULL];
//notification handler when player item completes playback
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.videoPlayer.currentItem];
}
//called when playback completes
-(void)playerItemDidReachEnd:(NSNotification *)notification {
[self.videoPlayer seekToTime:kCMTimeZero]; //rewind at the end of play
//other tasks
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"videoPlayer.currentItem.status"]) {
//NSLog(#"player status changed");
if (self.videoPlayer.currentItem.status == AVPlayerItemStatusReadyToPlay) {
//player is ready to play and you can enable your playback buttons here
}
}
}
As this would be normal view controller, you can add a toolbar buttons/buttons to it, for playing/sharing, etc. and trigger the player actions and any other related action like below:
-(IBAction)play:(id)sender {
[self.videoPlayer play];
}
-(IBAction)pause:(id)sender {
[self.videoPlayer pause];
}
//etc.
Also make sure to remove the observers in your dealloc:
-(void)dealloc {
//remove observers
#try {
[self removeObserver:self forKeyPath:#"videoPlayer.currentItem.status" context:NULL];
}
#catch (NSException *exception) {}
#try {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.videoPlayer.currentItem];
}
#catch (NSException *exception) {}
//other deallocations
[super dealloc];
}
More detailed and sophisticated explanation of the process can be found under apples companion guide, available here

Resources