iOS - Reset the command center / remote player - ios

I'm using different kinds of players in my app. I have some kind of control over all of them, but one of them I don't, at all.
When I play an embedded video from a UIWebView, specifically.
My issue is, the app requires audio to be played in background, which works fine. Note, the video's audio does not need to be played in background. But that means I need to use the remote controls (command center & lock screen commands).
My issue is the following :
After I've played a video from an embed, the remote controls still control that video, even though it's been dismissed. And when I press "play", it plays both my radio AND the embedded video.
How can I "clear the memory" of the command center ?
I have enough information to know when to apply a solution, I don't know what the solution is though.
Solution can include a complete disability to use the command center while playing the embed video, or until my radio is played. Solution can include some kind of "hard reset" of the command center.
Actually, any solution is welcome.

This question is a little bit old, but I just fixed this issue and feel like this could help someone out. The thing that was preventing me from reseting the command center was that I had added target actions that were persisting past the instance of my view controller's lifecycle.
Here's what my code looked like.
On the view controller:
var commandPlay: Any?
var commandPause: Any?
When adding the targets:
commandPlay = commandCenter.playCommand.addTarget {event in
self.startButtonTapped(self.startButton)
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = 1.0
return .success
}
commandPause = commandCenter.pauseCommand.addTarget {event in
self.startButtonTapped(self.startButton)
MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = 0.0
return .success
}
At dismiss to remove the targets (important part):
commandCenter.playCommand.removeTarget(commandPlay)
commandCenter.pauseCommand.removeTarget(commandPause)

Related

AudioKit5 / Swift / iOS: Playing a clip does not start if one already playing

It would seem a simple question, but perhaps I am missing something, I am simply trying to have a second track play on a button press, that stops the current and plays!
The first time code is executed, first track plays fine, status is "playing"
Second time code executed, first track stops, status is "playing", but no sound.
Third time executed, desired track plays fine.
clipPlayer.stop()
do {
try clipPlayer.load(file: file)
clipPlayer.play()
print("status:\(clipPlayer.status)")
} catch {
Log(error.localizedDescription, type: .error)
}
I see this same odd behaviour in the Audiokit5 Cookbook / Audioplayer Playlist too - having to click the desired track twice, surely so basic problem would've been picked up? Or am I missing something simple?
EDIT: Oh, additional info - if the current track has already stopped by reaching its end point. Clicking the new track starts as it should just fine.

Testing CarPlay Audio using the simulator

I have developed an iOS radio audio app using Swift / UIKit and everything works well.
I want to integrate wit CarPlay and got the required entitlements.
I believe I have set everything up right for the most part as I can see the CPListTemplate with my CPListItems and on tapping one of them, it goes to the CPNowPlayingTemplate and the audio starts playing in the simulator.
While everything seems to be working well, However, there are 2 issues:
I can't seem to interact with CPNowPlayingTemplate play / pause button, I just keep seeing the play button but clicking it does nothing
I am able to do this on the device's lock screen and through Command Center after adding this code:
func setupNowPlayingInfoCenter(){
UIApplication.shared.beginReceivingRemoteControlEvents()
MPRemoteCommandCenter.shared().playCommand.isEnabled = true
MPRemoteCommandCenter.shared().playCommand.addTarget { [weak self] event in
self?.reliableRadioPlayer?.play()
return .success
}
MPRemoteCommandCenter.shared().pauseCommand.isEnabled = true
MPRemoteCommandCenter.shared().pauseCommand.addTarget { [weak self] event in
self?.reliableRadioPlayer?.pause()
return .success
}
}
The second issue is again on the same screen, I cannot see any of the meta data such as the artwork, song name and artist name - again these show up on the device's lock screen and the Command Center with the help of these lines of code:
MPNowPlayingInfoCenter.default().nowPlayingInfo =
[MPMediaItemPropertyTitle: currentlyPlaying.getSongName(),
MPMediaItemPropertyArtist: currentlyPlaying.getSongArtist(),
MPMediaItemPropertyArtwork: artwork]
Do I need to set anything else up or are these simply limitations of the CarPlay simulator ?
Thanks
While I do not believe this is going to be best answer and someone might come up with something better, here are some things I believe could improve how you test CarPlay and explain some issues you might face:
Add these two lines of code before launching your NowPlayingTemplate
#if targetEnvironment(simulator)
UIApplication.shared.endReceivingRemoteControlEvents()
UIApplication.shared.beginReceivingRemoteControlEvents()
#endif
I immediately saw some improvements in the meta data displayed by the simulator
The player status does not reflect accurately on the simulator
When you launch the app on the CarPlay simulator and the now playing template is showing, most likely your audio is going to be playing but the player's status is going to show and this will not show the isPlaying status accurately in your CPListTemplate if you have one
There is nothing to worry about here as it works fine in the car, however, I suggest just clicking the play button so you can see the active status in the NowPlayingTemplate and CPListTemplate screen with the animated bars
Testing on a real device
While I don't think most of us can buy a car just to test CarPlay, you could look into buying a car stereo which supports CarPlay like a Sony XAV-AX5500, Sony XAV-AX1005DB or something lower end like this
You cannot power up a car stereo normally with your plugs at home, so I suggest youtubing some videos to power this up, however, this was the easiest one I found using a laptop charger - basically I believe you need something 12V or greater
Good luck

