kAudioSessionOverrideAudioRoute_Speaker issue - iphone

i need to trigger the iphone audio route back and forth from speaker to normal mode very quickly. I've created an audio Session similar to this site:
http://atastypixel.com/blog/using-remoteio-audio-unit/
Im creating a app that can do VOIP.
So let me explain the simple issue i have. I have a button that toggles from speaker to normal mode like this:
if(speakerState){
value = kAudioSessionOverrideAudioRoute_None;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerState = false;
}
else {
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerState = true;
}
... you get the idea just toggling the speaker on and off.
Here is the issue: When the end user RAPIDLY hits the button to toggle the speaker after about 10 presses the application freezes for a while as if its releasing something and after 30 seconds comes back. I need the end user to be able to constantly tap this button on/off as much as they want.
update: I tried calling the speakers in the background thread but still if i press the speakers too many times it freezes or i loose audio here is the code:
-(void) manageSpeakerState{
OSStatus error;
UInt32 value;
if(speakerState){
value = kAudioSessionOverrideAudioRoute_None;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerBtn.selected = false;
speakerState = false;
}
else {
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerBtn.selected = true;
speakerState = true;
}
}
// this gets called whenever the speaker button is pressed ..it toggles the speakers on/off
-(IBAction) speaker
{
[self performSelectorInBackground:#selector(manageSpeakerState) withObject:nil ];
}
UPDATE: My audio session is running on a seperate thread using pthread and its in a .c file. It could be a concurrency issue as it seems when the main thread gets tied up this issue occurs. How can i update the audio route on the thread that the audio session is running on ?
UPDATE: i have the same issue as this: http://lists.apple.com/archives/coreaudio-api/2012/Jul/msg00129.html
Whenever i change routes (ie. speaker or headphone) my audioUnits are paused so im reading empty stuff into the buffers. Its almost like a hardware manufacturer issue. How can i get around this ? the call backs are still being called but theres no audio unit while switching routes.

On button click just change button's background image and tag.Change audio session property in background thread.

In your application delegate just add this piece of code..I hope it work fine.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];

Related

How to resume spotify player playback after interrupt when app is in background?

I have an app which plays Spotify track. It works perfectly when app moves to background but when it get interrupted by 'call' or 'iOS pop-ups' it stops playing.
I have added Required background modes to App plays audio in .plist.
How to resume playback when interruption ended while app is in background.
i have used audio interruption callbacks.
AudioSessionInitialize (NULL, NULL, interruptionListenerCallback,self);
AudioSessionSetActive(YES);
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
And after receiving interruption call play/pause method of playback manager.
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState)
{
// This callback, being outside the implementation block, needs a reference
//to the AudioPlayer object
CustomPlayerView *controller = (__bridge CustomPlayerView *) inUserData;
if (interruptionState == kAudioSessionBeginInterruption)
{
[controller.playbackManager setIsPlaying:NO];
}
else if (interruptionState == kAudioSessionEndInterruption)
{
[controller.playbackManager setIsPlaying:YES];
}
}

Is it possible to play video using Avplayer in Background?

