I'm currently developing a music player app and ran into a coulpe of interesting behaviours regarding the the playback states of MPMusicPlayerController:
when I update the player queue and and set the nowPlayingItem because the music was playing, so I want it to continue. These changes obviously changes playbackStates. But if these updates happen rapidly, after some time (sometimes almost at first), the playbackState goes wrong and it says paused but the music is still playing!
and an other problem regarding these updates, when I update the queue, set the nowPlaying item and call the play method, I think it should cause 1 or 2 playback state change, but what happens is that the playbackStateDidChange notification gets called 4-5 times quickly. I really don't get why is there so many playback state changes, and I think maybe these causes the playbackState to go wrong.
I would very much appreciate any help!
Thanks
Related
I prepared a CountDown timer for Pomodoro technique. I would like to know how don't pause the app when it reach a background. I have a method which update UILabel from 20min to 0 by 1sec. When Timer reach 0 it should play the sound and vibrate device. All works fine when app is launched in foreground, but how to do it at background? Is it possible to track timer change when app is in background mode?
BR
iMat
The short answer is no. A timer on a VC will not continue to run when the app is in the background because it goes into suspended mode.
You could schedule a local notification to fire when the app is in the background, but as far as updating the UI label, you'll have to update that when the user comes back into the app.
Invalidate the timer when the app goes to background. Store the remaining time remainingTime and current time backgroundTime. (You can get the current time using Date())
Compare the current time backToForegroundTime when the app comes back with backgroundTime. Subtract them to get the time elapsed timeElapsed.
If timeElapsed is less than the remainingTime, subtract that amount from remainingTime and create the timer again with the new duration.
You can use my approach from this gist. Just create repeating timer and update what ever you want in repeating block, and handle timer finishing in other block on main queue or background queue!
Glad to help with questions!
Apple has defined a specific set of tasks, an app can perform when in background.
Running a timer, unfortunately, is not one of them.
Read Background Execution section of app programming guide for more details.
Most apps, intending to continue to execute code in background, implement one of the allowed long running background modes, even if it is not required for your apps actual functionality, and use them to execute their code.
But be ware, you will be doing something apple specifically asks you not to do. This could result in app store rejection if found.
We've been using the AIR sound API, building for iOS, with no problems till now, the problem is that the sound works until iPad goes to sleep and there's no sound from our app when iPad is woken back up.
Note: we have the app set to exit on suspend, and we don't have this problem when user suspends -- i.e, hits the button and goes back to desktop.
We only have it when iPad goes to sleep (screen goes dark but app is still running). As far as I can tell our app is not receiving any events from iOS when the iPad goes to sleep or wakes back up. Once this has all happened and the game has no sound, but exiting and restarting makes it work again.
We are getting an activate event from the native application object when iPad is woken up, I'm also checking (just because it sounds useful) the value of SoundMixer.areSoundsInaccessible, but that's way off base. I do get a soundchannel object back when I play a sound while the problem is in effect, I just can't hear it.
We were having a similar problem with audio not playing (roughly half the time the device had been asleep), and fixed it with this:
// AudioPlaybackMode.MEDIA is the default value
SoundMixer.audioPlaybackMode = AudioPlaybackMode.AMBIENT;
Once that was done, audio came back immediately after the app woke up.
Note that AMBIENT's behaviour depends on whether the microphone is being used.
My game has a pause screen which doubles as the main title screen with high scores. It is semi-transparent so the user can see what state the game-round was in. They can see the last message such as "Win" or "Loss". In the case of an interruption or pause they see "Redo". My game already handles interruptions gracefully, saving state and restoring last round so that the user can try again.
Question; when the user closes the app (hits home button) or if the app is interrupted with an incoming phone call during game-play, when the app comes back into the foreground should I show the pause screen or should I take them back to the game? Another way to phrase this question would be, should the home button and the pause button behave virtually the same for game-play interruption? (I presume that if the user were in a sub-screen, non-game-play, they would be taken back to that sub-screen).
Question 2; What does it mean to pause? I believe all sounds should cease but can I get away with keeping some minimal animation going? I do know that if the app were to go into the background that the OpenGL ES 2 animation would have to cease. Do you think users would prefer to have no animations during pause? I question the extent to which battery life should influence the answer.
I think this is a very subjective question. I'll try to give you some input.
I would say the good rule of thumb is: Will it affect your game play if you don't pause the game? For example, if your game is a chess or sudoku game, it wouldn't really matter if you pause the game when the user exit to home screen. However if your game is an action game that each mili second is very important and will decide whether the user will win, then in that case you would need to pause the game.
What does it mean to pause: It depends on your implementation. It can be anything you want. You can even continue the animation in the background if you want. This is all up to you.
While playing a video, I'm seeing rate change notifications from AVPlayer that don't seem to be connected to app activity.
When my app receives a UIApplicationDidEnterBackgroundNotification notification, I tell the AVPlayer to pause. The logic is that it should come back to the foreground at the same place the user left. If I do not call pause when going to the background, the problem doesn't appear.
The sequence of events sent to the player is pause, seekToTime:, play. Generally, this works fine but, after the app has been sent to the background and then returned to the foreground, each play invocation results in two rate changes from the AVPlayer. The first is to 1 and the second, immediately following, is to 0. This pattern continues for each call to -[AVPlayer play] as long as that player instance is in use.
I'm able to put a breakpoint on -[AVPlayer pause] and I do not see it being hit when the rate changes to 0. If I comment out the seekToTime: call, the problem goes away. If I use seekToTime:completionHandler:, I also get the same problem although my block's finished parameter is YES.
Apart from "how do I fix this", I'm interested in any details about how to detect the reason for rate changes in AVPlayer that aren't connected to play/pause. (Putting a breakpoint on -[AVPlayer setRate:] never seems to trigger.)
(One workaround that almost works is to save the player position when entering the background, let it play, and fix the position when returning to the foreground. This also requires some manipulation of audio levels, which is probably doable, but another problem is that not all background notifications indicate that the view has been obscured (e.g. double-tap home button). This leads to cases where my workaround shows a distracting moving image when the app is interrupted but still visible.)
Suggestions?
(A last bit of extra information: In all the cases I've tried, I eventually get to a state where the AVPlayer is changing the rate from 1 to 0 moments after I invoke 'play' if I've returned from the background and then performed a seek. There are things I can do to make it less frequent but none that eliminate it except getting rid of the AVPlayer and creating a new instance. This results is very long delays but is better than a complete malfunction...I guess.
I have some evidence that the seek distance affects the result, which suggests that the error might be in the underlying buffering mechanism. Without knowing what causes the rate change (other than play/pause) I don't see a way to investigate further.)
You are probably getting an AVPlayerItemPlaybackStalledNotification.
Try this:
[[NSNotificationCenter defaultCenter]
addObserverForName:AVPlayerItemPlaybackStalledNotification
object:cell.avPlayerItem
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
DDLogVerbose(#"%#", #"AVPlayerItemPlaybackStalledNotification");
}];
However, Apple docs say Playback will continue once a sufficient amount of media has subsequently been delivered. (https://developer.apple.com/library/iOS/documentation/AVFoundation/Reference/AVPlayerItem_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40009532-CH1-SW83)
This is not happening for me or you.
I am still tracking the reason down.
EDIT:
This was the problem for me. When AVPlayerItem.likelyToKeepUp was YES then it wouldn't happen.
Not sure why it wasn't resuming.
EDIT:
From the Apple docs there is a situation where playback will not resume:
This property communicates a prediction of playability. Factors
considered in this prediction include I/O throughput and media decode
performance. It is possible for playbackLikelyToKeepUp to indicate NO
while the property playbackBufferFull indicates YES. In this event the
playback buffer has reached capacity but there isn't the statistical
data to support a prediction that playback is likely to keep up in the
future.
It is up to you to decide whether to continue media playback.
I am creating player application, which is playing audio streams through internet. I want to add alarm functionality in my app - in particular time my player begins to play audio stream, I am trying to use UILocalNotification mechanism. But I've difficulties with it when my application in background mode, I can't call 'play' method, when notification is receiced (can't without user interaction). May be it is impossible?
But I bought this application:
http://itunes.apple.com/us/app/radio-alarm-clock-mp3-radio/id380271167?mt=8
And it seems like radio can start playing when local notification is received. Alarm can start playing radio when my app is in background mode.
Earlier I was trying to use NSTimer for this, but when my app goes to background, timer stops. If I use beginBackgroundTaskWithExpirationHandler: it works only 10 minutes. My app has special flag in plist, what is is audio application, and can playing music in background. In this case timers are working. But if I stop playing and go to background, timer is not working.
When I use \Radio Alarm Clock' application, I hear 'white noise' from dinamic, when music in not playing. May be it is the secret of this application?
Can you help me with my problem? Thanks.
maybe it's too late.
I had a look to the app you've mentioned at http://itunes.apple.com/us/app/radio-alarm-clock-mp3-radio/id380271167?mt=8 and yes, I think you are absolutely right, the only way to achieve that the application remains active while in background is to play a fake sound while it is in the background, which should be prohibited by Apple.
I've also seen that they don't use the remote iPod control, and this was strange at a first look.
At the end my opinion is that they do the following:
Avoid the call to beginReceivingRemoteControlEvents which allows to activate the iPod controls while in background (in fact they don't have it)
In this way, the status bar doesn't show the play icon while
the app plays audio
When the app goes in background, it probably plays a no sound periodically (once every 10 secs for example), in this way the app remains active
I saw that they also avoided to manage interruptions, for example in case another app is in foreground and plays music. Again Apple should have rejected the app for that reason, cos it is against the rules to follow while in background, but maybe they didn't see it during the acceptance tests.
So my interpretation is that they have intentionally missed to activate the iPod controls, just to avoid to show the play icon in the status bar while in background. In this way, the users are unaware that the app is active and is doing something strange after they close it.
In addition you can see that the app doesn't interrupt when another app plays in foreground a sound or audio, because otherwise they risk that the app doesn't restart on time when the alarm shpould fire.
That's just my idea of how they do that, and I think this is the only way for an audio app on iOS to remain active while it is in background and is supposed to be halted (well, in case Apple doesn't see the trick).
Have you tried adding this to appdelegate.m
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// Call your method in here.
}
if you have can you add code for us to see what your doing.