How to Play Audio in background in IPhone? - objective-c

I'm trying to play audio file at particular time which i have set.for that I have added audio in "UIBackgroundModes" in info.plist and backgroundtaskidentifier in didEnterBackground.Its working in simulator but not in device.Can anyone help to solve this.
Thanks in Advance...

You can check out this reference app with streaming and backgrounding of audio.
EDIT:
Example of a local notification w/ attached sound. More detail here.
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIInvalidBackgroundTask, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(#"%# has a message for you.", nil), friend];
localNotif.alertAction = NSLocalizedString(#"Read Message", nil);
localNotif.soundName = #"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
friend = nil;
break;
}
}
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}

If I understand you correctly, you want to play a sound at a particular time, no matter if your app is active and foregrounded at that time? Your only (app review-friendly) option is to schedule a local notification, as in slf's answer. UIBackgroundModes won't help you: the audio background mode is for being allowed to execute code while backgrounded only when you start playing audio when you are foregrounded. Example usages are music players and radio apps.
If you explain your use case more thoroughly, I might have a better answer.

Related

NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier : Download stops after the app is pushed into background

This method sets the background object.
- (void) downloadWithURL: (NSMutableArray *)urlArray
pathArr: (NSMutableArray *)pathArr
mediaInfo: (MediaInfo *)mInfo
{
bgDownloadMediaInfo = mInfo;
reqUrlCount = urlArray.count;
dict = [NSDictionary dictionaryWithObjects:pathArr
forKeys:urlArray];
mutableDictionary = [dict mutableCopy];
backgroundConfigurationObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"XXXXX"];
backgroundConfigurationObject.sessionSendsLaunchEvents = YES;
backgroundConfigurationObject.discretionary = YES;
backgroundSession = [NSURLSession sessionWithConfiguration: backgroundConfigurationObject
delegate: self delegateQueue: [NSOperationQueue currentQueue]];
self.requestUrl = [urlArray objectAtIndex:0];
download = [backgroundSession downloadTaskWithURL:self.requestUrl];
[download resume];
}
These are the completion handlers.
#pragma Mark - NSURLSessionDownloadDelegate
- (void)URLSession: (NSURLSession *)session
downloadTask: (NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL: (NSURL *)location
{
LogDebug(#"Download complete for request url (%#)", downloadTask.currentRequest.URL);
NSString *temp = [mutableDictionary objectForKey:downloadTask.currentRequest.URL];
NSString *localPath = [NSString stringWithFormat: #"%#",temp];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *destinationURL = [NSURL fileURLWithPath: localPath];
NSError *error = nil;
[fileManager moveItemAtURL:location toURL:destinationURL error:&error];
LogDebug(#"Moving download file at url : (%#) to : (%#)", downloadTask.currentRequest.URL, destinationURL);
reqUrlCount --;
downloadSegment ++;
// Handover remaining download requests to the OS
if ([finalUrlArr count] != 0) {
// remove the request from the array that got downloaded.
[finalUrlArr removeObjectAtIndex:0];
[finalPathArr removeObjectAtIndex:0];
if ([finalUrlArr count] > 0) {
// proceed with the next request on top.
self.requestUrl = [finalUrlArr objectAtIndex:0];
download = [backgroundSession downloadTaskWithURL:self.requestUrl];
[download resume];
}
}
if ([adsArray count] > 0) {
adsArrayCount --;
// delegate back once all the ADs segments have been downloaded.
if (adsArrayCount == 0) {
for (int i = 0; i < [adsArray count]; i++) {
NSArray *ads = [adsArray objectAtIndex: i];
for (int j = 0; j < [ads count]; j++) {
MediaInfo *ad = [ads objectAtIndex: j];
[self setDownloadComplete: ad];
// skip sending downloadFinish delegate if the media is marked as downloadDone
if (!ad.downloadDone) {
[delegate MediaDownloadDidFinish: ad.mediaId error: NO];
}
ad.downloadDone = YES;
}
}
downloadSegment = 0;
}
}
// delegate back once all the main media segments have been downloaded.
if (reqUrlCount == 0) {
[self setDownloadComplete: mediaInfo];
state = DownloadState_Done;
// skip sending downloadFinish delegate if the media is marked as downloadDone
if (!bgDownloadMediaInfo.downloadDone) {
[delegate MediaDownloadDidFinish: bgDownloadMediaInfo.mediaId error: NO];
}
bgDownloadMediaInfo.downloadDone = YES;
[urlArr release];
[pathArr release];
[finalUrlArr release];
[finalPathArr release];
// invalidate the NSURL session once complete
[backgroundSession invalidateAndCancel];
}
}
- (void)URLSession: (NSURLSession *)session
task: (NSURLSessionTask *)task
didCompleteWithError: (NSError *)error
{
if (error) {
NSLog(#"Failure to download request url (%#) with error (%#)", task.originalRequest.URL, error);
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
// save the total downloaded size
[self downloaderDidReceiveData:bytesWritten];
// enable the log only for debugging purpose.
// LogDebug(#"totalBytesExpectedToWrite %llu, totalBytesWritten %llu, %#", totalBytesExpectedToWrite, totalBytesWritten, downloadTask.currentRequest.URL);
}
With out this code(beginBackgroundTaskWithExpirationHandler) the download stops when the app is pushed into background.
// AppDelegate_Phone.m
- (void)applicationDidEnterBackground: (UIApplication *)application
{
NSLog(#"applicationDidEnterBackground");
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
Have you implemented application:handleEventsForBackgroundURLSession:completionHa‌​ndler: in your app delegate? That should save the completion handler and start background session with the specified identifier.
If you don't implement that method, your app will not be informed if the download finishes after the app has been suspended (or subsequently terminated in the course of normal app lifecycle). As a result, it might look like the download didn't finish, even though it did.
(As an aside, note that if the user force-quits the app, that not only terminates the download, but obviously won't inform your app that the download was terminated until the user manually restarts the app at some later point and your app re-instantiates the background session. This is a second-order concern that you might not worry about until you get the main background processing working, but it's something to be aware of.)
Also, your URLSessionDidFinishEventsForBackgroundURLSession: must call that saved completion handler (and dispatch this to the main queue).
Also, your design looks like it will issue only one request at a time. (I'd advise against that, but let's just assume it is as you've outlined above.) So, let's imagine that you have issued the first request and the app is suspended before it's done. Then, when the download is done, the app is restarted in the background and handleEventsForBackgroundURLSession is called. Let's assume you fixed that to make sure it restarts the background session so that the various delegate methods can be called. Make sure that when you issue that second request for the second download that you use the existing background session, not instantiating a new one. You can have only one background session per identifier. Bottom line, the instantiation of the background session should be decoupled from downloadWithURL:pathArr:mediaInfo:. Only instantiate a background session once.
Add "Required background modes" in your .plist
There, add the item "App downloads content from the network"

How to play audio in notification action in ios8?

I'm trying to use new ios8 notification actions to let users play audio from their lock screen.
I manage to download the data in the background, but the avaudioplayer does not seem to play.
Any help would be greatly appreciated !
Here is the code :
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler
{
//handle the actions
if ([identifier isEqualToString:#"ACCEPT_IDENTIFIER"]){
Message *newMessage = [Message rawMessageToInstance:[userInfo valueForKey:#"message"]];
AVAudioSession* session = [AVAudioSession sharedInstance];
BOOL success; NSError* error;
success = [session setCategory:AVAudioSessionCategoryPlayback
error:&error];
if (!success)
NSLog(#"AVAudioSession error setting category:%#",error);
[session setActive:YES error:nil];
NSLog(#"%lu",newMessage.identifier);
[ApiUtils downloadAudioFileAtURL:[newMessage getMessageURL] success:^void(NSData *data) {
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:data error:nil];
[player setVolume:2];
[player play];
[self performSelector:#selector(completeNotif:) withObject:completionHandler afterDelay:[player duration]];
} failure:^(){
completionHandler();
}];
}
}
I had the same problem ... first I came up, it must have to do something with background and sound playing options - so I added the background audio flag to the info.plist.
With standard AVAudio stuf it works on the simulator, but not on the device. The special hook is, that you want to start the audio while you are in background - so add the code from anger: How to use kAudioSessionProperty_OverrideCategoryMixWithOthers before starting with the AVAudioPlayer and it will also work on the device.
Hope that helps, for me it works.

Stop radio after call

I'm making an iphone Live radio application. My application is working fine in background mode. But, if the phone rings when radio app is playing in background mode, my radio stops.
MP AVAudioSessionDelegateMediaPlayerOnly end interruption. Interruptor <Phone> category <completed> resumable <0>, _state = 6
2013-01-02 10:26:24.487 Radio99[2527:707] MP endInterruptionFromInterruptor :: resuming playback at 1.000000
Use only AudioStreamer. (Never use AVAudioPlayer or MPMoviePlayerController it's not play radio after call).
NSURL *url =[NSURL URLWithString:#"http://y1.eoews.com/assets/ringtones/2012/5/18/34049/oiuxsvnbtxks7a0tg6xpdo66exdhi8h0bplp7twp.mp3"];
AudioStreamer *tempStreamer = [[AudioStreamer alloc] initWithURL:url];
self.streamer = tempStreamer;
[tempStreamer release]; [streamer start];
Add this code in background
- (void)applicationDidEnterBackground:(UIApplication *)application
{
CTCallCenter *_center = [[CTCallCenter alloc] init];
_center.callEventHandler = ^(CTCall *call)
{
if ([call.callState isEqualToString:CTCallStateIncoming])
{
NSLog(#"Pause");
[streamPlayer pause];
}
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
NSLog(#"call:%#", call.callState);
NSLog(#"Play");
[streamPlayer start];
}
};
}
}

iPad capturing 16:9 photos

I am building a prototype app on iOS, and I’m cannibalizing some Apple sample code to do it (thin ice, I know—this code uses goto statements :\ ). I am using the AVCam project from Session 520 - What's New in Camera Capture. I don’t need video capture capability, just still photos.
The device inputs and outputs are set up thusly:
// Init the device inputs
AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:nil];
AVCaptureDeviceInput *newAudioInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self audioDevice] error:nil];
// Setup the still image file output
AVCaptureStillImageOutput *newStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = #{AVVideoCodecKey: AVVideoCodecJPEG};
[newStillImageOutput setOutputSettings:outputSettings];
// Create session (use default AVCaptureSessionPresetHigh)
AVCaptureSession *newCaptureSession = [[AVCaptureSession alloc] init];
// Add inputs and output to the capture session
if ([newCaptureSession canAddInput:newVideoInput]) {
[newCaptureSession addInput:newVideoInput];
}
if ([newCaptureSession canAddInput:newAudioInput]) {
[newCaptureSession addInput:newAudioInput];
}
if ([newCaptureSession canAddOutput:newStillImageOutput]) {
[newCaptureSession addOutput:newStillImageOutput];
}
[self setStillImageOutput:newStillImageOutput];
[self setVideoInput:newVideoInput];
[self setAudioInput:newAudioInput];
[self setSession:newCaptureSession];
And here is the method that’s called when I tap the shutter button:
- (void) captureStillImage
{
AVCaptureConnection *stillImageConnection = [[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo];
if ([stillImageConnection isVideoOrientationSupported])
[stillImageConnection setVideoOrientation:orientation];
[[self stillImageOutput]
captureStillImageAsynchronouslyFromConnection:stillImageConnection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
ALAssetsLibraryWriteImageCompletionBlock completionBlock = ^(NSURL *assetURL, NSError *error) {
if (error)
{
if ([[self delegate] respondsToSelector:#selector(captureManager:didFailWithError:)])
{
[[self delegate] captureManager:self didFailWithError:error];
}
}
};
if (imageDataSampleBuffer != NULL)
{
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
UIImage *image = [[UIImage alloc] initWithData:imageData];
if ([self.delegate respondsToSelector:#selector(captureManagerCapturedImage:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate captureManagerCapturedImage:image];
});
}
[library writeImageToSavedPhotosAlbum:[image CGImage]
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:completionBlock];
}
else
{
completionBlock(nil, error);
}
if ([[self delegate] respondsToSelector:#selector(captureManagerStillImageCaptured:)])
{
[[self delegate] captureManagerStillImageCaptured:self];
}
}];
}
This code successfully captures an image and saves it to the library. However, at some point while I was working on it, it changed from capturing 5-megapixel 4:3 images to capturing 1920x1080 16:9 images. I can’t find anywhere that the aspect ratio is specified, and I didn’t change any of the code relating to the configuration of the camera, capture sessions, or capture connection. Why did my camera start taking 16:9 photos?
Update: I just re-ran Apple’s original sample code, and it appears that it is also saving 16:9 images captured directly from the video. It is quite possible that I was insane before, or I took a test shot with Camera.app and was looking at that. So my real question is, how do I show a live feed from the camera on the screen while I’m shooting, and take a full-resolution photo. I can’t use UIImagePickerController, because I need to be able to overlay things on top of the live camera feed.
Update 2: I was able to solve this by throwing out the AVCapture code I was using. It turns out that UIImagePickerController does what I needed. I didn’t realize you could overlay custom controls - I thought it took over the whole screen until you were done taking a picture.
If you're capturing frames from a video source, you'll end up with a resolution of 16:9. Capturing frames from a video source and taking photos are different things.

iphone iOS5 audioPlayerDidFinishPlaying not called screen lock?

I have a series of audio files which I want to play in sequence. I setup audioPlayerDidFinishPlaying delegate method to start the next file in the sequence. This works with no problem if I turn off screen lock or if I run it on the emulator or on an iOS4 device. However, when I run it on a disconnected device, iOS5, screen locked, it stops after the first track. Is there some way to start a new sound track after the screen lock has come on?
Here is my basic code
// Registers this class as the delegate of the audio session.
[[AVAudioSession sharedInstance] setDelegate: self];
// Use this code instead to allow the app sound to continue to play when the screen is locked.
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 0;
AudioSessionSetProperty ( kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (doSetProperty),
&doSetProperty
);
// Registers the audio route change listener callback function
AudioSessionAddPropertyListener (
kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self
);
// Activates the audio session.
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:#"m4a"];
float vol = [appSoundPlayer volume];
AVAudioPlayer * newAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
self.appSoundPlayer = newAudio; // automatically retain audio and dealloc old file if new file is loaded
[newAudio release]; // release the audio safely
appSoundPlayer.delegate = self;
[appSoundPlayer prepareToPlay];
[appSoundPlayer setNumberOfLoops:0];
[appSoundPlayer play];
[appSoundPlayer setVolume:vol];
[progTime setProgress:0];
}
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) lappSoundPlayer successfully: (BOOL) flag {
//... code here to select nex sound track
[lappSoundPlayer play];
}
I have already made it so the app continues to play the current track even when screen lock comes on, I just can't get it to start up the next track.
Note: I have verified that this was working in iOS4.
Have you set up an audio session category, specifically AVAudioSessionCategoryPlayback, as described here?
[[AVAudioSession sharedInstance] setDelegate: self];
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
If that doesn't work, another thing to try, according to user ascanio in this message in the Apple dev forums, is to set your app up to listen for remote control events:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];