As titled, I'm currently working on an app that needs to play two videos on a server. The thing is I need to play these two videos in sync which means two of the AVPlayers start the video exactly at the same time, and whenever one of the AVPlayer is paused due to buffering, the other AVPlayer needs to be paused, along with the resumed after buffer and vice versa.
I tried to look around but can't really seem to find a solution to this case.
I need to play it via stream which means my business requirement won't allow me to download both of the videos first.
Related
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.
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?
In my app I need to play multiple videos one after another. Currently, I am streaming the videos using AVPlayer but its seems very laggy, the videos freeze quite often. I'm wondering if downloading the files with NSFileHandle will provide a better user experience with less lagging. BUT, Im worried about memory issues.
Does anyone have any recommendations in which way is more efficient? Or, for example, how snapchat plays such a large number of videos so smoothly. Thanks.
To control the playback of assets, you use an AVPlayer object. During playback, you can use an AVPlayerItem instance to manage the presentation state of an asset as a whole, and an AVPlayerItemTrack object to manage the presentation state of an individual track. To display video, you use an AVPlayerLayer object.
enter link description here
I have an app that uses AVPlayer (or AVQueuePLayer) to play local files that were recorded by the App. All works great. But I also want this to work on iPhone when a call is in progress (the videos are event recordings). What I found is that during a phone call, the video feed to avplayerLayer goes blank, AVPlayer rate change to 0 (STOP), and all attempts to change rate to non-zero (PLAY) are ignored (rate stays at 0). There does not appear to be any documentation on this, and the only way to detect this condition in the player, is that player is STOPPED and will not start PLAYBACK. Of course, I also check for audio interruptions, and call center calls in progress.
Obviously, in this case the interruption is caused by a call, so there is always a inactive/resume or a intactive/background/foreground/resume transition. As well as audio route notification, audio interruption. So indirectly I know the condition is probably occurring.
So questions are:
(1) Is there any direct method (specific to AVPlayer,AVPlayerLayer) to be notified that AVPlayer is in this non-playing mode. I now use "avplayer.rate failed to change rate from 0 to non-zero", but this seems hacky (and too much "crossing the streams"!) I want to Notify user that video temporarily can not be played or previewed, so they do not think the App is broken. And also inform them or automatically continue Playback when iPhone call ends. (Without a looping process that keeps trying to start playback every 500ms!)
(2) Can AVPlayer play anything while a iPhone call (Green Bar) is in progress? or is this just the way apple designed the AVPlayer SDK? (If so there is no documentation on this) Obviously, other apps can play video during an iPhone call, but I suspect they are using a lower level SDK and not 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.