Check if AVPlayer is playing already - ios

I have a tableview of songs on the ios device and if I select a row, I push to a new view where AVPlayer starts playing the selected song. Now if I go back and select another row, pushing to the view the app will start playing the new song, while continue to play the one that was running already.
EDIT:
I tried using the rate value like this without success, as it will always output "not playing" if i put it in my viewdidload method, even if a song is playing:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
appDelegate = (SimpleTableAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.audioPlayer = [[AVPlayer alloc] init];
if (appDelegate.audioPlayer.rate == 0.0f)
{
NSLog(#"not playing");
} else {
NSLog(#"already playing");
}
}

In this line
appDelegate.audioPlayer = [[AVPlayer alloc] init];
you seem to be alloc'ing and init'ing a new AVPlayer. As such it's not surprising that you get a "not playing" result. Simply leave out that line.

AVPlayer has a rate property which indicates the speed of playback as a float. 0.0f indicates stopped. The play and stop methods just change the rate property.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
appDelegate = (SimpleTableAppDelegate *)[[UIApplication sharedApplication] delegate];
//Stop potential existing audioPlayer prior to creating new player
[appDelegate.audioPlayer stop];
//Create new audio player
appDelegate.audioPlayer = [[AVPlayer alloc] init];
}

Related

IOS How to determine when video clip is finished playing

In my application I need to play a movie from vimeo, this goes fine but my hole application is made for portrait only. I want to play the movie in a specific viewController and force this controller to be able to rotate to portrait or landscape.
When someone clicks the thumbnail of the movie, I segue to the VideoViewController, I need to know how to determine when this movie clips is finished so I can segue them back directly after the video is finished.
Code;
#implementation VideoViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self vimeoVideo];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)vimeoVideo
{
[YTVimeoExtractor fetchVideoURLFromURL:#"http://vimeo.com/1071123395"
quality:YTVimeoVideoQualityMedium
referer:#"http://xxxxx.com"
completionHandler:^(NSURL *videoURL, NSError *error, YTVimeoVideoQuality quality) {
if (error) {
// handle error
NSLog(#"Video URL: %#", [videoURL absoluteString]);
} else {
// run player
self.playerView = [[MPMoviePlayerViewController alloc] initWithContentURL:videoURL];
[self.playerView.moviePlayer prepareToPlay];
[self presentViewController:self.playerView animated:YES completion:^(void) {
self.playerView = nil;
}];
}
}];
}
You need to add an observer on viewDidLoad so when your Video will stop playing, let it execute your method.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(moviePlayerDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:playerView];
This is the receiver method when the video stops playing
- (void)moviePlayerDidFinish:(NSNotification *)notification
{
//Do your stuff
}
In the MPMoviePlayerController, there are list of Notifications available for you. The one you need is MPMoviePlayerPlaybackDidFinishNotification
Posted when a movie has finished playing. The userInfo dictionary of this notification contains the MPMoviePlayerPlaybackDidFinishReasonUserInfoKey key, which indicates the reason that playback finished. This notification is also sent when playback fails because of an error.

AirPlay with multiple instances of AVPlayer

In my app, I have multiple tabs and on each tab I have an instance of AVPlayer. When I activate AirPlay, however, "the first player wins". That means that the player on the currently active tab connects to AirPlay and when I switch to a different tab and press play, nothing happens. So only the first instance of AVPlayer that connects to AirPlay can actually play through AirPlay and no players on the other tabs work. What to do?
The solution is quite easy: When your view controller that contains a player appears, you set allowsExternalPlayback on the AVPlayer instance to YES, when in disappears you set it to NO.
Example:
- (void)viewWillAppear:(BOOL)animated
{
// _player is an instance of AVPlayer
if ([_player respondsToSelector:#selector(setAllowsExternalPlayback:)]) {
// iOS 6+
_player.allowsExternalPlayback = YES;
} else {
// iOS 5
_player.allowsAirPlayVideo = YES;
}
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
// _player is an instance of AVPlayer
if ([_player respondsToSelector:#selector(setAllowsExternalPlayback:)]) {
// iOS 6+
_player.allowsExternalPlayback = NO;
} else {
// iOS 5
_player.allowsAirPlayVideo = NO;
}
[super viewWillDisappear:animated];
}
Enjoy.

multiple instances of the same sound on iOS

i'm making a bubble popping game and hit a bit of a snag :/
i have a sound (pop.mp3), this sound needs to play EVERY time one of the bubbles are tapped
all the bubbles are buttons that call the same method (-(IBACTION)bubblePop)
i initialised an AVAudioPlayer in the Viewdidload method
and call it's play function inside the bubblePop method
this clearly did not work however because the bubble's pop sound only plays one at a time, it doest overlap like i would expect it to
does anybody know how i can resolve this?
Extra info
if i initialise the audio player inside bubblePop i get no sound at all
You should initialize AVPlayer inside bubblePop and keep strong reference to it.
Try this:
#interface ViewController()
#property (strong, nonatomic) NSMutableDictionary *players;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.players = [[NSMutableDictionary alloc] init];
}
- (void)bubblePop {
NSURL *URL = your sound URL;
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:URL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
self.players[playerItem] = player;
[player play];
}
-(void)itemDidFinishPlaying:(AVPlayerItem *)sender {
[self.players removeObjectForKey:sender];
}
#end

AVAudioPlayer won't mute when SilentMode is on

I am starting to get convinced that iOS is just pure magic. I looked through numerous posts on AVAudioPlayer for solutions, but none worked.
When the application starts I have a movie starting off from MPMoviePlayerController and I have AVAudioPlayer kicking off with a sound as follows:
-(void)createAndPlayMovieFromURL:(NSURL *)movieURL sourceType:(MPMovieSourceType)sourceType{
/* Create a new movie player object. */
player = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
player.controlStyle = MPMovieControlModeDefault;
if (player)
{
/* Save the movie object. */
[self setMoviePlayerController:player];
/* Specify the URL that points to the movie file. */
[player setContentURL:movieURL];
/* If you specify the movie type before playing the movie it can result
in faster load times. */
[player setMovieSourceType:sourceType];
CGRect frame = CGRectMake(0, 0, 480, 320);
/* Inset the movie frame in the parent view frame. */
[[player view] setFrame:frame];
[player view].backgroundColor = [UIColor blackColor];
/* To present a movie in your application, incorporate the view contained
in a movie player’s view property into your application’s view hierarchy.
Be sure to size the frame correctly. */
[self.view addSubview: [player view]];
}
[[self moviePlayerController] play];
}
and for sound:
- (void)setupAudioPlayBack:(NSString *) path : (NSString *) extension{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:path
ofType:extension]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
}
}
And now lets talk magic. When the first movie plays and the sound, the sound is not responding to either the volume controls on the phone nor to Silent Mode On switch.
But then immediately a second movie plays with its own sound and everything works fine - there is no sound with Silent Mode On, if Silent Mode Off it responds to Volume Controls. 3rd movie - plays perfectly too.
I tried using the player.useApplicationAudioSession = NO for MPMoviePlayerController - fixed the problem with the first time sound not responding to Silent Mode or Volume Controls, but now when the second movie starts to play it freezes instantly and the sound played is cut from the original size to something around 2 seconds.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:nil]; didn't help either wherever I set it.
So my question is - how to make the MPMoviePlayerController interact with AVAudioPlayer without any problems...
Ok I actually found a solution (once again, magic).
When you start your ViewController you set it up as follows:
- (void)viewDidLoad
{
[self setupAudioPlayBack:#"sound1" :#"caf"];
[self playMovieFile:[self localMovieURL:#"mov1" :#"mov"]];
audioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(playAudio) userInfo:nil repeats:NO];
[super viewDidLoad];
}
Where the playAudio method is simply:
- (void)playAudio{
[audioPlayer play];
}
Just 0.1 millisecond timer is enough to make everything work (0.0 doesn't work) and make sure a new video doesn't start before the audio finishes or it will destroy the settings so you will have to invalidate the timer and redo the timer again.

MPMoviePlayerController plays only when called twice. Only occurs in iOS 4

I have an app for iOS 4.0-5.1 that uses HTTP Live Streaming to play videos. I have a simple setup with a button in a view that starts playing the stream when it is tapped. This works fine in iOS 5 but the button needs to be tapped twice before the stream begins playing in iOS 4. Does anybody know why this is happening and how to make the video play on the first tap of the button?
Here is what I'm doing:
.h
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#interface ViewController : UIViewController{
MPMoviePlayerController *moviePlayer;
}
#property (nonatomic, strong) MPMoviePlayerController *moviePlayer;
-(IBAction)playApple:(id)sender;
#end
.m
-(IBAction)playApple:(id)sender{
if(self.moviePlayer){
self.moviePlayer = nil;
}
//Use Apple's sample stream
NSURL *mediaURL = [NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:mediaURL];
//Begin observing the moviePlayer's load state.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.moviePlayer];
[self.moviePlayer setMovieSourceType:MPMovieSourceTypeStreaming];
[self.moviePlayer setShouldAutoplay:NO];//Stop it from autoplaying
[self.moviePlayer prepareToPlay];//Start preparing the video
}
#pragma mark Notification Center
- (void)moviePlayerLoadStateChanged:(NSNotification *)notification{
NSLog(#"State changed to: %d\n", moviePlayer.loadState);
if(self.moviePlayer.loadState == MPMovieLoadStatePlayable){
//if load state is ready to play
[self.view addSubview:[self.moviePlayer view]];
[self.moviePlayer setFullscreen:YES];
[self.moviePlayer play];//play the video
}
}
I can see some possible issues - just try it out and let us know if any or all of this did the trick.
1. loadstate masking
The loadstate should not be directly compared but masked before comparing as it might contain a mixture of multiple states.
MPMovieLoadState
Constants describing the network load state of the movie player.
enum {
MPMovieLoadStateUnknown = 0,
MPMovieLoadStatePlayable = 1 << 0,
MPMovieLoadStatePlaythroughOK = 1 << 1,
MPMovieLoadStateStalled = 1 << 2,
};
typedef NSInteger MPMovieLoadState;
So instead of directly comparing it, as you are, mask it to be on the safe side.
2. view setup
Why not setting up the player view right before starting the playback. I have never seen it the way you do it (not that I was positively sure that this is the problem - still, seems odd to me). Additionally, even though the player will not actually use the view you are assigning (fullscreen mode does it a bit differently), you may want to enhance the view-setup with assigning a proper frame.
All of the above rolled into this new version of your code...
-(IBAction)playApple:(id)sender
{
if(self.moviePlayer)
{
self.moviePlayer = nil;
}
//Use Apple's sample stream
NSURL *mediaURL = [NSURL URLWithString:#"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:mediaURL];
//Begin observing the moviePlayer's load state.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.moviePlayer];
self.moviePlayer.view.frame = self.view.bounds;
[self.view addSubview:[self.moviePlayer view]];
[self.moviePlayer setFullscreen:YES];
[self.moviePlayer setMovieSourceType:MPMovieSourceTypeStreaming];
[self.moviePlayer setShouldAutoplay:NO]; //Stop it from autoplaying
[self.moviePlayer prepareToPlay]; //Start preparing the video
}
- (void)moviePlayerLoadStateChanged:(NSNotification *)notification
{
NSLog(#"State changed to: %d\n", moviePlayer.loadState);
if((self.moviePlayer.loadState & MPMovieLoadStatePlayable) == MPMovieLoadStatePlayable)
{
//if load state is ready to play
[self.moviePlayer play];//play the video
}
}

Resources