ZQPlayer.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. //
  2. // ZQPlayer.m
  3. // ZQPlayer
  4. //
  5. // Created by wang on 2018/3/30.
  6. // Copyright © 2018年 qigge. All rights reserved.
  7. //
  8. #import "ZQPlayer.h"
  9. @interface ZQPlayer ()
  10. // 是否正在播放
  11. @property (nonatomic, assign) BOOL isPlaying;
  12. // 是否在缓冲
  13. @property (nonatomic, assign) BOOL isBuffering;
  14. /** player 时间监听 */
  15. @property (nonatomic,strong) id playerTimeObserver;
  16. @end
  17. @implementation ZQPlayer
  18. #pragma mark - Public Method
  19. - (instancetype)init {
  20. if (self = [super init]) {
  21. // 打断
  22. [[AVAudioSession sharedInstance] setActive:YES error:nil];
  23. [[AVAudioSession sharedInstance]
  24. setCategory: AVAudioSessionCategorySoloAmbient
  25. error: nil];
  26. // app退到后台
  27. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground) name:UIApplicationWillResignActiveNotification object:nil];
  28. // app进入前台
  29. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterPlayGround) name:UIApplicationDidBecomeActiveNotification object:nil];
  30. _isPlaying = NO;
  31. _isBuffering = YES;
  32. _player = [AVPlayer playerWithPlayerItem:_playerItme];
  33. _playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
  34. _playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
  35. }
  36. return self;
  37. }
  38. - (instancetype)initWithUrl:(NSString *)url {
  39. if (self = [self init]) {
  40. [self nextWithUrl:url];
  41. }
  42. return self;
  43. }
  44. - (void)nextWithUrl:(NSString *)url {
  45. [_player pause];
  46. // 移除之前的时间监听
  47. [self removeTimeObserver];
  48. if (_playerItme) {
  49. [_playerItme removeObserver:self forKeyPath:@"status"];
  50. [_playerItme removeObserver:self forKeyPath:@"loadedTimeRanges"];
  51. [_playerItme removeObserver:self forKeyPath:@"playbackBufferEmpty"];
  52. [_playerItme removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
  53. [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItme];
  54. _playerItme = nil;
  55. }
  56. NSURL *videoUrl;
  57. if ([url containsString:@"http"]) {
  58. videoUrl = [self translateIllegalCharacterWtihUrlStr:url];
  59. }else {
  60. videoUrl = [NSURL fileURLWithPath:url];
  61. }
  62. _playerItme = [AVPlayerItem playerItemWithURL:videoUrl];
  63. if (@available(iOS 9.0, *)) {
  64. _playerItme.canUseNetworkResourcesForLiveStreamingWhilePaused = YES;
  65. }
  66. [_player replaceCurrentItemWithPlayerItem:_playerItme];
  67. // AVPlayer播放完成通知
  68. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItme];
  69. // 监听播放状态
  70. [_playerItme addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  71. // 监听loadedTimeRanges属性
  72. [_playerItme addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
  73. // Will warn you when your buffer is empty
  74. [_playerItme addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
  75. // Will warn you when your buffer is good to go again.
  76. [_playerItme addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
  77. _player.rate=1.0;
  78. if (_isPlaying) {
  79. [_player play];
  80. }else {
  81. [_player pause];
  82. }
  83. }
  84. #pragma mark - 公开的方法
  85. - (void)play {
  86. if (!_isPlaying) {
  87. [_player play];
  88. _isPlaying = YES;
  89. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  90. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStatePlaying];
  91. }
  92. }
  93. }
  94. - (void)pause {
  95. if (_isPlaying) {
  96. _isPlaying = NO;
  97. [_player pause];
  98. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  99. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStatePause];
  100. }
  101. }
  102. }
  103. #pragma mark - Priva Method
  104. //设置播放进度和时间
  105. -(void)setTheProgressOfPlayTime {
  106. self.timeInterval = CMTimeGetSeconds(_playerItme.asset.duration);
  107. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerTotalTime:totalTime:)]) {
  108. [self.delegate ZQPlayerTotalTime:self totalTime:self.timeInterval];
  109. }
  110. [self removeTimeObserver];
  111. __weak typeof(self) weakSelf = self;
  112. _playerTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
  113. if (weakSelf.isPlaying) {
  114. CGFloat currentTime = CMTimeGetSeconds(time);
  115. if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(ZQPlayerCurrentTime:currentTime:)]) {
  116. [weakSelf.delegate ZQPlayerCurrentTime:weakSelf currentTime:currentTime];
  117. }
  118. }
  119. }];
  120. }
  121. - (void)removeTimeObserver {
  122. if (_playerTimeObserver) {
  123. @try {
  124. [_player removeTimeObserver:_playerTimeObserver];
  125. }@catch (id e) {
  126. }@finally {
  127. _playerTimeObserver = nil;
  128. }
  129. }
  130. }
  131. - (NSURL *)translateIllegalCharacterWtihUrlStr:(NSString *)yourUrl{
  132. //如果链接中存在中文或某些特殊字符,需要通过以下代码转译
  133. NSString *encodedString = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)yourUrl, (CFStringRef)@"!NULL,'()*+,-./:;=?@_~%#[]", NULL, kCFStringEncodingUTF8));
  134. return [NSURL URLWithString:encodedString];
  135. }
  136. #pragma mark - NSNotification
  137. - (void)moviePlayDidEnd:(NSNotification *)noti {
  138. [_player seekToTime:kCMTimeZero];
  139. _isPlaying = NO;
  140. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  141. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStateStop];
  142. }
  143. }
  144. // 应用退到后台
  145. - (void)appDidEnterBackground {
  146. [self pause];
  147. }
  148. // 应用进入前台
  149. - (void)appDidEnterPlayGround {
  150. }
  151. #pragma mark - KVO
  152. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  153. if (object == _playerItme) {
  154. if ([keyPath isEqualToString:@"status"]) {
  155. if (_playerItme.status == AVPlayerStatusReadyToPlay) {
  156. [self setTheProgressOfPlayTime];
  157. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  158. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStateReadyToPlay];
  159. }
  160. }
  161. } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
  162. CMTimeRange range = [_playerItme.loadedTimeRanges.firstObject CMTimeRangeValue];
  163. CGFloat loadSeconds = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration);
  164. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerLoadTime:loadTime:)]) {
  165. [self.delegate ZQPlayerLoadTime:self loadTime:loadSeconds];
  166. }
  167. }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {
  168. // 当缓冲是空的时候
  169. }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
  170. if (!_playerItme.playbackLikelyToKeepUp) {
  171. _isBuffering = YES;
  172. }else {
  173. _isBuffering = NO;
  174. }
  175. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  176. [self.delegate ZQPlayerStateChange:self state:_isBuffering?ZQPlayerStateBufferEmpty:ZQPlayerStateKeepUp];
  177. }
  178. }
  179. }
  180. }
  181. #pragma mark - dealloc
  182. - (void)dealloc {
  183. [[NSNotificationCenter defaultCenter]removeObserver:self];
  184. [self.playerItme removeObserver:self forKeyPath:@"status"];
  185. [self.playerItme removeObserver:self forKeyPath:@"loadedTimeRanges"];
  186. [self.playerItme removeObserver:self forKeyPath:@"playbackBufferEmpty"];
  187. [self.playerItme removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
  188. // 恢复其他音频
  189. [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  190. }
  191. @end