How to stop audio in background mode? - ios

I want to stop audio in background mode in first controller and play audio in background mode in second controller.
I use this code:
NSString *soundFilePath = [NSString stringWithFormat:#"%#/%#.mp3",[[NSBundle mainBundle] resourcePath], #"sound"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
_player.numberOfLoops = 1000;
[_player play];
To stop audio in background mode I use this code:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
[[AVAudioSession sharedInstance] setActive: NO error: nil];
To play audio in background mode I use this code:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
But my code to stop audio doesn't work...
UPD
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(handleForgroundScenario) name:UIApplicationWillEnterForegroundNotification object:nil];
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(handleBackgroundScenario) name:UIApplicationWillResignActiveNotification object:nil];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
-(void) handleForgroundScenario {
if([self.player rate] == 0) {
[_player play];
}
}
-(void) handleBackgroundScenario {
[_player pause];
}
- (void)viewDidLoad
{
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
[[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: nil];
[super viewDidLoad];
NSString *soundFilePath = [NSString stringWithFormat:#"%#/%#.mp3",[[NSBundle mainBundle] resourcePath], #"sound"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
_player.numberOfLoops = 1000;
[_player play];
}

Add ApplicationWillResign notification in ViewWillAppear of ViewController in which you wanna pause player
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(handleBackgroundScenario), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
}
Pause player in selector
#objc func handleBackgroundScenario() {
//pause
}
Finally remove observer in viewWillDisappear
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self)
}
This will make sure that notification will be triggered only when ViewController is loaded and app goes to background.
For all other VC notification will not trigger the selector and hence it will not alter the behavior of player
EDIT:
Code in Objective - C
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(handleBackgroundScenario) name:UIApplicationWillResignActiveNotification object:nil];
}
-(void) handleBackgroundScenario {
//pause player
[_player pause];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
EDIT:
Where I should call player after app become alive?
By the statement u mean when to start player when app comes to foreground you can use
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(handleForgroundScenario) name:UIApplicationDidBecomeActiveNotification object:nil];
Finally implement handleForgroundScenario
-(void) handleForgroundScenario {
if([self.player rate] == 0) {
[_player play];
}
}

Related

Playing continuous audio in Iphone

I have this piece of code for playing audio, but once it is finished, I want to play the same audio again and again, I think I should use numberofloops=-1, but where I need to use this directly. Please help me.
#import "JetNapMusicPlayer.h"
#import <AVFoundation/AVFoundation.h>
#interface JetNapMusicPlayer()
#property(nonatomic,strong) AVQueuePlayer *avQueuePlayer;
#end
static JetNapMusicPlayer *sharedManager = nil;
#implementation JetNapMusicPlaye
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if(sharedManager == nil)
sharedManager = [[super alloc] init];
}
return sharedManager;
}
- (id)init {
if (self = [super init]) {
// [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
MPRemoteCommandCenter *rcc = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *playCommand = rcc.playCommand;
[playCommand setEnabled:YES];
[playCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] play];
return MPRemoteCommandHandlerStatusSuccess;
}];
MPRemoteCommand *pauseCommand = rcc.pauseCommand;
[pauseCommand setEnabled:YES];
[pauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent *event) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] pause];
return MPRemoteCommandHandlerStatusSuccess;
}];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
-(AVPlayer *)avQueuePlayer
{
if (!_avQueuePlayer) {
[self initSession];
_avQueuePlayer = [[AVQueuePlayer alloc] init];
}
return _avQueuePlayer;
}
-(void)initSession
{
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(audioSessionInterrupted:)
name: AVAudioSessionInterruptionNotification
object: [AVAudioSession sharedInstance]];
//set audio category with options - for this demo we'll do playback only
NSError *categoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&categoryError];
if (categoryError) {
NSLog(#"Error setting category! %#", [categoryError description]);
}
//activation of audio session
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) {
if (activationError) {
NSLog(#"Could not activate audio session. %#", [activationError localizedDescription]);
} else {
NSLog(#"audio session could not be activated!");
}
}
}
#pragma mark - notifications
-(void)audioSessionInterrupted:(NSNotification*)interruptionNotification
{
NSLog(#"interruption received: %#", interruptionNotification);
}
#pragma mark - player actions
-(void) pause
{
[[self avQueuePlayer] pause];
}
-(void) play
{
[[self avQueuePlayer] play];
}
-(void) clear
{
[[self avQueuePlayer] removeAllItems];
}
#pragma mark - remote control events
#pragma mark - Kony FFI
+ (BOOL)playMusic:(NSString *)filename artistname:(NSString *)artistname songname:(NSString *)songname {
NSString *name = [filename stringByDeletingPathExtension];
NSString *ext = [filename pathExtension];
AVPlayerItem *avSongItem = [[AVPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[[NSString alloc] initWithFormat:name] ofType:ext]]];
if (avSongItem) {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] clear];
[[[JetNapMusicPlayer sharedManager] avQueuePlayer] insertItem:avSongItem afterItem:nil];
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] play];
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = #{MPMediaItemPropertyTitle: songname, MPMediaItemPropertyArtist:artistname};
}
return YES;
}
+ (BOOL)stopMusic {
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] pause];
[(JetNapMusicPlayer *)[JetNapMusicPlayer sharedManager] clear];
return YES;
}
#end
To loop a song use below code after alloc init of avSongItem.
avSongItem.actionAtItemEnd = AVPlayerActionAtItemEndNone;
More info : Looping a video with AVFoundation AVPlayer?
Also as mentioned in the link use notification.
avSongItem.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[avPlayer currentItem]];
this will prevent the player to pause at the end.
in the notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[p seekToTime:kCMTimeZero];
}

