AVQueuePlayer playing several audio tracks in background iOS5 - objective-c

I used AVQueuePlayer to play several items in background. And code worked perfect in iOS4. And in iOS5 AVQueuePlayer changed its behavior, so player stops playing after first item is ended.
Matt Gallagher wrote a hint in this post. "As of iOS 5, it appears that AVQueuePlayer no longer pre-buffers. It did pre-buffer the next track in iOS 4."
So my question is how to play several items in background using AVPlayer or AVQueuePlayer in iOS5.

Matt Gallagher's answer in his blog:
"You must observe the current item in the AVQueuePlayer. When it changes, you must use UIApplication to start a backgroundTask and only end the background task when you receive a ready to play notification for the next file."
Actually, this did not helped me.
So my solution is:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
I can't explain why, but this line of code put before adding new AVPlayerItem made my code work.
Also for those one who adds next track in background and uses method
- (void)loadValuesAsynchronouslyForKeys:(NSArray *)keys completionHandler:(void (^)(void))handler;
You must add AVPlayerItem to player on the main thread.
like this:
- (void)addAsset:(AVAsset*)as
{
[player insertItem:[AVPlayerItem playerItemWithAsset:as] afterItem:[player currentItem]];
}
.........
//adding new track
AVURLAsset* as = [[self createAsset:urlString] retain];
NSArray *keys = [NSArray arrayWithObject:#"tracks"];
[as loadValuesAsynchronouslyForKeys:keys completionHandler:^(void) {
NSError *error =
AVKeyValueStatus trackStatus = [as statusOfValueForKey:#"tracks" error:&error];
switch (trackStatus) {
case AVKeyValueStatusLoaded:
[self performSelectorOnMainThread:#selector(addAsset:) withObject:as waitUntilDone:YES];
[as release];
break;
case AVKeyValueStatusFailed:
[as release];
break;
case AVKeyValueStatusCancelled:
[as release];
break;
default:
break;
}
}];
UPDATE:
Matt Gallagher was right, but it works only if you do not use asynchronous loading.

Related

Displaying a lot of objects by means of Magical Record

The objective is to load ~20k entities on application load (in background thread). Then after load I want to show those in a UITableView. To this purpose with such big amount of objects I shall use fetchedResultController with batching set to like 100?
This is the way I'm trying to achieve it:
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
for(int i = 0; i <20000; i++){
Object *object = [Object MR_createInContext:localContext];
object.number = i;
}
} completion:^(BOOL success, NSError *error) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
[self finishedSaving];
}];
Then in finishedSaving
- (void)finishedSaving
{
NSManagedObjectContext *context = [NSManagedObjectContext MR_contextForCurrentThread];
self.fetchResultController = [Object MR_fetchAllGroupedBy:nil withPredicate:nil sortedBy:#"name" ascending:YES inContext:context];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
}
}
I'm having UI freeze on that method. Any ideas to improve or fix?
By default Magical Record has a batch size of 20 (see kMagicalRecordDefaultBatchSize within the source code).
So, with the following size, even if there a lot of objects to grab, the UI should not freeze. NSFetchedResultsController does the right job using a lazing loading mechanism for you. It grabs the first 20. If you scroll, it grabs other 20, and so on.
My guess (I don't know what Magical Record does) is that your saving code (the first snippet) is not running in a background thread or it is not running correctly. Could you provide the entire source code for it?
About the first snippet, do you need to run your task in the background when the app has enter the foreground? Just to be sure, using begin... and end... it does not mean to run a task in the background but just tells the system that you're in the middle of doing something (important for you) that would like to complete in the background if that's is ok.
Update 1
I investigated a bit on MR source code and here the results.
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// This block runs in background thread
} completion:^(BOOL success, NSError *error) {
// This block runs in main thread
}];
So, does your Object only contain a number attribute?

UIImagePickerController delegate is not calling

