Creating new AVPlayer while in background does not work? - ios

I'm playing music with an AVPlayer. Now at a certain time a NSTimer fires and I'dlike to fade over to another track. So I start fading out my AVPlayer and create a new AVPlayer instance to play the next song.
When on foreground this works as expected. But when my app is on background. The playing track fades out but the new AVPlayer instance does not start playing. Is it just not possible to create a new AVPlayer instance on background? or how can I make it play? Or is there another way to overlap two tracks?
I could do the playback with AVQeueuPalyer, but then I can't let tracks overlap. Any suggestions?
-- EDIT --
If it was not clear, I am able to play background audio as long as I want. Just creating a new AVPlayer instance in background does not work.

The correct way to do what I wanted seems to be AVMutableComposition. With that I don't need multiple AVPlayers and a few other benefits. For more details: I summarized it in a blogpost: http://www.postblog.me/2012/03/playing-multiple-overlapping-audio-tracks-in-background/

Try adding a key named "UIBackgroundModes" (array) to your app's Info.plist and add the value "audio" inside it. Then call
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:NULL];
And then you should be able to play audio in the background (you should link to the AVFoundation framework).

Related

Best way to play silence using AVAudioPlayer on iOS

I found myself in a situation where I need to simulate audio playback to trick OS controls and MPNowPlayingInfoCenter into thinking that an audio is being played. This is because I am building a player that plays multiple audio tracks, with pauses in-between creating one, continuous "audio" track. I have already everything setup inside the app itself, and the lock screen controls are working correctly but the only problem I am facing is while the actual audio stops and a pause is being "played", the lock screen info center stops the timer, and it only continues with showing correct time and overall state once another audio track starts playing.
Here is the example of my audio track built from audio files and pause items:
let items: [AudioItem] = [
.audio("part-1.mp3"),
.pause(duration: 5), // value of type: TimeInterval
.audio("part-2.mp3"),
.pause(duration: 3),
... // the list goes on
]
then in my custom player, once AVAudioPlayer finishes its job with current item, I get the next one from the array and play either a .pause with a scheduled Timer or another .audio with AVAudioPlayer.
extension Player: AVAudioPlayerDelegate {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
playNextItem()
}
}
And here lies the problem, once the AVAudioPlayer stops, the Now Playing info center automatically stops too, even tho I keep feeding it fresh nowPlayingInfo. Then when it hits another .audio item, it resumes correctly and shows current time, etc.
And here lies the question
how do I trick the MPNowPlayingInfoCenter into thinking that audio is being played while I "play" my .pause item?
I realise that it may still not be clear, what I am trying to achieve but I am happy to share more insight if needed. Thanks!
Some solutions I am currently thinking about:
A. Keeping 1s long empty audio track that would play on loop for as long as the pause is needed to play.
B. Creating programatically empty audio track with appropriate lenght and playing it instead of using Timer for keeping track of pause duration/progress and relying completely on AVAudioPlayer for both .audio and .pause items. Not sure this is possible though.
C. Maybe there is a way to tell the MPNowPlayingInfoCenter that the audio keeps playing without the need of using AVAudioPlayer but some API I am not familiar with?
AVAudioPlayer is probably the wrong tool here. You want AVAudioPlayerNode, which is slightly lower-level. Create an AVAudioEngine, and attach an AVAudioPlayerNode. You can then call scheduleFile(_:at:completionHandler:) to play the audio at the times you want.
Much of the Apple documentation on AVAudioEngine appears broken right this moment, but the links hopefully will be available again shortly in the links for Audio Engine Building Blocks. (If it stays down and you have trouble finding docs, leave a comment and I'll hunt down the WWDC videos and other tutorials on using AVAudioEngine. It's not particularly difficult for simple problems.)
If you know in advance how you want to compose these items (and it looks like you may), see also AVMutableComposition, which lets you glue together assets very efficiently, including adding empty segments of silence. See Media Composition and Editing for the various tools in that space.

MPMediaItem and AVPlayerItem playback sequence in background