Detect any headset Play/Pause forward/backward volume up/down button tap on swift

I am trying to build an Audio Player App! I can not figure out, how I can get my app responsible to Play/Pause forward/backward volume up/down button tap or click from any headphone or headset!
I found this page from apple! but it is not understandable for me! I like to have an example code!
Link: https://developer.apple.com/documentation/mediaplayer/mpremotecommand
Assuming that you already have the class that will handle the media playback the rest is rather easy.
As mentioned in the link you provided, any event related to media is sent for the apps that are listening it doesn't matter where the event comes from (control center, headset...) that part is handled by the Media Player Framework
On the class you need to grab the command center of the MPF (Media player framework) and set the functions that will be triggered by the events, I will only show the one in the link you provided.
class MediaPlayer {
let commandCenter = MPRemoteCommandCenter.shared()
init() {
// this is for the play command, check the docs for more commands
commandCenter.playCommand.addTarget { [unowned self] event in
if self.player.rate == 0.0 { // <- is stopped?
// do your stuff and say to the OS that everything worked
return .success
}
// if the command does not run properly you must inform the OS
return .commandFailed
}
}
}
There's some missing code that depends on your implementation, but what you want is here, now when something triggers the "play" command your app will detect it and if the player is not reproducing any media it will start.
You can see on the docs more info about the MPRemoteCommandCenter and for more commands, that follow the same pattern.

remove info and control in Control Center from UIWebView

if we place the url which contains video in a UIWebView, and play with it.
the control center will have the info and controls of the video.
is there a way to remove this info and control ?
I found this question asking is that UIWebView player a MPMoviePlayerController instance, and how to get a reference to that instance?
although the answer been accepted, but it doesn't reveal the real player in UIWebView, if I knew what it is, maybe I could find more info about how it works, and how to deal with it.
since I'm the one put the UIWebView in the app, I should have the control, right?
I try to get the AVPlayer with url by this question Get current track playing on control center iOS, but it's returning a new AVPlayer, so is AVQueuePlayer and AVPlayerLayer.. no matter what I did to them, there won't be any effect in the control center..
I found this piece in WWDC sessions
it looks convincing, will this be the right direction ?
I was introduced to method swizzling by #DaidoujiChen
it helps me filter the event of registering remove control from UIWebView
find out which method you don't wanna be touched by others
perform the method by the flag you defined, that's all !
- (void)avoidWebViewUse_beginReceivingRemoteControlEvents {
if ([ JukeboxMacro sharedSingleton ].requestingRemoteControl) {
[ self avoidWebViewUse_beginReceivingRemoteControlEvents ];
} else {
NSLog(#"UIWebView trying to beginReceivingRemoteControlEvents ");
}}

Editing video-timing in AVComposition

I have an AVComposition containing multiple video and audio tracks. So far, it's pure programmatically, no GUI. I want the user to be able to push one track a couple of frames back or forth on will, by clicking a button. e.g: a button titled "-10 frames" will push a track 10 frames back, while "+10 frames" pushes it 10 frames forward. I can't find any way to actually move a track after it has been added using insertTimeRange:ofTrack:atTime:.
I tried to remove it and re-adding it like this:
[secondAudioTrack removeTimeRange:CMTimeRangeMake(kCMTimeZero, kCMTimeIndefinite)];
[secondAudioTrack insertTimeRange:range ofTrack:[[secondAsset tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:newTime error:nil];
And it's not actually moving so far (I might've done something wrong though), but I feel like this is such a hacky way of doing it.. Anyone know of a proper way to give a new time to an already added track in an AVComposition?
I don't think there is. The complete video should be planned out before involving video composition. Let the user provide only information, save it, then run the video composition only when you have enough information from the user. Start clean each time. You should never have to change the time range inside an AVMutableCompositionTrack.

Resources