Stop AvAudioPlayer in other View iOS

I would like to stop the audio with AvAudioPlayer but when another view dissapear, There are two views:
The problem is that when view2 disappear, the audio doesn't stop... How I should do?
View1.h
#property (nonatomic, retain) AVAudioPlayer *audioPlayer;
- (IBAction)playAudio:(id)sender;
- (IBAction)stopAudio:(id)sender;
View1.m
- (void)viewDidLoad
{
[super viewDidLoad];
[self playSound];
}
- (void) playSound
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:#"simpsonsTheme"
ofType:#"mp3"]];
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
//[self.audioPlayer play];
[self playAudio:audioPlayer];
}
- (IBAction)playAudio:(id)sender {
[audioPlayer play];
}
- (IBAction)stopAudio:(id)sender {
[audioPlayer stop];
}
And View2.m
-(void)viewWillDisappear:(BOOL)animated{
view1 = (View1 *)[[UIApplication sharedApplication] delegate];
[view1 stopAudio:view1.audioPlayer];
}
In the View2 I import the View1 to do that.
Thanks
Your app delegate is not a view controller. Maybe you want [[UIApplication sharedApplication] delegate].window.rootViewController.
As you've seen, that's fragile. Instead of having pointers between your viewcontrollers, you could use have looser coupling and use a "stop" NSNotification. It may even be useful elsewhere in your application.
Define
NSString *StopPlayingVideoNotification = #"StopPlayingVideo";
So both views can see it, so put extern NSString *StopPlayingVideoNotification; in one of your header files.
In View1.m's init:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopAudio:) name:StopPlayingVideoNotification object:nil];
Add a dealloc to View1:
-(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:StopPlayingVideoNotification object:nil];
}
In View2.m
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] postNotificationName:StopPlayingVideoNotification object:self];
}

iOS7 AVQueueplayer background loop (loop queued music in background)

