Background Audio for iOS Video App - ios

So I was working on a video capture app that plays background audio (from Spotify or Apple Music) and I'm having a small problem where there's a small audio interruption when I open my app while audio is being played.
Here's what I have for allowing background audio to play (located in my didFinishLaunchingWithOptions in my AppDelegate class:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers
error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
Any clues to stopping that beginning interruption? Thanks!!
EDIT
I should also mention after setting the AVAudioSession I am setting my AVCaptureSession. I initialize it then set the properties.
self.session.usesApplicationAudioSession = YES;
self.session.automaticallyConfiguresApplicationAudioSession = NO;

I think the reason of interruption is you are updating category in every case. You may use below function to check and update category only if it is needed.
-(BOOL) checkAndUpdateCategory {
NSError *error;
AVAudioSession *session = [AVAudioSession sharedInstance];
BOOL result = [session.category isEqualToString:AVAudioSessionCategoryPlayAndRecord];
if(!result) {
result = [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:&error];
if(error) {
//Handle Error
NSLOG(#"Error:%#", error);
}
}
return result;
}

Related

How can stream Audio when iPhone is locked?

I'm working on AppRTC for Audio call, It's working fine when app is open. Audio call is not working when device is locked, If wI un-lock device while call and open app, It's start working normally but while it's locked it's not working. (Audio is not passing or playing).
I already added "App plays audio or streams audio/video using AirPlay" in "Required background modes" of plist.
Audio session is also configured. Please check code below.
- (void)configureAVAudioSession:(BOOL)isSpeakerOn
{
// Get your app's audioSession singleton object
AVAudioSession *session = [AVAudioSession sharedInstance];
// Error handling
BOOL success;
NSError *error;
// set the audioSession category.
// Needs to be Record or PlayAndRecord to use audioRouteOverride:
success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
error:&error];
if (!success) {
NSLog(#"AVAudioSession error setting category:%#",error);
}
// Set the audioSession override
success = [session overrideOutputAudioPort:isSpeakerOn?AVAudioSessionPortOverrideSpeaker:AVAudioSessionPortOverrideNone
error:&error];
if (!success) {
NSLog(#"AVAudioSession error overrideOutputAudioPort:%#",error);
}
// Activate the audio session
success = [session setActive:YES error:&error];
if (!success) {
NSLog(#"AVAudioSession error activating: %#",error);
}
else {
NSLog(#"AudioSession active");
}
}
Please let me know if anything is missing or i'd implemented wrongly.
Regards,

No audio from AVCaptureSession after changing AVAudioSession

I'm making an app that supports both video playback and recording. I always want to allow background audio mixing except for during video playback (during playback, background audio should be muted). Therefore, I use the two methods below while changing the state of playback. When my AVPlayer starts loading, I call MuteBackgroundAudio, and when I dismiss the view controller containing it, I call ResumeBackgroundAudio. This works as expected, and the audio returns successfully after leaving playback.
The issue is that after doing this at least once, whenever I record anything using AVCaptureSession, no sounds gets recorded. My session is configured like so:
AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
if (error)
{
NSLog(#"%#", error);
}
if ([self.session canAddInput:audioDeviceInput])
{
[self.session addInput:audioDeviceInput];
}
[self.session setAutomaticallyConfiguresApplicationAudioSession:NO];
// ... videoDeviceInput
Note that I have not set usesApplicationAudioSession, so it defaults to YES.
void MuteBackgroundAudio(void)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if ([[AVAudioSession sharedInstance] isOtherAudioPlaying] && !isMuted)
{
isMuted = YES;
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
error:&error];
if (error)
{
NSLog(#"DEBUG - Set category error %ld, %#", (long)error.code, error.localizedDescription);
}
NSError *error2 = nil;
[[AVAudioSession sharedInstance] setActive:YES
withOptions:0
error:&error2];
if (error2)
{
NSLog(#"DEBUG - Set active error 2 %ld, %#", (long)error.code, error.localizedDescription);
}
}
});
}
void ResumeBackgroundAudio(void)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (isMuted)
{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *deactivationError = nil;
[audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&deactivationError];
if (deactivationError)
{
NSLog(#"DEBUG - Failed at deactivating audio session, retrying...");
ResumeBackgroundAudio();
return;
}
isMuted = NO;
NSLog(#"DEBUG - Audio session deactivated");
NSError *categoryError = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers
error:&categoryError];
if (categoryError)
{
NSLog(#"DEBUG - Failed at setting category");
return;
}
NSLog(#"DEBUG - Audio session category set to mix with others");
NSError *activationError = nil;
[audioSession setActive:YES error:&activationError];
if (activationError)
{
NSLog(#"DEBUG - Failed at activating audio session");
return;
}
NSLog(#"DEBUG - Audio session activated");
}
});
}
Debugging
I have noticed that the audioSession always needs two tries to successfully deactivate after calling ResumeBackgroundAudio. It seems my AVPlayer does not get deallocated or stopped in time, due to this comment in AVAudioSession.h:
Note that this method will throw an exception in apps linked on or
after iOS 8 if the session is set inactive while it has running or
paused I/O (e.g. audio queues, players, recorders, converters, remote
I/Os, etc.).
The fact that no sound gets recorded bring me to believe the audioSession does not actually get activated, but my logging says it does (always in the second iteration of the recursion).
I got the idea of using recursion to solve this problem from this post.
To clarify, the flow that causes the problem is the following:
Open app with Spotify playing
Begin playback of any content in the app
Spotify gets muted, playback begins (MuteBackgroundAudio)
Playback ends, Spotify starts playing again (ResumeBackgroundAudio)
Start recording
Stop recording, get mad that there is no audio
I've had the exact same issue as you're describing, down to the very last detail ([audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&deactivationError]; failed the first time no matter what)
The only acceptable way, for my case, was to stop then start the AVCaptureSession. I'm not entirely sure but I think this is the same exact way Whatsapp handles it - their camera behaves exactly like mine with the solution I'm suggesting.
Removing and adding the same audio input on the already running session also seemed to work but I got a terrible camera freeze - nothing compared to the session start / stop.
The recursion solution is nice, but I think it would be a great idea to 'throttle' that recursion call (add a short delay & a max retry count of some sort) in case for a legit reason the set fails every time. Otherwise your stack will overflow and your app will crash.
If you or anyone found a better solution to this I would love to know it.

Audio playing and recording is not working with iOS 10

I'm unable to play any audio file in iOS 10, Even Default keyboard click sound also not playing.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];
In Video recording, (I'm using AVCaptureConnection), Sometimes it's record sound and sometime not.
I'm using LLSimpleCamera for record video.
Can anyone suggest me?
Regards
I had the same problem with LLSimpleCamera, with this code
#property(readonly, strong, nonatomic) LLSimpleCamera *camera;
//...
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
[[self camera] startRecordingWithOutputUrl:path didRecord:^(LLSimpleCamera *camera, NSURL *outputFileUrl, NSError *error)
{ /*do something*/ }
When I setup category much more earlier before calling startRecordingWithOutputUrl:
the problem doesn't appear. So, put some code after setCategory: and check.
I know that it is not a "true" solution, but it works for me after 6 hours of hard working.
Cannot reproduce the issue with this code:
[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
/* do some UI-related stuff, hide buttons, show bars, some animation */
[[self camera] startRecordingWithOutputUrl:path didRecord:^(LLSimpleCamera *camera, NSURL *outputFileUrl, NSError *error)
{ /*do something*/ }
P.S. Reproduced only on iPhone 7

AVSpeechSynthesizer won't work when phone locked

I am trying to use AVSpeechSynthesizer when the phone is locked, but the audio stops when I lock the screen. I am using the simulator, not an actual device. I have seen a couple other questions similar to this on this site and I followed their recommendations, but it still does not work.
In the app delegate I set the audio session category to AVAudioSessionCategoryPlayback.
- (void)configureAudioSession{
NSError *error = NULL;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
if(error) {
NSLog(#"Error setting category of audio session: %#",error.description);
}
error = NULL;
[[AVAudioSession sharedInstance] setActive:YES error: &error];
if (error) {
NSLog(#"Error activating audio session: %#",error.description);
}
}
And I checked the 'Audio and Airplay' mode under Project Settings->Capabilities->Background Modes.
Can anyone tell me how to get this to work?
Here's how I got AVSpeechSynthesizer to continue speaking when phone goes idle, if phone gets locked, or if app goes to background. (iOS8)
Step 1) Open up info.plist and add the key "Required background modes". Within this array, add a String called "App plays audio or streams audio/video using AirPlay".
Step 2) Add the following to your app delegate didFinishLaunchingWithOptions:
NSError *error = NULL;
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
if(error) {
// Do some error handling
}
[session setActive:YES error:&error];
if (error) {
// Do some error handling
}
Step 3) Run on your device and test it out!
I was able to follow this post to get it to work about a month ago:
AVAudioPlayer stops playing on screen lock even though the category is AVAudioSessionCategoryPlayback
Basically, you need to make entry in your app's .plist file. I'm not sure this will work on the simulator, so you will probably want to test it on an actual device.

How to play audio with OutputAudioQueue in silent mode? [duplicate]

I want to play a sound even in silent mode in iPhone.
Can it be done by using AVAudioPlayer (Without using AVAudioSession)
(For ios 3.0+)
Thanks in advance.
Actually, you can do this. It is controlled via the Audio Session and has nothing to do with AVAudioPlayer itself. Why don't you want to use AudioSession? They play nice together...
In your app, you should initialize the Audio Session, and then you can also tell indicate what kind of audio you intend to play. If you're a music player, then it sort of makes sense that the user would want to hear the audio even with the ring/silent switch enabled.
AudioSessionInitialize (NULL, NULL, NULL, NULL);
AudioSessionSetActive(true);
// Allow playback even if Ring/Silent switch is on mute
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory,
sizeof(sessionCategory),&sessionCategory);
I have an app that I do this very thing, and use AVAudioPlayer to play audio, and with the ring/silent switch enabled, I can hear the audio.
UPDATE (11/6/2013)
In the app I mentioned above, where I used the code above successfully, I have (for some time) been using the following code instead to achieve the same result:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
BOOL result = NO;
if ([audioSession respondsToSelector:#selector(setActive:withOptions:error:)]) {
result = [audioSession setActive:YES withOptions:0 error:&error]; // iOS6+
} else {
[audioSession setActive:YES withFlags:0 error:&error]; // iOS5 and below
}
if (!result && error) {
// deal with the error
}
error = nil;
result = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
if (!result && error) {
// deal with the error
}
I thought I'd post this as an alternative, in light of the most recent comment to this answer. :-)
MarkGranoff's solution is correct. However, if you prefer to do it in Obj-c instead of C, the following works as well:
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];
The above answers are correct. Following is the Swift version.
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
//print("AVAudioSession Category Playback OK")
do {
try AVAudioSession.sharedInstance().setActive(true)
//print("AVAudioSession is Active")
} catch _ as NSError {
//print(error.localizedDescription)
}
} catch _ as NSError {
//print(error.localizedDescription)
}
Swift 4 simple version:
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
This will simply do the trick (using AVAudioSession)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)

Resources