I'm having an issue with playing sequences of different kinds of items in a background.
In an app I'm working on we've introduced playlists which contain both content provided by the app and Apple Music content.
For that use AVPlayer and MPMusicPlayerController respectively. We observe one player or the other (depending what content is now playing) and if the other kind of content comes next, we release the old player (if we can - MPMusicPlayerController is a singleton, so best we can do is stop it) and load item to another player.
The problem starts when the app leaves foreground. Once MPMusicPlayerController takes over it doesn't want to give up control, so if any AVPlayer content comes after MPMusicPlayerController content, the music stops.
One workaround that I've tried is playing with .mixWithOthers options when I set the category on AVAudioSession, however this creates new category of problems - I'm loosing lockscreen controls, therefore I'm also loosing airplay. One dirty trick that I've tried was setting .mixWithOthers 3 seconds before MPMediaItem ends, and then disabling it back once AVPlayer starts. Beside the fact that there're probably many different things that can go wrong here, MPMediaPlayerController still doesn't want to give me back the control over lockscreen controls.
Is there any way this could ever work on iOS 13?

How can I avoid the AVPlayerLayer `setPlayer` audio blip?

I have an application that plays video using AVPlayer and AVPlayerLayer. In my app, I'm able to play audio when the app is locked by setting the player property of my AVPlayerLayer to nil when the application enters the background:
-(void)appEnteredBackgroundNotification:(NSNotification *)notification {
[[self playerLayer] setPlayer:nil];
}
However, when I do this, the audio will lag/blip for around 0.5 seconds. This sounds really really bad for the end user. Same goes for when the app enters foreground and I re-set the player property.
How can I avoid this audio blip? As a test I've tried removing the player in a background thread to no avail.
Update: I spoke with an Apple engineer at WWDC and they said that this issue is a bug on their end (so far not fixed in iOS 9) and this approach is the correct approach. Great...
I think may not you call pause before setting to nil and vice versa. And, try calling prepare before play.

volume slider has no effect

I am creating a radio streaming app with play, pause and volume slider.
I have implemented volume slider using MPVolumeView but unfortunately it is not working.
Can any one please let me know the correct code so that the volume slider will work in my app. I have used MPMoviePlayerController, AVPlayer, AVAudioPlayer.
Sounds as if you missed an important part of Apple's documentation;
Working with Movies and iPod Music
I am suspecting that you are using both, AVAudioPlayer and MPMoviePlayerController together and that you have setup some audio session attributes to get that working properly. Now when doing so, you may want to tell MPMoviePlayerController to use that session / or not.
Using the Media Player Framework Exclusively
If your application is using a movie player only, or a music player
only—and you are not playing your own sounds—then you should not
configure an audio session.
If you are using a movie player exclusively, you must tell it to use
its own audio session, as follows:
myMoviePlayer.useApplicationAudioSession = NO
If you are using a movie
player and a music player, then you probably want to configure how the
two interact; for this, you must configure an audio session, even
though you are not playing application audio per se. Use the guidance
in Table 6-1.

Full-featured music player using AVPlayer

I'm writing a music player for iOS that needs to have all the features of the built-in Music app. My app needs to continue running in the background so I have to use the AVPlayer class.
Are there any open source implementations out there that I can use instead of writing the whole thing myself?
Just found this. It works great:
https://github.com/gangverk/GVMusicPlayerController
If you want to play tracks from your iTunes music library, and don't want to use the MPMusicPlayerController class, your best bet is to use AVPlayer or AVQueuePlayer (subclass of AVPlayer). You must establish the appropriate audio session and register to receive remote control events for the app to continue playing music in the background.
There are downsides to this method; you won't be able to play DRM-protected tracks and audiobooks purchased from the iTunes store. There's no way to instantiate an iTunes Match download with the AVPlayer class. Furthermore, you'll have a bit of work on your hands if you want to add gapless playback and equaliser settings (The closest you'll get to gapless playback is with the AVQueuePlayer subclass, though in theory, you could overlap AVPlayers with an NSTimer).
You'll also need to change 'Required Background Modes' in your Info.plist to 'App plays audio'
As for the rest of your app, I suggest you read up on UITabBarControllers and UITableViewControllers along with MPMediaQuerys.
See this solution for the audio part.

Resources