Hope that the issue goes to the correct stream.
I play HLS, i.e. I use AVPlayer for playing a mixed content( audio & pure video) from a server.
I keep playing audio after moving the app into background: I have enabled such feature in plist, plus I do store Prev Player, disable visual tracks, and set to nil AVPlayerLayer. Here are the methods:
- (void)changePlayerState:(BOOL)restored
{
if (restored && !self.storedPlayer)
return;
if (!restored){
self.storedPlayer = self.playerView.player;
[self.playerView setPlayer:nil];
} else if (self.storedPlayer) {
[self.playerView setPlayer:self.storedPlayer];
self.storedPlayer = nil;
}
AVPlayerItem *playerItem = self.playerView.playerItem;
NSArray *tracks = [playerItem tracks];
for (AVPlayerItemTrack *playerItemTrack in tracks)
{
//
if ([playerItemTrack.assetTrack hasMediaCharacteristic:AVMediaCharacteristicVisual])
playerItemTrack.enabled = restored; //
}
}
And following one:
- (void)setPlayer:(AVPlayer *)player
{
if (_player != player) {
[self removePlayerObservers];
_player = player;
[self configurePlayerLayer];
if (_player)
[self addPlayerObservers];
}
}
- (void)removePlayerObservers
{
if ([_player observationInfo] != nil) {
[_player removeObserver:self
forKeyPath:@"rate"
context:NPUIPlayerViewPlayerRateObservationContext];
[_player removeObserver:self
forKeyPath:@"isExternalPlaybackActive"
context:NPUIPlayerViewAirPlayVideoActiveObservationContext];
}
[self removePeriodicTimeObserver];
}
- (void)addPlayerObservers {
[_player addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:NPUIPlayerViewPlayerRateObservationContext];
[_player addObserver:self
forKeyPath:@"isExternalPlaybackActive"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:NPUIPlayerViewAirPlayVideoActiveObservationContext];
[self addPeriodicTimeObserver];
}
- (void)removePeriodicTimeObserver
{
if (_registeredTimeObserver) {
[_player removeTimeObserver:_registeredTimeObserver];
_registeredTimeObserver = nil;
}
}
- (void)addPeriodicTimeObserver
{
__weak NPUIPlayerView *weakSelf = self;
_registeredTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.5f, 10) queue:dispatch_get_main_queue()
So after moving the app to the background , pressing HOme button, and activating Control Center(sliding up from the button, where is a calculator, camera, light buttons) , I can set the title of item being played, but apple's player controls doesn't work : pressing pause, play doesn't work also slider doesn't work. I do receive events when I press the buttons, but at the same time, inspite of the fact, that I am calling [player pause], or change rate it doesn't change the button. But when there is an issue with accessing audio via internet, or sound buffer is empty, the button is changed, it has paused UI. Below goes the code for setting now playing media center and adding commands
- (void)adjustNowPlayingScreen
{
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary *mDic =[NSMutableDictionary dictionary];
NSString *newTitle = self.playerTitle.text ?: self.itemToPlay.shortName ?: self.itemToPlay.name ?: @"";
if (newTitle.length)
mDic[MPMediaItemPropertyTitle] = newTitle;
AVPlayerItem * item = [self.playerView.player currentItem];
CMTime itemDuration = [self.playerView.player.currentItem duration];
if (CMTIME_IS_VALID(itemDuration) ) {
NSTimeInterval duration = CMTimeGetSeconds(itemDuration);
if (duration)
mDic[MPMediaItemPropertyPlaybackDuration] = @(duration);
}
else
{
NSTimeInterval duration = CMTimeGetSeconds([item.asset duration]);
if (!isnan(duration) && duration > 0)
mDic[MPMediaItemPropertyPlaybackDuration] = @(duration);
else {
duration = CMTimeGetSeconds([[[[self playerView] playerItem] asset] duration]);
if (!isnan(duration) && duration > 0)
mDic[MPMediaItemPropertyPlaybackDuration] = @(duration);
}
}
NSString *urlStr = self.itemToPlay.autoQualityURL ?: self.itemToPlay.lowAutoQualityURL;
if (urlStr.length)
mDic[MPMediaItemPropertyAssetURL] = urlStr;
CMTime curTime = self.playerView.playerItem.currentTime;
if (CMTIME_IS_VALID(curTime)) {
NSTimeInterval duration = CMTimeGetSeconds(curTime);
mDic[MPNowPlayingInfoPropertyElapsedPlaybackTime]= @(duration);
}
if (mDic.count) {
#warning @"HACK: Necessary to change number of played items & index"
mDic[ MPMediaItemPropertyAlbumTrackNumber] = @(1);
mDic[ MPMediaItemPropertyAlbumTrackCount] = @(1);
mDic[ MPNowPlayingInfoPropertyPlaybackRate] = @(self.playerView.isPlaying ? 1.0 : 0.0);
UIImage *img = [UIImage imageNamed:@"team_4f6ae0b99d4b856118000124"];
mDic[MPMediaItemPropertyArtwork] = [[MPMediaItemArtwork alloc]initWithImage:img];
infoCenter.nowPlayingInfo = mDic;
}
}
- (void)addRemoteCommands
{
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *command = [commandCenter pauseCommand];
command.enabled = true;
[command addTarget:self action:@selector(pauseCommand:)];
command = [commandCenter playCommand];
command.enabled = true;
[command addTarget:self action:@selector(playCommand:)];
command = [commandCenter togglePlayPauseCommand];
command.enabled = true;
[command addTarget:self action:@selector(toggleCommand:)];
command = [commandCenter seekForwardCommand];
command.enabled = true;
[command addTarget:self action:@selector(seekForwardCommand:)];
command = [commandCenter seekBackwardCommand];
command.enabled = true;
[command addTarget:self action:@selector(seekBackwardCommand:)];
}
- (void)updateElapsedTime
{
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *command = [commandCenter togglePlayPauseCommand];
if (!command.enabled)
return;
[self adjustNowPlayingScreen];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 2);
dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void){
[self updateElapsedTime];
});
}
- (void)removeRemoteCommands
{
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
MPRemoteCommand *command = [commandCenter pauseCommand];
command.enabled = false;
[command removeTarget:self action:@selector(pauseCommand:)];
command = [commandCenter playCommand];
command.enabled = false;
[command removeTarget:self action:@selector(playCommand:)];
command = [commandCenter togglePlayPauseCommand];
command.enabled = false;
[command removeTarget:self action:@selector(toggleCommand:)];
command = [commandCenter seekForwardCommand];
command.enabled = false;
[command removeTarget:self action:@selector(seekForwardCommand:)];
command = [commandCenter seekBackwardCommand];
command.enabled = false;
[command removeTarget:self action:@selector(seekBackwardCommand:)];
}
- (void)seekBackwardCommand:(MPRemoteCommandEvent *)event
{
NSLog(@"%@",NSStringFromClass([event class]));
}
- (void)seekForwardCommand:(MPRemoteCommandEvent *)event
{
NSLog(@"%@",NSStringFromClass([event class]));
}
- (void)pauseCommand:(MPRemoteCommandEvent *)event
{
[self pause]; //_player pause];
AVPlayerItem *playerItem = self.playerView.playerItem;
NSArray *tracks = [playerItem tracks];
for (AVPlayerItemTrack *playerItemTrack in tracks)
{
/
if ([playerItemTrack.assetTrack hasMediaCharacteristic:AVMediaCharacteristicAudible])
playerItemTrack.enabled = false; /
}
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary *mDic =[NSMutableDictionary dictionaryWithDictionary:infoCenter.nowPlayingInfo];
mDic[ MPNowPlayingInfoPropertyPlaybackRate] = @(0.0);
infoCenter.nowPlayingInfo = mDic;
}
So , my pause command is called, I call AVPlayer's pause method. But it doesn't stop audio stream.
And Slider doesn't work, therefore my seekBackwardCommand/seekForwardCommand are not called.
But as I said when the stream is over (audio also) the player on Control Center changes button from playing into pause, i.e. somehow it listen to Audio Session changes.
I adjust the sound category by setting AVAudioSessionCategoryPlayback
, and setting mode : AVAudioSessionModeMoviePlayback
Please help how properly to handle pause/ play buttons on Control Screen, how to enable slider?
I am running my code on iOS9, iPhone 6.