ZQPlayer.m 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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. _playerLayer.backgroundColor = [UIColor clearColor].CGColor;
  36. }
  37. return self;
  38. }
  39. - (instancetype)initWithUrl:(NSString *)url {
  40. if (self = [self init]) {
  41. [self nextWithUrl:url];
  42. }
  43. return self;
  44. }
  45. - (void)nextWithUrl:(NSString *)url {
  46. [_player pause];
  47. // 移除之前的时间监听
  48. [self removeTimeObserver];
  49. if (_playerItme) {
  50. [_playerItme removeObserver:self forKeyPath:@"status"];
  51. [_playerItme removeObserver:self forKeyPath:@"loadedTimeRanges"];
  52. [_playerItme removeObserver:self forKeyPath:@"playbackBufferEmpty"];
  53. [_playerItme removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
  54. [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItme];
  55. _playerItme = nil;
  56. }
  57. if (!url) {
  58. return;
  59. }
  60. _playUrl = url;
  61. NSURL *videoUrl;
  62. if ([url containsString:@"http"]) {
  63. videoUrl = [self translateIllegalCharacterWtihUrlStr:url];
  64. }else {
  65. videoUrl = [NSURL fileURLWithPath:url];
  66. }
  67. _playerItme = [AVPlayerItem playerItemWithURL:videoUrl];
  68. if (@available(iOS 9.0, *)) {
  69. _playerItme.canUseNetworkResourcesForLiveStreamingWhilePaused = YES;
  70. }
  71. [_player replaceCurrentItemWithPlayerItem:_playerItme];
  72. // AVPlayer播放完成通知
  73. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItme];
  74. // 监听播放状态
  75. [_playerItme addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  76. // 监听loadedTimeRanges属性
  77. [_playerItme addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
  78. // Will warn you when your buffer is empty
  79. [_playerItme addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
  80. // Will warn you when your buffer is good to go again.
  81. [_playerItme addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
  82. _player.rate=1.0;
  83. if (_isPlaying) {
  84. [_player play];
  85. }else {
  86. [_player pause];
  87. }
  88. }
  89. #pragma mark - 公开的方法
  90. - (void)play {
  91. if (!_isPlaying) {
  92. if (!_playerItme) {
  93. [self nextWithUrl:_playUrl];
  94. }
  95. [_player play];
  96. _isPlaying = YES;
  97. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  98. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStatePlaying];
  99. }
  100. }
  101. }
  102. - (void)pause {
  103. if (_isPlaying) {
  104. _isPlaying = NO;
  105. [_player pause];
  106. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  107. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStatePause];
  108. }
  109. }
  110. }
  111. - (void)stop {
  112. _isPlaying = NO;
  113. [_player pause];
  114. // 移除之前的时间监听
  115. [self removeTimeObserver];
  116. if (_playerItme) {
  117. [_playerItme removeObserver:self forKeyPath:@"status"];
  118. [_playerItme removeObserver:self forKeyPath:@"loadedTimeRanges"];
  119. [_playerItme removeObserver:self forKeyPath:@"playbackBufferEmpty"];
  120. [_playerItme removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
  121. [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItme];
  122. _playerItme = nil;
  123. }
  124. [_player replaceCurrentItemWithPlayerItem:_playerItme];
  125. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  126. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStateStop];
  127. }
  128. }
  129. #pragma mark - Priva Method
  130. //设置播放进度和时间
  131. -(void)setTheProgressOfPlayTime {
  132. self.timeInterval = CMTimeGetSeconds(_playerItme.asset.duration);
  133. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerTotalTime:totalTime:)]) {
  134. [self.delegate ZQPlayerTotalTime:self totalTime:self.timeInterval];
  135. }
  136. [self removeTimeObserver];
  137. __weak typeof(self) weakSelf = self;
  138. _playerTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 10) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
  139. if (weakSelf.isPlaying) {
  140. CGFloat currentTime = CMTimeGetSeconds(time);
  141. if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(ZQPlayerCurrentTime:currentTime:)]) {
  142. [weakSelf.delegate ZQPlayerCurrentTime:weakSelf currentTime:currentTime];
  143. }
  144. }
  145. }];
  146. }
  147. - (void)removeTimeObserver {
  148. if (_playerTimeObserver) {
  149. @try {
  150. [_player removeTimeObserver:_playerTimeObserver];
  151. }@catch (id e) {
  152. }@finally {
  153. _playerTimeObserver = nil;
  154. }
  155. }
  156. }
  157. - (NSURL *)translateIllegalCharacterWtihUrlStr:(NSString *)yourUrl{
  158. //如果链接中存在中文或某些特殊字符,需要通过以下代码转译
  159. yourUrl = [yourUrl stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  160. NSString *encodedString = [yourUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  161. return [NSURL URLWithString:encodedString];
  162. }
  163. #pragma mark - NSNotification
  164. - (void)moviePlayDidEnd:(NSNotification *)noti {
  165. [_player seekToTime:kCMTimeZero];
  166. _isPlaying = NO;
  167. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  168. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStateStop];
  169. }
  170. }
  171. // 应用退到后台
  172. - (void)appDidEnterBackground {
  173. [self pause];
  174. }
  175. // 应用进入前台
  176. - (void)appDidEnterPlayGround {
  177. }
  178. #pragma mark - KVO
  179. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  180. if (object == _playerItme) {
  181. if ([keyPath isEqualToString:@"status"]) {
  182. if (_playerItme.status == AVPlayerStatusReadyToPlay) {
  183. [self setTheProgressOfPlayTime];
  184. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  185. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStateReadyToPlay];
  186. }
  187. }else if (_playerItme.status == AVPlayerStatusFailed) {
  188. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  189. [self.delegate ZQPlayerStateChange:self state:ZQPlayerStateFailed];
  190. }
  191. [self stop];
  192. }
  193. } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
  194. CMTimeRange range = [_playerItme.loadedTimeRanges.firstObject CMTimeRangeValue];
  195. CGFloat loadSeconds = CMTimeGetSeconds(range.start) + CMTimeGetSeconds(range.duration);
  196. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerLoadTime:loadTime:)]) {
  197. [self.delegate ZQPlayerLoadTime:self loadTime:loadSeconds];
  198. }
  199. }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {
  200. // 当缓冲是空的时候
  201. }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
  202. if (!_playerItme.playbackLikelyToKeepUp) {
  203. _isBuffering = YES;
  204. }else {
  205. _isBuffering = NO;
  206. }
  207. if (self.delegate && [self.delegate respondsToSelector:@selector(ZQPlayerStateChange:state:)]) {
  208. [self.delegate ZQPlayerStateChange:self state:_isBuffering?ZQPlayerStateBufferEmpty:ZQPlayerStateKeepUp];
  209. }
  210. }
  211. }
  212. }
  213. #pragma mark - dealloc
  214. - (void)dealloc {
  215. [[NSNotificationCenter defaultCenter]removeObserver:self];
  216. [self.playerItme removeObserver:self forKeyPath:@"status"];
  217. [self.playerItme removeObserver:self forKeyPath:@"loadedTimeRanges"];
  218. [self.playerItme removeObserver:self forKeyPath:@"playbackBufferEmpty"];
  219. [self.playerItme removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
  220. // 恢复其他音频
  221. [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
  222. }
  223. @end