This is my first post and I have a problem that I am completely stumped on. I have a AVQueueplayer with multiple mp3's posted on a server.
The app plays and loops while the app is in the foreground. However, when I press the home button, queueplayer stops looping.
Here is the code I have so far
-(void)playselectedsong{
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
NSString* musicURL= [NSString stringWithFormat:#"%#%#", getMusicURL, [musicArray objectAtIndex:0]];
NSString* musicURL2= [NSString stringWithFormat:#"%#%#", getMusicURL, [musicArray objectAtIndex:1]];
NSArray *queue = #[[AVPlayerItem playerItemWithURL:[NSURL URLWithString:musicURL]], [AVPlayerItem playerItemWithURL:[NSURL URLWithString:musicURL2]]];
AVQueuePlayer *qplayer = [[AVQueuePlayer alloc] initWithItems:queue];
self.queuePlayer=qplayer;
[qplayer play];
qplayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[queue lastObject]];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// code here to play next sound file
NSLog(#"playeritemdidreachend");
[self playselectedsong ];
}
Adjust your project for playing audio in background (see here)
Start playing via
dispatch_async(dispatch_get_main_queue(), ^{
[avqueueplayerinstance play];
});//end block`

AVQueuePlayer AVPlayerItemDidPlayToEndTimeNotification fails to call

Im using AVQueuePlayer to loop through an array of AVPlayerItems.
The way I'm looping it, I listen to the AVPlayerItemDidPlayToEndTimeNotification and every time its called, I add the current object to the end of the queue.
heres the code:
viewWillAppear
{
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_queuePlayer currentItem]];
[_queuePlayer play];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
AVPlayerItem *fakeNew = [[AVPlayerItem alloc] initWithAsset:p.asset];
if (_queuePlayer.items.count == 1)
{
[p seekToTime:kCMTimeZero];
[_queuePlayer play];
}
else
{
[_queuePlayer insertItem:fakeNew afterItem:[_queuePlayer.items lastObject]];
}
NSLog(#"array of items to play:%lu", (unsigned long)_queuePlayer.items.count);
}
The problem is, that the method is called only for the first video that plays, after that, the method stops getting called, so if for example i have 2 movies in the array, it would play them both+the first one again, any idea why is this happening?
More Info:
also tried to make a new player every time and set it to layer. failed to send the notification more than once just the same
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
[self.playList removeObjectAtIndex:0];
[self.playList addObject:p];
AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
_player = newPlayer;
self.AVPlayerLayerView.layer.player = self.player;
[_player play];
}
After a lot of messing around, apparently for whatever reason, the view unregistered as observer every time, I just removed and added observer after every notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
AVPlayerItem *p = [notification object];
AVPlayerItem *fakeNewItem = [[AVPlayerItem alloc] initWithAsset:p.asset];
[self.playList removeObjectAtIndex:0];
[self.playList addObject:fakeNewItem];
AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
_player = newPlayer;
self.AVPlayerLayerView.layer.player = self.player;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[_player currentItem]];
[_player play];
}
For a clean approach to resolve this issue. I approached with the next piece of code instead
The first is you have to add the code necessary to receive a feedback from the AVPlayer when the reproduction time changes.
- (void)addPeriodicTimeObserverForReproductionChanges {
#weakify(self);
[self.player
addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(kBQDefaultTimeIntervalReproductionChanges, NSEC_PER_SEC)
queue:self.eventsQueue
usingBlock:^(CMTime time) {
#strongify(self);
[self dispatchBlockOnMainQueue:^{
if ([self.delegate respondsToSelector:#selector(playerItemController:didChangeReproductionTime:)])
[self.delegate playerItemController:self
didChangeReproductionTime:time];
[self checkForItemPlayToEnd];
}];
}];
}
- (void)checkForItemPlayToEnd
{
CMTime durationScaled = CMTimeConvertScale(self.duration,self.player.currentTime.timescale, kCMTimeRoundingMethod_Default);
if (CMTIME_COMPARE_INLINE(durationScaled, ==, self.player.currentTime)) {
[self playerDidFinishReproducingItem];
}
}

Video won't play in iPad simulator

I am trying to load a short movie file (.m4v) before the application starts
However, when I using the ipad simulator, only the sound plays. There is no video running.
If I change the simulator to iphone simulator, the video plays just fine.
I use MPMoviePlayerController to play the movie file
I even downloaded the sample mediaPlayer from Apple, and the result is the same. With the sample movie file (.m4v) the iphone simulator can play the movie, but the ipad simulator cannot
Please help!
Here is my solution if you need it:
The solution is integrated with cocos2d, but it should be relatively easy to modify it regardless.
Please refer to the following website for general usage:
Getting MPMoviePlayerController to Work with iOS4, 3.2 (iPad) and Earlier Versions of iPhone SDK
iPad iOS: 3.2.2
#implementation MovieLayer
/*
* name: movie file name
* type: movie file type
* target: target class to handle the selectors
* finish: selector called when the movie is finished
* load: selector called when the movie loading state is changed
*/
+ (id)layerWithMovieName:(NSString*)name type:(NSString*)movieType target:(id)target
finish:(SEL)finishSelector load:(SEL)loadSelector
{
return [[[self alloc] initWithMovieName:name type:movieType target:target
finish:finishSelector load:loadSelector] autorelease];
}
- (id)initWithMovieName:(NSString*)name type:(NSString*)movieType target:(id)target
finish:(SEL)finishSelector load:(SEL)loadSelector
{
if ((self = [super init]) != nil)
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath = [bundle pathForResource:name ofType:movieType];
if (moviePath)
{
NSURL *moviePathURL = [NSURL fileURLWithPath:moviePath];
[self loadMovieAtURL:moviePathURL];
// Movie finish loading notification
[[NSNotificationCenter defaultCenter]
addObserver:target
selector:loadSelector
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
// Movie finish Notification
[[NSNotificationCenter defaultCenter]
addObserver:target
selector:finishSelector
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
}
}
return self;
}
- (void)dealloc
{
[theMovie.view removeFromSuperview];
theMovie.initialPlaybackTime = -1;
[theMovie stop];
[theMovie release];
[super dealloc];
}
- (void)loadMovieAtURL:(NSURL*)theURL
{
CGSize size = [[CCDirector sharedDirector] winSize];
theMovie = [[MPMoviePlayerController alloc] initWithContentURL:theURL];
[theMovie prepareToPlay];
// > 3.2
[theMovie respondsToSelector:#selector(loadState)];
theMovie.scalingMode = MPMovieScalingModeAspectFill;
[theMovie setFullscreen:TRUE animated:TRUE];
theMovie.controlStyle = MPMovieControlStyleNone;
theMovie.view.frame = CGRectMake(0, 0, size.width, size.height);
theMovie.view.backgroundColor = [UIColor clearColor];
// Transform
theMovie.view.transform = CGAffineTransformMakeRotation(-270.0f * (M_PI/180.0f));
theMovie.view.center = [[CCDirector sharedDirector] openGLView].center;
[[[CCDirector sharedDirector] openGLView] addSubview:theMovie.view];
}
- (void)play
{
[theMovie play];
}
#end
Basic usage using cocos2d
MovieLayer *player = [MovieLayer layerWithMovieName:LOGO_ANIMATION
type:LOGO_ANIMATION_FILE_EXT
target:self
finish:#selector(myMovieFinishedCallback:)
load:#selector(myMovieLoadCallback:)];
[self addChild:player z: 0 tag:1];
}
return self;
}
- (void)myMovieLoadCallback:(NSNotification*)notification
{
MPMovieLoadState state = [(MPMoviePlayerController*)notification.object loadState];
// Remove observer
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
if (state & MPMovieLoadStateUnknown)
{
[self myMovieFinishedCallback:nil];
}
else if (state & MPMovieLoadStatePlayable)
{
[(MovieLayer*)[self getChildByTag:1] play];
}
else if (state & MPMovieLoadStatePlaythroughOK)
{
[(MovieLayer*)[self getChildByTag:1] play];
}
else if (state & MPMovieLoadStateStalled)
{
[self myMovieFinishedCallback:nil];
}
}
- (void)myMovieFinishedCallback:(NSNotification*)notification
{
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
[self removeChildByTag:1 cleanup:YES];
}
Almost certain this is a know bug/limitation in the simulator.

Resources