You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
628 lines
24 KiB
628 lines
24 KiB
// |
|
// WXYZ_Player.m |
|
// WXYZ_Player |
|
// |
|
// Created by ihoudf on 2017/7/18. |
|
// Copyright © 2017年 ihoudf. All rights reserved. |
|
// |
|
|
|
#import "WXYZ_Player.h" |
|
#import <MediaPlayer/MediaPlayer.h> |
|
#import "WXYZ_TouchAssistantView.h" |
|
|
|
#define WXYZ_PlayerHighGlobalQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) |
|
#define WXYZ_PlayerDefaultGlobalQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) |
|
|
|
/**Asset KEY*/ |
|
NSString * const WXYZPlayableKey = @"playable"; |
|
/**PlayerItem KEY*/ |
|
NSString * const WXYZStatusKey = @"status"; |
|
NSString * const WXYZLoadedTimeRangesKey = @"loadedTimeRanges"; |
|
NSString * const WXYZPlaybackBufferEmptyKey = @"playbackBufferEmpty"; |
|
NSString * const WXYZPlaybackLikelyToKeepUpKey = @"playbackLikelyToKeepUp"; |
|
|
|
@interface WXYZ_Player() |
|
{ |
|
BOOL _isOtherPlaying; // 其他应用是否正在播放 |
|
BOOL _isBackground; // 是否进入后台 |
|
BOOL _isSeek; // 正在seek |
|
BOOL _isNaturalToEndTime; // 是否是自然结束 |
|
dispatch_group_t _netGroupQueue; // 组队列-网络 |
|
dispatch_group_t _dataGroupQueue; // 组队列-数据 |
|
CGFloat _seekValue; // seek value |
|
NSMutableDictionary *_remoteInfoDictionary;//控制中心信息 |
|
} |
|
/** player */ |
|
@property (nonatomic, strong) AVPlayer *player; |
|
/** playerItem */ |
|
@property (nonatomic, strong) AVPlayerItem *playerItem; |
|
/** 播放进度监测 */ |
|
@property (nonatomic, strong) id timeObserver; |
|
/** model数据数组 */ |
|
@property (nonatomic, strong) NSMutableArray<WXYZ_PlayerModel *> *playerModelArray; |
|
|
|
@property (nonatomic, copy) void(^seekCompletionBlock)(void); |
|
|
|
@property (nonatomic, assign) UIBackgroundTaskIdentifier bgTaskId; |
|
|
|
@property (nonatomic, readwrite, strong) WXYZ_PlayerModel *currentAudioModel; |
|
@property (nonatomic, readwrite, strong) WXYZ_PlayerInfoModel *currentAudioInfoModel; |
|
@property (nonatomic, readwrite, assign) TFBasicVoicePlayerState state; |
|
@property (nonatomic, readwrite, assign) CGFloat bufferProgress; |
|
@property (nonatomic, readwrite, assign) CGFloat progress; |
|
@property (nonatomic, readwrite, assign) CGFloat currentTime; |
|
@property (nonatomic, readwrite, assign) CGFloat totalTime; |
|
|
|
@end |
|
|
|
@implementation WXYZ_Player |
|
|
|
#pragma mark - 初始化 |
|
+ (WXYZ_Player *)sharedPlayer { |
|
static WXYZ_Player *player = nil; |
|
static dispatch_once_t predicate; |
|
dispatch_once(&predicate, ^{ |
|
player = [[[self class] alloc] init]; |
|
}); |
|
return player; |
|
} |
|
|
|
- (void)dealloc{ |
|
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
|
UIBackgroundTaskIdentifier taskID = [[UIApplication sharedExtensionApplication] beginBackgroundTaskWithExpirationHandler:^{}]; |
|
if (taskID != UIBackgroundTaskInvalid) { |
|
[[UIApplication sharedExtensionApplication] endBackgroundTask:taskID]; |
|
} |
|
} |
|
|
|
#pragma mark - 初始化播放器 |
|
- (void)initPlayerWithUserId:(NSString *)userId{ |
|
|
|
[[AVAudioSession sharedInstance] setActive:YES error:nil]; |
|
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; |
|
|
|
_isOtherPlaying = [AVAudioSession sharedInstance].otherAudioPlaying; |
|
|
|
self.playMode = WXYZ_PlayerModeOnlyOnce; |
|
self.state = TFBasicVoicePlayerStateStoped; |
|
_isBackground = NO; |
|
|
|
_netGroupQueue = dispatch_group_create(); |
|
_dataGroupQueue = dispatch_group_create(); |
|
|
|
[self addPlayerObserver]; |
|
[self addRemoteControlHandler]; |
|
} |
|
|
|
- (void)addPlayerObserver{ |
|
//将要进入后台 |
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; |
|
[notificationCenter addObserver:self |
|
selector:@selector(playerWillResignActive) |
|
name:UIApplicationWillResignActiveNotification |
|
object:nil]; |
|
//已经进入前台 |
|
[notificationCenter addObserver:self |
|
selector:@selector(playerDidEnterForeground) |
|
name:UIApplicationDidBecomeActiveNotification |
|
object:nil]; |
|
//监测耳机 |
|
[notificationCenter addObserver:self |
|
selector:@selector(playerAudioRouteChange:) |
|
name:AVAudioSessionRouteChangeNotification |
|
object:nil]; |
|
//监听播放器被打断(别的软件播放音乐,来电话) |
|
[notificationCenter addObserver:self |
|
selector:@selector(playerAudioBeInterrupted:) |
|
name:AVAudioSessionInterruptionNotification |
|
object:[AVAudioSession sharedInstance]]; |
|
} |
|
|
|
- (void)playerWillResignActive{ |
|
_isBackground = YES; |
|
_bgTaskId = [self backgroundPlayerID:_bgTaskId]; |
|
} |
|
|
|
- (UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId { |
|
// 设置并激活音频会话类别 |
|
AVAudioSession *session = [AVAudioSession sharedInstance]; |
|
[session setCategory:AVAudioSessionCategoryPlayback error:nil]; |
|
[session setActive:YES error:nil]; |
|
// 允许应用程序接收远程控制 |
|
// 设置后台任务ID |
|
UIBackgroundTaskIdentifier taskId = UIBackgroundTaskInvalid; |
|
taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; |
|
if(taskId != UIBackgroundTaskInvalid && |
|
backTaskId != UIBackgroundTaskInvalid) { |
|
[[UIApplication sharedApplication] endBackgroundTask:backTaskId]; |
|
} |
|
return taskId; |
|
} |
|
|
|
- (void)playerDidEnterForeground{ |
|
_isBackground = NO; |
|
} |
|
|
|
- (void)playerAudioRouteChange:(NSNotification *)notification{ |
|
NSInteger routeChangeReason = [notification.userInfo[AVAudioSessionRouteChangeReasonKey] integerValue]; |
|
switch (routeChangeReason) { |
|
case AVAudioSessionRouteChangeReasonNewDeviceAvailable://耳机插入 |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(player:isHeadphone:)]) { |
|
[self.delegate player:self isHeadphone:YES]; |
|
} |
|
break; |
|
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable://耳机拔出,停止播放操作 |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(player:isHeadphone:)]) { |
|
[self.delegate player:self isHeadphone:NO]; |
|
}else{ |
|
[self pause]; |
|
} |
|
break; |
|
case AVAudioSessionRouteChangeReasonCategoryChange: |
|
// |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
- (void)playerAudioBeInterrupted:(NSNotification *)notification{ |
|
NSDictionary *dic = notification.userInfo; |
|
if ([dic[AVAudioSessionInterruptionTypeKey] integerValue] == 1) {//打断开始 |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(player:isInterrupted:)]) { |
|
[self.delegate player:self isInterrupted:YES]; |
|
}else{ |
|
[self pause]; |
|
} |
|
}else {//打断结束 |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(player:isInterrupted:)]) { |
|
[self.delegate player:self isInterrupted:NO]; |
|
}else{ |
|
if ([notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue] == 1) { |
|
[self play]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
-(void)playerDidPlayToEndTime:(NSNotification *)notification{ |
|
// 其他音视频播放结束也会触发回调,例如穿山甲的激励视频。 |
|
if (notification.object != self.playerItem) return; |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerDidPlayToEndTime:)]) { |
|
[self.delegate playerDidPlayToEndTime:self]; |
|
}else{ |
|
_isNaturalToEndTime = YES; |
|
[self next]; |
|
} |
|
} |
|
|
|
/**远程线控*/ |
|
- (void)addRemoteControlHandler |
|
{ |
|
if (@available (iOS 7.1, *)) { |
|
kCodeSync([[UIApplication sharedApplication] endReceivingRemoteControlEvents]); |
|
kCodeSync([[UIApplication sharedApplication] beginReceivingRemoteControlEvents]); |
|
MPRemoteCommandCenter *center = [MPRemoteCommandCenter sharedCommandCenter]; |
|
[self addRemoteCommand:center.playCommand selector:@selector(play)]; |
|
[self addRemoteCommand:center.pauseCommand selector:@selector(pause)]; |
|
[self addRemoteCommand:center.previousTrackCommand selector:@selector(last)]; |
|
[self addRemoteCommand:center.nextTrackCommand selector:@selector(next)]; |
|
[center.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { |
|
if ([WXYZ_Player sharedPlayer].state == TFBasicVoicePlayerStatePlaying) { |
|
[[WXYZ_Player sharedPlayer] pause]; |
|
}else{ |
|
[[WXYZ_Player sharedPlayer] play]; |
|
} |
|
return MPRemoteCommandHandlerStatusSuccess; |
|
}]; |
|
|
|
if (@available (iOS 9.1,*)) { |
|
[center.changePlaybackPositionCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { |
|
MPChangePlaybackPositionCommandEvent *positionEvent = (MPChangePlaybackPositionCommandEvent *)event; |
|
if (self.totalTime > 0) { |
|
[self seekToTime:positionEvent.positionTime / self.totalTime completionBlock:nil]; |
|
} |
|
return MPRemoteCommandHandlerStatusSuccess; |
|
}]; |
|
} |
|
} |
|
} |
|
|
|
- (void)addRemoteCommand:(MPRemoteCommand *)command selector:(SEL)selector{ |
|
[command addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { |
|
if ([self respondsToSelector:selector]) { |
|
IMP imp = [self methodForSelector:selector]; |
|
void (*func)(id, SEL) = (void *)imp; |
|
func(self, selector); |
|
} |
|
return MPRemoteCommandHandlerStatusSuccess; |
|
}]; |
|
} |
|
|
|
- (void)setRemoteCenterPreviousEnable:(BOOL)remoteCenterPreviousEnable |
|
{ |
|
_remoteCenterPreviousEnable = remoteCenterPreviousEnable; |
|
[MPRemoteCommandCenter sharedCommandCenter].previousTrackCommand.enabled = remoteCenterPreviousEnable; |
|
} |
|
|
|
- (void)setRemoteCenterNextEnable:(BOOL)remoteCenterNextEnable |
|
{ |
|
_remoteCenterNextEnable = remoteCenterNextEnable; |
|
[MPRemoteCommandCenter sharedCommandCenter].nextTrackCommand.enabled = remoteCenterNextEnable; |
|
} |
|
|
|
#pragma mark - 数据源 |
|
|
|
- (void)reloadData{ |
|
if (self.dataSource && [self.dataSource respondsToSelector:@selector(audioDataForPlayer:)]) { |
|
if (!self.playerModelArray) { |
|
self.playerModelArray = [NSMutableArray array]; |
|
} |
|
if (self.playerModelArray.count != 0) { |
|
[self.playerModelArray removeAllObjects]; |
|
} |
|
dispatch_group_enter(_dataGroupQueue); |
|
dispatch_group_async(_dataGroupQueue, WXYZ_PlayerHighGlobalQueue, ^{ |
|
dispatch_async(WXYZ_PlayerHighGlobalQueue, ^{ |
|
|
|
[self.playerModelArray addObjectsFromArray:[self.dataSource audioDataForPlayer:self]]; |
|
|
|
//更新currentAudioId |
|
if (self.currentAudioModel.audioUrl) { |
|
[self.playerModelArray enumerateObjectsWithOptions:(NSEnumerationConcurrent) usingBlock:^(WXYZ_PlayerModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { |
|
if ([obj.audioUrl.absoluteString isEqualToString:self.currentAudioModel.audioUrl.absoluteString]) { |
|
self.currentAudioModel.audioId = idx; |
|
*stop = YES; |
|
} |
|
}]; |
|
} |
|
dispatch_group_leave(self->_dataGroupQueue); |
|
}); |
|
}); |
|
} |
|
} |
|
|
|
#pragma mark - 播放 IMPORTANT |
|
|
|
- (void)playWithAudioId:(NSUInteger)audioId{ |
|
dispatch_group_notify(_dataGroupQueue, WXYZ_PlayerHighGlobalQueue, ^{ |
|
if (self.playerModelArray.count > audioId) { |
|
self.currentAudioModel = self.playerModelArray[audioId]; |
|
[self audioPrePlay]; |
|
} |
|
}); |
|
} |
|
|
|
- (void)audioPrePlay{ |
|
[self reset]; |
|
|
|
if (self.dataSource && [self.dataSource respondsToSelector:@selector(audioInfoForPlayer:)]) { |
|
self.currentAudioInfoModel = [self.dataSource audioInfoForPlayer:self]; |
|
} |
|
|
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerAudioAddToPlayQueue:)]) { |
|
[self.delegate playerAudioAddToPlayQueue:self]; |
|
} |
|
|
|
[self loadPlayerItemWithURL:self.currentAudioModel.audioUrl]; |
|
} |
|
|
|
- (void)loadPlayerItemWithURL:(NSURL *)URL{ |
|
self.playerItem = [[AVPlayerItem alloc] initWithURL:URL]; |
|
[self loadPlayer]; |
|
} |
|
|
|
- (void)loadPlayerItemWithAsset:(AVURLAsset *)asset{ |
|
self.playerItem = [AVPlayerItem playerItemWithAsset:asset]; |
|
[self loadPlayer]; |
|
} |
|
|
|
- (void)loadPlayer{ |
|
self.player = [[AVPlayer alloc] initWithPlayerItem:self.playerItem]; |
|
if (@available(iOS 10.0,*)) { |
|
self.player.automaticallyWaitsToMinimizeStalling = NO; |
|
} |
|
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
|
// 处理章节开始播放,但监听还未执行的问题 |
|
[self play]; |
|
}); |
|
[self addProgressObserver]; |
|
[self addPlayingCenterInfo]; |
|
} |
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ |
|
if (object == self.player.currentItem) { |
|
if ([keyPath isEqualToString:WXYZStatusKey]) { |
|
AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue]; |
|
switch (status) { |
|
case AVPlayerItemStatusUnknown: |
|
self.state = TFBasicVoicePlayerStateStoped; |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerStateChange:)]) { |
|
[self.delegate playerStateChange:self.state]; |
|
} |
|
break; |
|
case AVPlayerItemStatusReadyToPlay: |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerReadyToPlay:)]) { |
|
if (self.state != TFBasicVoicePlayerStatePause) { |
|
[self.delegate playerReadyToPlay:self]; |
|
} |
|
} |
|
break; |
|
case AVPlayerItemStatusFailed: |
|
self.state = TFBasicVoicePlayerStateFail; |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerStateChange:)]) { |
|
[self.delegate playerStateChange:self.state]; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
} else if ([keyPath isEqualToString:WXYZLoadedTimeRangesKey]) { |
|
[self addBufferProgressObserver]; |
|
} else if ([keyPath isEqualToString:WXYZPlaybackBufferEmptyKey]) { |
|
|
|
} else if ([keyPath isEqualToString:WXYZPlaybackLikelyToKeepUpKey]) { |
|
|
|
} |
|
} else { |
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; |
|
} |
|
} |
|
|
|
#pragma mark - 缓冲进度 播放进度 歌曲锁屏信息 音频跳转 |
|
|
|
- (void)addBufferProgressObserver{ |
|
self.totalTime = CMTimeGetSeconds(self.playerItem.asset.duration); |
|
|
|
CMTimeRange timeRange = [self.playerItem.loadedTimeRanges.firstObject CMTimeRangeValue]; |
|
CGFloat startSeconds = CMTimeGetSeconds(timeRange.start); |
|
CGFloat durationSeconds = CMTimeGetSeconds(timeRange.duration); |
|
if (self.totalTime != 0) {//避免出现inf |
|
self.bufferProgress = (startSeconds + durationSeconds) / self.totalTime; |
|
} |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(player:bufferProgress:)]) { |
|
[self.delegate player:self bufferProgress:self.bufferProgress]; |
|
} |
|
} |
|
|
|
- (void)addProgressObserver |
|
{ |
|
WS(weakSelf) |
|
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1.0, 1.0) queue:nil usingBlock:^(CMTime time){ |
|
SS(sSelf) |
|
AVPlayerItem *currentItem = sSelf.playerItem; |
|
NSArray *loadedRanges = currentItem.seekableTimeRanges; |
|
if (loadedRanges.count > 0 && currentItem.duration.timescale != 0){ |
|
|
|
if (sSelf.state != TFBasicVoicePlayerStatePlaying && sSelf.state != TFBasicVoicePlayerStatePause) { |
|
sSelf.state = TFBasicVoicePlayerStatePlaying; |
|
if (sSelf.delegate && [sSelf.delegate respondsToSelector:@selector(playerStateChange:)]) { |
|
[sSelf.delegate playerStateChange:sSelf.state]; |
|
} |
|
} |
|
|
|
CGFloat currentT = (CGFloat)CMTimeGetSeconds(time); |
|
sSelf.currentTime = currentT + (sSelf.totalTime > 1?1:0); |
|
if (sSelf.totalTime != 0 && !isnan(sSelf.totalTime)) {// 避免出现inf |
|
sSelf.progress = (CMTimeGetSeconds([currentItem currentTime]) + (sSelf.totalTime > 1?1:0)) / sSelf.totalTime; |
|
} |
|
|
|
if (sSelf.delegate && [sSelf.delegate respondsToSelector:@selector(player:progress:currentTime:totalTime:)]) { |
|
[sSelf.delegate player:sSelf progress:sSelf.progress currentTime:currentT totalTime:sSelf.totalTime]; |
|
} |
|
|
|
[sSelf updatePlayingCenterInfo]; |
|
} |
|
}]; |
|
} |
|
|
|
- (void)addPlayingCenterInfo{ |
|
_remoteInfoDictionary = [NSMutableDictionary dictionary]; |
|
|
|
if (self.currentAudioInfoModel.audioName) { |
|
_remoteInfoDictionary[MPMediaItemPropertyTitle] = self.currentAudioInfoModel.audioName; |
|
} |
|
if (self.currentAudioInfoModel.audioAlbum) { |
|
_remoteInfoDictionary[MPMediaItemPropertyAlbumTitle] = self.currentAudioInfoModel.audioAlbum; |
|
} |
|
if (self.currentAudioInfoModel.audioSinger) { |
|
_remoteInfoDictionary[MPMediaItemPropertyArtist] = self.currentAudioInfoModel.audioSinger; |
|
} |
|
if ([self.currentAudioInfoModel.audioImage isKindOfClass:[UIImage class]] && self.currentAudioInfoModel.audioImage) { |
|
|
|
MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:CGSizeMake(BOOK_WIDTH, BOOK_HEIGHT) requestHandler:^UIImage * _Nonnull(CGSize size) { |
|
return self.currentAudioInfoModel.audioImage; |
|
}]; |
|
|
|
_remoteInfoDictionary[MPMediaItemPropertyArtwork] = artwork; |
|
} |
|
_remoteInfoDictionary[MPNowPlayingInfoPropertyPlaybackRate] = [NSNumber numberWithFloat:1.0]; |
|
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = _remoteInfoDictionary; |
|
} |
|
|
|
- (void)updatePlayingCenterInfo{ |
|
if (!_isBackground) {return;} |
|
|
|
if ([WXYZ_TouchAssistantView sharedManager].productionType == TFProductionTypeAudio) { |
|
_remoteInfoDictionary[MPNowPlayingInfoPropertyElapsedPlaybackTime] = [NSNumber numberWithDouble:CMTimeGetSeconds(self.playerItem.currentTime)]; |
|
_remoteInfoDictionary[MPMediaItemPropertyPlaybackDuration] = [NSNumber numberWithDouble:CMTimeGetSeconds(self.playerItem.duration)]; |
|
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = _remoteInfoDictionary; |
|
} |
|
} |
|
|
|
- (void)seekToTime:(CGFloat)value completionBlock:(void (^)(void))completionBlock{ |
|
_isSeek = YES; |
|
// 先暂停 |
|
BOOL resumePlay = NO; |
|
if (self.state == TFBasicVoicePlayerStatePlaying || self.state == TFBasicVoicePlayerStateLoading) { |
|
self.state = TFBasicVoicePlayerStatePause; |
|
[self.player pause]; |
|
resumePlay = YES; |
|
} |
|
|
|
[self didSeekToTime:value resumePlay:resumePlay completionBlock:completionBlock]; |
|
} |
|
|
|
- (void)didSeekToTime:(CGFloat)value resumePlay:(BOOL)resumePlay completionBlock:(void (^)(void))completionBlock |
|
{ |
|
[self.player seekToTime:CMTimeMake(floorf(self.totalTime * value), 1) |
|
toleranceBefore:kCMTimeZero |
|
toleranceAfter:kCMTimeZero |
|
completionHandler:^(BOOL finished) { |
|
if (finished) { |
|
self->_isSeek = NO; |
|
[self play]; |
|
if (!resumePlay) { |
|
[self pause]; |
|
} |
|
if (completionBlock) { |
|
completionBlock(); |
|
} |
|
} |
|
}]; |
|
} |
|
|
|
/**倍速播放*/ |
|
- (void)setRate:(CGFloat)rate { |
|
_playRate = rate; |
|
for (AVPlayerItemTrack *track in self.playerItem.tracks){ |
|
if ([track.assetTrack.mediaType isEqual:AVMediaTypeAudio]){ |
|
track.enabled = YES; |
|
} |
|
} |
|
|
|
if (self.state == TFBasicVoicePlayerStatePlaying) { |
|
self.player.rate = rate; |
|
} |
|
} |
|
|
|
/**释放播放器*/ |
|
- (void)deallocPlayer{ |
|
|
|
[self reset]; |
|
|
|
self.state = TFBasicVoicePlayerStateStoped; |
|
|
|
if (@available(iOS 7.1, *)) { |
|
[[UIApplication sharedApplication] endReceivingRemoteControlEvents]; |
|
MPRemoteCommandCenter *center = [MPRemoteCommandCenter sharedCommandCenter]; |
|
[[center playCommand] removeTarget:self]; |
|
[[center pauseCommand] removeTarget:self]; |
|
[[center nextTrackCommand] removeTarget:self]; |
|
[[center previousTrackCommand] removeTarget:self]; |
|
[[center togglePlayPauseCommand] removeTarget:self]; |
|
if(@available(iOS 9.1, *)) { |
|
[center.changePlaybackPositionCommand removeTarget:self]; |
|
} |
|
} |
|
|
|
if (_isOtherPlaying) { |
|
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil]; |
|
}else{ |
|
[[AVAudioSession sharedInstance] setActive:NO error:nil]; |
|
} |
|
[self.player.currentItem cancelPendingSeeks]; |
|
[self.player.currentItem.asset cancelLoading]; |
|
|
|
if (self.playerModelArray) { |
|
self.playerModelArray = nil; |
|
} |
|
|
|
if (self.playerItem) { |
|
self.playerItem = nil; |
|
} |
|
|
|
if (self.player) { |
|
self.player = nil; |
|
} |
|
} |
|
|
|
- (void)reset{ |
|
if (self.state == TFBasicVoicePlayerStatePlaying) { |
|
[self.player pause]; |
|
} |
|
|
|
//移除进度观察者 |
|
if (self.timeObserver) { |
|
[self.player removeTimeObserver:self.timeObserver]; |
|
self.timeObserver = nil; |
|
} |
|
|
|
//重置 |
|
self.progress = .0f; |
|
self.bufferProgress = .0f; |
|
self.currentTime = .0f; |
|
self.totalTime = .0f; |
|
} |
|
|
|
#pragma mark - 播放 暂停 下一首 上一首 |
|
/**播放*/ |
|
- (void)play |
|
{ |
|
[self.player play]; |
|
self.player.rate = self.playRate; |
|
self.state = TFBasicVoicePlayerStateLoading; |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerStateChange:)]) { |
|
[self.delegate playerStateChange:self.state]; |
|
} |
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
[[UIApplication sharedApplication] endReceivingRemoteControlEvents]; |
|
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; |
|
}); |
|
} |
|
|
|
/**暂停*/ |
|
- (void)pause |
|
{ |
|
[self.player pause]; |
|
self.state = TFBasicVoicePlayerStatePause; |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(playerStateChange:)]) { |
|
[self.delegate playerStateChange:self.state]; |
|
} |
|
} |
|
|
|
/**下一首*/ |
|
- (void)next |
|
{ |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(audioPlayerRemoteCenterSwitchToNext)]) { |
|
[self.delegate audioPlayerRemoteCenterSwitchToNext]; |
|
} |
|
} |
|
|
|
/**上一首*/ |
|
- (void)last |
|
{ |
|
if (self.delegate && [self.delegate respondsToSelector:@selector(audioPlayerRemoteCenterSwitchToPrevious)]) { |
|
[self.delegate audioPlayerRemoteCenterSwitchToPrevious]; |
|
} |
|
} |
|
|
|
#pragma mark - setter |
|
|
|
- (void)setCategory:(AVAudioSessionCategory)category{ |
|
[[AVAudioSession sharedInstance] setCategory:category error:nil]; |
|
} |
|
|
|
- (void)setPlayerItem:(AVPlayerItem *)playerItem{ |
|
if (_playerItem == playerItem) { |
|
return; |
|
} |
|
if (_playerItem) { |
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; |
|
[_playerItem removeObserver:self forKeyPath:WXYZStatusKey]; |
|
[_playerItem removeObserver:self forKeyPath:WXYZLoadedTimeRangesKey]; |
|
[_playerItem removeObserver:self forKeyPath:WXYZPlaybackBufferEmptyKey]; |
|
[_playerItem removeObserver:self forKeyPath:WXYZPlaybackLikelyToKeepUpKey]; |
|
} |
|
_playerItem = playerItem; |
|
if (playerItem) { |
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerDidPlayToEndTime:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; |
|
[playerItem addObserver:self forKeyPath:WXYZStatusKey options:NSKeyValueObservingOptionNew context:nil]; |
|
[playerItem addObserver:self forKeyPath:WXYZLoadedTimeRangesKey options:NSKeyValueObservingOptionNew context:nil]; |
|
[playerItem addObserver:self forKeyPath:WXYZPlaybackBufferEmptyKey options:NSKeyValueObservingOptionNew context:nil]; |
|
[playerItem addObserver:self forKeyPath:WXYZPlaybackLikelyToKeepUpKey options:NSKeyValueObservingOptionNew context:nil]; |
|
} |
|
} |
|
|
|
@end
|
|
|