Iam using the following code for taking the picture automatically from IPAD front camera:
UIImagePickerController *imgPkr = [[UIImagePickerController alloc] init];
imgPkr.sourceType = UIImagePickerControllerSourceTypeCamera;
imgPkr.delegate = self;
imgPkr.cameraDevice=UIImagePickerControllerCameraDeviceFront;
[self presentModalViewController:imgPkr animated:YES];
imgPkr.showsCameraControls = NO;
[imgPkr takePicture];
But this code Dont take any picture and dont call the delegate:
-(void)imagePickerController:(UIImagePickerController*)picker
didFinishPickingMediaWithInfo:(NSDictionary*)info
Any idea what is wrong with the code
My first guess is that [imgPkr takePicture]; is being called before the picker is done being presented. Try it like this:
UIImagePickerController *imgPkr = [[UIImagePickerController alloc] init];
imgPkr.sourceType = UIImagePickerControllerSourceTypeCamera;
imgPkr.delegate = self;
imgPkr.cameraDevice=UIImagePickerControllerCameraDeviceFront;
[self presentModalViewController:imgPkr animated:YES];
imgPkr.showsCameraControls = NO;
[self performSelector:#selector(takePicture) withObject:self afterDelay:1.0];
OR
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(takePicture)
name:AVCaptureSessionDidStartRunningNotification
object:nil];
&
- (void)takePicture
{
[imgPkr takePicture];
}
I bet that you get a message in your logs saying that
UIImagePickerController: ignoring request to take picture; camera is
not yet ready.
This is a common problem because the underlying capture session needs some time to start, so you just have to be sure that the camera is ready to take a picture and then call takePicture method. Now, a way to get notified is explained in detail in my answer here: How to know if iPhone camera is ready to take picture?
Note though that this method will work on iOS5+ (there is a bug in older versions that prevents the system notifications for this event, contrary to what is described in the documentation). I hope that this helps.
another way to wait for camera ready until take picture is completion block ->
[self presentViewController:imagePicker animated:YES
completion:^ {
[imagePicker takePicture];
}];
thank you 'GrandSteph' at
iOS taking photo programmatically

Custom iOS UILocalNotification sound does not play (possibly related to Xcode update)

I'm trying to get a custom sound working on a UILocalNotification, and I'm just getting no sound at all. If I use UILocalNotificationDefaultSoundName, I indeed get the default sound, but when the custom sound is specified, there is no sound, just the message. The sound is less than 30 seconds and it's in the right format, as far as I can tell. Here's a screenshot of the file info:
I've inspected the .app directory in XCode's DerivedData directory, and the alarm.caf file is at the root of the app, which I believe means it's in the bundle (right?).
I'm pretty sure this was working a while ago, and I've since upgraded Xcode. Maybe that is a hint?
I've also tried deleting/reinstalling/rebooting as mentioned in other answers. As you can see, I'm calling cancelAllLocalNotifications first.
Does anyone have any idea what could be wrong?
[[UIApplication sharedApplication] cancelAllLocalNotifications];
NSLog(#"installing alarm");
[arguments pop]; // name
[arguments pop]; // title
alarm.alertBody = [arguments pop];
alarm.fireDate = [[NSDate date] addTimeInterval:[[arguments pop] intValue]/1000];
//alarm.soundName = UILocalNotificationDefaultSoundName;
alarm.soundName = #"alarm.caf";
[[UIApplication sharedApplication] scheduleLocalNotification:alarm];
Your code seems to be good.
Try to clean your project, uninstall your app from your device/simulator, then re-install it. It could help maybe :)
I don't know the reason (and I didn't read documentation too), I just turned on the action property notification setHasAction:YES and the sound began to play.
please make sure that the iPhone is not in silent mode( because your code seems to be good )
just check the button on the side of your iPhone
Ok, so here's what happened. I forgot how the app handles the notification itself if it is still running. My code was only displaying a UIAlertView and not playing the sound. I'm not sure why it worked with the default sound. In any case, I added code like this to my AppDelegate:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"didReceiveLocalNotification");
if (application.applicationState == UIApplicationStateActive) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"MarkMyTime"
message:notification.alertBody
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
NSString *soundFilePath = [[NSBundle mainBundle]
pathForResource:notification.soundName ofType:nil];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL: fileURL error: nil];
[fileURL release];
player.delegate = self;
[player prepareToPlay];
[player play];
[alertView show];
if (alertView) {
[alertView release];
}
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"Releasing player");
[player release];
}
This will show a UIAlertView and play the sound on the notification object. You also need to add the AVAudioPlayerDelegate interface to the AppDelegate to be able to assign the delegat to the player. I think if you are using ARC, this code could be simplified a bit.
#interface AppDelegate : PhoneGapDelegate <AVAudioPlayerDelegate> {
I'm not sure if this is the best approach, so feel free to chime in with any improvements.
Maybe you do not add the sound file (*.caf) in Xcode project: Build Phases/Copy Bundle Resources.
Your code is good, but check your iPhone setting
setting -> Notification center -> Your App -> Sound - > "On"
the sound should be "On".
So, to enable this, checked Inter App Audio at Capabilities in Targets of the application and it was Off Capabilities in Inter-app audio
change this to On.
Then local notification sound is working.