I am using Avplayer to show video clips and when i go back (app in background) video stop. How can i keep playing the video?
I have search about background task & background thread ,IOS only support music in background (Not video)
http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
here is some discussion about play video in background
1) https://discussions.apple.com/thread/2799090?start=0&tstart=0
2) http://www.cocoawithlove.com/2011/04/background-audio-through-ios-movie.html
But there are many apps in AppStore, that play video in Background like
Swift Player : https://itunes.apple.com/us/app/swift-player-speed-up-video/id545216639?mt=8&ign-mpt=uo%3D2
SpeedUpTV : https://itunes.apple.com/ua/app/speeduptv/id386986953?mt=8
This method supports all the possibilities:
Screen locked by the user;
List item
Home button pressed;
As long as you have an instance of AVPlayer running iOS prevents auto lock of the device.
First you need to configure the application to support audio background from the Info.plist file adding in the UIBackgroundModes array the audio element.
Then put in your AppDelegate.m into
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions:
these methods
[[AVAudioSession sharedInstance] setDelegate: self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
and #import < AVFoundation/AVFoundation.h >
Then in your view controller that controls AVPlayer
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
and
- (void)viewWillDisappear:(BOOL)animated
{
[mPlayer pause];
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
then respond to the
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
if([mPlayer rate] == 0){
[mPlayer play];
} else {
[mPlayer pause];
}
break;
case UIEventSubtypeRemoteControlPlay:
[mPlayer play];
break;
case UIEventSubtypeRemoteControlPause:
[mPlayer pause];
break;
default:
break;
}
}
Another trick is needed to resume the reproduction if the user presses the home button (in which case the reproduction is suspended with a fade out).
When you control the reproduction of the video (I have play methods) set
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
and the corresponding method to be invoked that will launch a timer and resume the reproduction.
- (void)applicationDidEnterBackground:(NSNotification *)notification
{
[mPlayer performSelector:#selector(play) withObject:nil afterDelay:0.01];
}
Its works for me to play video in Backgorund.
Thanks to all.
If you try to change the background mode:
Sorry, App store wont approve it.MPMoviePlayerViewController playback video after going to background for youtube
In my research, someone would take the sound track out to play in te background when it goes into background as the video would be pause and get the playbacktime for resume playing when go into foreground
It is not possible to play background music/video using Avplayer. But it is possible using
MPMoviePlayerViewController. I have done this in one of my app using this player & this app
is run successfully to appstore.
Try with this snippet, I've already integrated this with my app & it's being useful for me..hope this will work for you!!
Follow the steps given below:
Add UIBackgroundModes in the APPNAME-Info.plist, with the selection
App plays audio
Then add the AudioToolBox framework to the folder frameworks.
In the APPNAMEAppDelegate.h add:
-- #import < AVFoundation/AVFoundation.h>
-- #import < AudioToolbox/AudioToolbox.h>
In the APPNAMEAppDelegate.m add the following:
// Set AudioSession
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
/* Pick any one of them */
// 1. Overriding the output audio route
//UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
//AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute,
sizeof(audioRouteOverride), &audioRouteOverride);
========================================================================
// 2. Changing the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
into the
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
but before the two lines:
[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];
Enjoy Programming!!
Swift version for the accepted answer.
In the delegate:
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
In the view controller that controls AVPlayer
override func viewDidAppear(animated: Bool) {
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
self.becomeFirstResponder()
}
override func viewWillDisappear(animated: Bool) {
mPlayer.pause()
UIApplication.sharedApplication().endReceivingRemoteControlEvents()
self.resignFirstResponder()
}
Don't forget to "import AVFoundation"
In addition to fattomhk's response, here's what you can do to achieve video forwarding to the time it should be after your application comes in foreground:
Get currentPlaybackTime of playing video when go to background and store it in lastPlayBackTime
Store the time when application goes to background (in userDefault probably)
Again get the time when application comes in foreground
Calculate the duration between background and foreground time
Set current playback time of video to lastPlayBackTime + duration
If you are Playing video using WebView you can handle using javascript to play video on background.
strVideoHTML = #"<html><head><style>body.........</style></head> <body><div id=\"overlay\"><div id=\"youtubelogo1\"></div></div><div id=\"player\"></div> <script> var tag = document.createElement('script'); tag.src = \"http://www.youtube.com/player_api\"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player; events: { 'onReady': onPlayerReady, } }); } function onPlayerReady(event) { event.target.playVideo(); } function changeBG(url){ document.getElementById('overlay').style.backgroundImage=url;} function manualPlay() { player.playVideo(); } function manualPause() { player.pauseVideo(); } </script></body> </html>";
NSString *html = [NSString stringWithFormat:strVideoHTML,url, width, height,videoid;
webvideoView.delegate = self;
[webvideoView loadHTMLString:html baseURL:[NSURL URLWithString:#"http://www.your-url.com"]];
On view diappear call
strscript = #"manualPlay();
[webvideoView stringByEvaluatingJavaScriptFromString:strscript];
I'd like to add something that for some reason ended up being the culprit for me. I had used AVPlayer and background play for a long time without problems, but this one time I just couldn't get it to work.
I found out that when you go background, the rate property of the AVPlayer sometimes seems to dip to 0.0 (i.e. paused), and for that reason we simply need to KVO check the rate property at all times, or at least when we go to background. If the rate dips below 0.0 and we can assume that the user wants to play (i.e. the user did not deliberately tap pause in remote controls, the movie ended, etc) we need to call .play() on the AVPlayer again.
AFAIK there is no other toggle on the AVPlayer to keep it from pausing itself when app goes to background.

AVAudioSessionCategoryAmbient in IPhone background not working

Im just trying to play a simple audio file. Here is the scenario, the app is started and i "prepare to play" the audio on viewdidLoad. When i go into the BACKGROUND i need to play music for a brief moment but it wont play in ambient category but AVAudioSessionCategoryPlayback works fine. I need it to be ambient category so the user can mute the sound if they want.
-(void) startRing
{
{
OSStatus error;
UInt32 value;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryAmbient error: nil];
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
if (error)
NSLog(#"couldn't set kAudioSessionOverrideAudioRoute_Speaker!");
[ringTonePlayer play];
NSLog(#"ringing %s",[ringTonePlayer isPlaying]?"true":"false");//this always shows false when im in the background
self->ringing = true;
}
}
in my info plist im putting that this app uses audio in the background as a background mode. the issue is i need to START the audio while in the background but the system wont let me in ambient mode.

AudioServicesAddSystemSoundCompletion callback not getting called in silent mode

If I put device into silent mode using switch, AudioServicesAddSystemSoundCompletion callback method doesn't get called. If the switch is on, I mean if device is NOT in silent mode, method gets called perfectly.
Has anyone experienced something like this. Is this a bug?
I had the same problem. It is not a bug. If the sound is not played, due to the device being muted the call back never gets called.
A work around is to use an NSTimer that is the same length as the sound to be played. If the sound doesn't play the timer call back gets called. Which can perform the same code as your callback.
Here is how you can use the NSTimer for soundDidFinishPlaying callback even in Silent Mode.
- (IBAction)playSelectedSound:(id)sender {
if (!self.isPlaying)
{
// playing the sound
NSString *fileName = [soundsFileNames objectAtIndex:self.selectedIndex];
SystemSoundID topClick;
NSBundle *bundle = [NSBundle mainBundle];
NSString *topClikFile = [bundle pathForResource:fileName ofType:#"aiff"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL URLWithString:topClikFile], &topClick);
AudioServicesPlaySystemSound(topClick);
// getting the file duration
AudioFileID audioFileID;
AudioFileOpenURL((__bridge CFURLRef)[NSURL URLWithString:topClikFile], kAudioFileReadPermission, 0, &audioFileID);
NSTimeInterval seconds;
UInt32 propertySize = sizeof(seconds);
OSStatus st = AudioFileGetProperty(audioFileID, kAudioFilePropertyEstimatedDuration, &propertySize, &seconds);
// fire the timer
if (st == 0)
{
[NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:#selector(soundDidFinishPlaying) userInfo:nil repeats:NO];
}
self.isPlaying = YES;
}
}
- (void)soundDidFinishPlaying {
self.isPlaying = NO;
}
The source code for Sound Switch (with demo project) indicates that the callback does in fact occur also if the device is in silent mode.
I've just tried this with iOS 8.1 on an iPhone 5 and silent mode turned off/on with the button on the device.

how to resume recording after interruption occured in iphone?

I am working on an audio recorder application, it is working perfectly fine.
But I'm stuck with the problem of interruption. When a call comes,
- (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder
then this method is called and the recording is paused.
And if the user rejects the call:
- (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder
Then here I want to resume the recording from the point where it was interrupted.
But when I call the record method again, the recording starts with a new file.
The problem is solved!
I have re-written the recording code to avoid this problem. I used AudioQueues, basically the backend code is the same of SpeakHere application with some minor changes. Provided two more apis in it:
-(void)resume
{
AudioQueueStart(queueObject, NULL);
}
-(void)pause
{
AudioQueuePause(queueObject);
}
In AudioRecorder class. The basic purpose being to avoid the recording setup which is done in record method.
Setup the interrupt callback and then use this pause and resume methods appropriately in the callback. Also take care to set active the audio session based on whether your application needs it or not.
Hope this helps someone out there.
EDIT:
Audio interrupt listener callback:
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState)
{
if (interruptionState == kAudioSessionBeginInterruption)
{
[self.audioRecorder pause];
}
else if (interruptionState == kAudioSessionEndInterruption)
{
// if the interruption was removed, and the app had been recording, resume recording
[self.audioRecorder resume];
}
}
Listening for audio interruption:
AudioSessionInitialize(NULL, NULL, interruptionListenerCallback, self);
The OS is probably stopping the AVAudioRecorder during the interruption. You can either present the two or more files as a single recording to the user, or you can use AudioQueue to write your code for handling interruptions (as well as saving audio data to a file).
I tried to do this a while ago and came to the conclusion that it couldn't be done properly; the operating system itself won't let you do it. Maybe that's changed in 3.1.2, but when I tried in 3.0 it just wouldn't work.
To handle the interruption, you have to use AVAudioSessionDelegate methodes instead of AVAudioRecorderDelegate methods.This is the code sample for handling interruption:
/*=================================================================
Interruption Handling Method during Recording
==================================================================*/
- (void) beginInterruption
{
if(self.recorder)
{
//Method to handle UI
}
}
- (void) endInterruption
{
NSError *err = noErr;
[[AVAudioSession sharedInstance] setActive: YES error: &err];
if(err != noErr)
{
NSLog([err description]);
}
//method to handle UI
}
First method automatically deactivate the audio session.So,in second method,you have to reactivate the audio session.First method will pause the recording and when interruption ends you can resume with second method.I have tried this on version 3.0 and upper version.