How to resume AVAudioPlayer after interrupted in background - iphone

I am playing music using AVAudioPlayer in background. The problem is: if there is a incoming calling interrupts the player, it will never resume unless switch to foreground and do it manually.
The code is simple, to play it in background:
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
url = [[NSURL alloc] initFileURLWithPath:...];
audio_player = [[AVAudioPlayer alloc] initWithContentsOfURL: url error:NULL];
audio_player.delegate = self;
bool ret = [audio_player play];
delegate to handle interruptions:
-(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
//tried this, not working [[AVAudioSession sharedInstance] setActive: NO error: nil];
NSLog(#"-- interrupted --");
}
//----------- THIS PART NOT WORKING WHEN RUNNING IN BACKGROUND ----------
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player
{
NSLog(#"resume!");
//--- tried, not working: [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil];
//--- tried, not working: [[AVAudioSession sharedInstance] setActive: YES error: nil];
//--- tried, not working: [audio_player prepareToPlay];
[audio_player play];
}
Can any one help me?

Found the solution!
I had the same problem, my app was resuming the audio nicely after an interruption, only if my app was open. When it was on the background it failed ro resume playing the audio after an interruption.
I fixed this by adding the following lines of code:
Add this line whenever your app start playing audio.[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
And on the endInterruption method, wait 1 or 2 seconds before resume playing the audio. This to allow the OS to stop using the audio channel.
- (void)endInterruptionWithFlags:(NSUInteger)flags {
// Validate if there are flags available.
if (flags) {
// Validate if the audio session is active and immediately ready to be used.
if (AVAudioSessionInterruptionFlags_ShouldResume) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1), dispatch_get_main_queue(), ^{
// Resume playing the audio.
});
}
}
}
You can also add this line when your app stops (not pause) playing audio. But is not required.
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

Try this
-(void)audioPlayerEndInterruption:(AVAudioPlayer *)audioPlayer withFlags:(NSUInteger)flags{
if (flags == AVAudioSessionFlags_ResumePlay) {
[audioPlayer play];
}
Hope it helps.

Related

ios remote sometimes defaults to native music player instead of my app

About 1 in 20 times my app (after launch) does not seems to 'beginReceivingRemoteControlEvents' and hence the ios remote defaults to the native music player. Because of this my app has been rejected (at an update, not initial release), the thing is I can never see it happen when the iPhone is plugged into mac and xcode only when running the app on the phone, below is my audio initialization routine.
Hope someone can help, thanks Mike.
-(void)viewDidAppear:(BOOL)animated
{
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[[AVAudioSession sharedInstance] setDelegate: self];
//Set the audio category of this app to playback.
NSError *setCategoryError = nil; [[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError];
if (setCategoryError)
{
ShowMessageBox(#"Audio error", #"Category failed");
}
//Activate the audio session
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (activationError) {
ShowMessageBox(#"Audio error", #"Activate failed");
}
}
// And also
- (BOOL)canBecomeFirstResponder
{
return YES;
}

Create a Slider and Custom ActivityIndicator for Streaming Audio

I am Working with streaming URL and I need to create a UISlider(control volume) and an indicator(buffering/loading) like one in the above image.
Code I am using is
_theAudio=[[AVPlayer alloc] initWithURL:streamURL];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[_theAudio play];
refer this Link
or
- (void)updateSlider {
// Updates the slider about the music time
slider.value = player.currentTime;
}
- (IBAction)sliderChanged : (UISlider *)sender {
// Fast skips the music when user scrolls the UISlider
[player stop];
[player setCurrentTime:slider.value];
[player prepareToPlay];
[player play];
}
// Stops the timer when the music is finished
- (void)audioPlayerDidFinishPlaying : (AVAudioPlayer *)player successfully : (BOOL)flag {
// Music completed
if (flag) {
[sliderTimer invalidate];
}
}
for volume control u can add below line in
sliderValue_changed delegate
[audioPlayer setVolume:slider.value];
but first set min slider value to 0 and max to 1.

Buffering label in a stream using MPMoviePlayer

Im making a radio streaming application, my app is working perfectly. I have two buttons in my screen one for playing and the other for pausing, also I have a label that indicates the state of my player. I have no problem making this label show the state "Playing" or "Paused" my problem is that when I press the play button there is a time where the buffer is gathering information and I can't figure out a way to show a "Buffering..." label before the player start to stream the audio.
This is the code Im using for streaming the radio station.
NSString *url = [NSString stringWithFormat:#"http://66.7.218:8816"];
player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:url]];
player.movieSourceType = MPMovieSourceTypeStreaming;
player.view.hidden = YES;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance]setActive:YES error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[player prepareToPlay];
[player play];
Im usin a function I created called changeStatus and this function is called each second to identify the status of the player.
-(void)changeStatus
{
if (player.loadState == MPMovieLoadStateUnknown) {
status.text = #"Buffering...";
}
if(player.playbackState == MPMoviePlaybackStatePlaying)
{
status.text = #"Playing.";
}
if(player.playbackState == MPMoviePlaybackStatePaused)
{
status.text = #"Paused";
}
}
I really need to solve this problem and Ive tried all I can to solve it. Hope you can help me! Thanks in advance.
I achieved to solve the problem. The part that was missing is the if clause which gives the signal to start playing only when the player is prepared to play. Without that the player starts to play before the buffer is completely loaded so it plays no audio. And that was the reason my Playing label showed immediately instead of the buffering one.
NSString *url = [NSString stringWithFormat:#"http://66.7.218:8816"];
player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:url]];
player.movieSourceType = MPMovieSourceTypeStreaming;
player.view.hidden = YES;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance]setActive:YES error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[player prepareToPlay];
if(player.isPreparedToPlay)
{
[player play];
}
Adittionally, the MPMoviePlaybackStateInterrupted refers to the buffering process while doing an stream. So if you want to make something happens in the buffering process refer to this method.
-(void)changeStatus
{
if(player.playbackState == MPMoviePlaybackStatePlaying)
{
status.text = #"Playing.";
}
if(player.playbackState == MPMoviePlaybackStateInterrupted)
{
status.text = #"Buffering...";
}
}
Thanks a lot to the user That Guy who helped me solving this.
Actually there's a notification called
MPMediaPlaybackIsPreparedToPlayDidChangeNotification
The observer will get notified after it is ready to play. It is more or less the same effect but with different mechanism.