UIImagePickerController won't record a video

I want to record a video with UIImagePickerController. My Problem is that the method [imagePickerController startVideoCapture]; always returns 0. I am testing the application with an iPhone 4S running iOS 5.1. Could somebody please help me with this:
- (IBAction)playButtonPushed:(id)sender
{
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
UIView *videoView = self.videoViewController.view;
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] >init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePickerController.mediaTypes = [[NSArray alloc] initWithObjects: (NSString >*)kUTTypeMovie, nil];
imagePickerController.showsCameraControls = NO;
imagePickerController.toolbarHidden = NO;
imagePickerController.wantsFullScreenLayout = YES;
imagePickerController.allowsEditing = YES;
imagePickerController.videoQuality = UIImagePickerControllerQualityTypeMedium;
imagePickerController.videoMaximumDuration = 30;
[imagePickerController startVideoCapture];
imagePickerController.cameraViewTransform = > CGAffineTransformScale(self.imagePickerViewController.cameraViewTransform, CAMERA_TRANSFORM, CAMERA_TRANSFORM);
imagePickerController.delegate = self;
[self.imagePickerViewController setCameraOverlayView:videoView];
NSLog(#"%s videoCapture: %d", __PRETTY_FUNCTION__, [imagePickerController > startVideoCapture]
);
[self presentModalViewController:imagePickerViewController animated:YES];
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSLog(#"didFinishPickingMediaWithInfo");
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
NSLog(#"Cancel");
}
I had the same problem and Apple's documentation didn't help. That's because they missed to mention that startVideoCapture() can return false if you haven't set the capture mode to .video like this:
picker.cameraCaptureMode = .video
This worked for me and I'm able to capture videos now.
If im not mistaken, the "startVideoCapture" method is a bool
Taken straight from apple documentation:
startVideoCapture
Starts video capture using the camera specified by the UIImagePickerControllerCameraDevice property.
- (BOOL)startVideoCapture
Return Value
YES on success or NO on failure. This method may return a value of NO for various reasons, among them the following:
Movie capture is already in progress
The device does not support movie capture
The device is out of disk space
Discussion
Use this method in conjunction with a custom overlay view to initiate the programmatic capture of a movie. You can take more than one movie without leaving the interface, but to do so requires you to hide the default image picker controls.
Calling this method while a movie is being captured has no effect. You must call the stopVideoCapture method, and then wait until the associated delegate object receives an imagePickerController:didFinishPickingMediaWithInfo: message, before you can capture another movie.
Calling this method when the source type of the image picker is set to a value other than UIImagePickerControllerSourceTypeCamera results in the throwing of an NSInvalidArgumentException exception.
If you require additional options or more control over movie capture, use the movie capture methods in the AV Foundation framework. Refer to AV Foundation Framework Reference.
Availability
Available in iOS 4.0 and later.
Declared In
UIImagePickerController.h
stopVideoCapture
Stops video capture.
- (void)stopVideoCapture
Discussion
After you call this method to stop video capture, the system calls the image picker delegate’s imagePickerController:didFinishPickingMediaWithInfo: method.
Availability
Available in iOS 4.0 and later.
Declared In
UIImagePickerController.h
Think you need to call the startVideoCapture method after calling presentModalViewController:animated: and not before.
You probably also need a short delay (<1s?) after the UIImagePickerController has been presented on-screen and the animation has stopped, then call startVideoCapture, otherwise there are times where it will not start recording.
I had exactly the same problem. AndyV has it right, the recording won't start until the picker has been presented. I spent a lot of time trying to introduce a delay with sleep, but the simplest solution is to start a timer after displaying the picker using
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(startRecording) userInfo:nil repeats: NO];
Then ..
- (void)startRecording
{
BOOL result = [picker startVideoCapture];
}

how to play sound when alertview appears iphone sdk?

i want to play a sound file when alertview appears and plays continuously till user clicks on ok or cancel.how do i do this?
As Zoul says, you set-up and play your sound as you call [myAlert show] and cancel the sound in the alert view callback. Your code will look something like this:
AVAudioPlayer *myPlayer;
// ...
// create an alert...
NSError *error;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:mySoundFileURL error:&error];
// handle errors here.
[myPlayer setNumberOfLoops:-1]; // repeat forever
[myPlayer play];
[myAlert show];
// ...
// in alert callback.
[myPlayer stop];
[myPlayer release];
As you already call the show method to display the dialog, why don’t you simply start playing the sound there and stop in the alert view callback? For the sound itself you can use AVAudioPlayer.