AVPlayer doesn't resume playback on endinterruption on iPhone but does so on iPad

I'm writing a radio app for both iPhone and iPad and am running into some weird behaviour when handling pausing and playing the audio with interruptions. I'm using the AVAudioSessionDelegate methods beginInterruption and endInterruption to respectively pause and play the AVPlayer. Below is the relevant play code.
Now, the following situations seem to happen consistently:
On iPad: If I force an interruption (Facetime call), beginInterruption is called as expected and playback stops. If the interruption stops, endInterruption is called as expected, and the playback resumes as expected.
On iPhone: Pressing the play and pause button, triggers pause and play exactly the same as beginInterruption and endInterruption would. Playback behaves as expected.
On iPhone: Forcing an interruption (by calling the phone), calls endInterruption as expected and playback pauses as expected. However, when the interruption is finished, beginInterruption is called as expected, play is called as expected, it actually reaches and executes the [self.player play] line, but playback does not resume! I hear nothing.
Situation 3 above is extremely weird, so I'm wondering if I may have overlooked something. Any ideas?
Play code
- (void)play {
NSError* error = nil;
AVKeyValueStatus keyStatus = [currentAsset statusOfValueForKey:#"tracks" error:&error];
if(error){
DLog(#"Error %#", [error localizedDescription]);
}
else {
DLog(#"Current Key Status: %i", keyStatus);
if(keyStatus == AVKeyValueStatusLoaded){
DLog(#"Continue playing source: %#", self.source);
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error) {
DLog(#"Error during play while setting AVAudioSessionCategory: %#", [error localizedDescription]);
}
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (error) {
DLog(#"Error during play while setting AVAudioSessionCategory: %#", [error localizedDescription]);
}
[[AVAudioSession sharedInstance] setDelegate:self];
if(backgroundTaskID != UIBackgroundTaskInvalid){
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
}
backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
[self.player play];
[self setStatus:kNPOMediaPlayerStatusPlaying];
}
else {
DLog(#"PlaySource: %#", self.source);
[self playSource:self.source];
}
}}
I had the same issue. Issue disappeared after I implemented remote control event handling. I called [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];when starting playback.
It turns out that this is a known bug in iOS, which requires some careful work-arounds in the applicationDidBecomeActive to handle beginInterruption. Sadly, I couldn't figure out another solution.

How to play even background mode using MPMoviePlayerController

Audio Live Streaming Service using IIS7 is prepared.
Audio Live streaming server setting is very complex, but have succeeded.
I obtained a URL for streaming like this(#"http://xxx.xxx.xx.xx/liveStream.isml/manifest(format=m3u8-aapl).m3u8").
My using code is unexpectedly simple.
self.theURL = [NSURL URLWithString:#"http://xxx.xxx.xx.xx/liveStream.isml/manifest(format=m3u8-aapl).m3u8"];
if(moviePlayer == nil)
{
moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:theURL];
}
[moviePlayer play];
Hm... I can't control music player as I wished.
Question is like this.
I don't want to stop to play music even though backgroundMode.
Please tell me some advice. Thanks.
please check in only device.I have also done in my Application. My code is given below.i am talking about iOS5
And in plist i made an array named "Required background mode" and insert an item in array name, "App Plays Audio". some times URL problem is occurs check this URL MPMoviePlayer sound working in Simulators and ipad device, but not Working in iPhone Device
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryErr];
[[AVAudioSession sharedInstance] setActive: YES error: &activationErr];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
}