ZFAVPlayerManager.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. //
  2. // ZFAVPlayerManager.m
  3. // ZFPlayer
  4. //
  5. // Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. #import "ZFAVPlayerManager.h"
  25. #import <UIKit/UIKit.h>
  26. //edit by lcy begin
  27. #import <KTVHTTPCache/KTVHTTPCache.h>
  28. //edit by lcy end
  29. #if __has_include(<ZFPlayer/ZFPlayer.h>)
  30. #import <ZFPlayer/ZFPlayer.h>
  31. #import <ZFPlayer/ZFReachabilityManager.h>
  32. #else
  33. #import "ZFPlayer.h"
  34. #import "ZFReachabilityManager.h"
  35. #endif
  36. #pragma clang diagnostic push
  37. #pragma clang diagnostic ignored"-Wdeprecated-declarations"
  38. /*!
  39. * Refresh interval for timed observations of AVPlayer
  40. */
  41. static NSString *const kStatus = @"status";
  42. static NSString *const kLoadedTimeRanges = @"loadedTimeRanges";
  43. static NSString *const kPlaybackBufferEmpty = @"playbackBufferEmpty";
  44. static NSString *const kPlaybackLikelyToKeepUp = @"playbackLikelyToKeepUp";
  45. static NSString *const kPresentationSize = @"presentationSize";
  46. @interface ZFPlayerPresentView : ZFPlayerView
  47. @property (nonatomic, strong) AVPlayer *player;
  48. /// default is AVLayerVideoGravityResizeAspect.
  49. @property (nonatomic, strong) AVLayerVideoGravity videoGravity;
  50. @end
  51. @implementation ZFPlayerPresentView
  52. + (Class)layerClass {
  53. return [AVPlayerLayer class];
  54. }
  55. - (AVPlayerLayer *)avLayer {
  56. return (AVPlayerLayer *)self.layer;
  57. }
  58. - (instancetype)initWithFrame:(CGRect)frame {
  59. self = [super initWithFrame:frame];
  60. if (self) {
  61. self.backgroundColor = [UIColor blackColor];
  62. }
  63. return self;
  64. }
  65. - (void)setPlayer:(AVPlayer *)player {
  66. if (player == _player) return;
  67. self.avLayer.player = player;
  68. }
  69. - (void)setVideoGravity:(AVLayerVideoGravity)videoGravity {
  70. if (videoGravity == self.videoGravity) return;
  71. [self avLayer].videoGravity = videoGravity;
  72. }
  73. - (AVLayerVideoGravity)videoGravity {
  74. return [self avLayer].videoGravity;
  75. }
  76. @end
  77. @interface ZFAVPlayerManager () {
  78. id _timeObserver;
  79. id _itemEndObserver;
  80. ZFKVOController *_playerItemKVO;
  81. }
  82. @property (nonatomic, strong) AVPlayerLayer *playerLayer;
  83. @property (nonatomic, assign) BOOL isBuffering;
  84. @property (nonatomic, assign) BOOL isReadyToPlay;
  85. @end
  86. @implementation ZFAVPlayerManager
  87. @synthesize view = _view;
  88. @synthesize currentTime = _currentTime;
  89. @synthesize totalTime = _totalTime;
  90. @synthesize playerPlayTimeChanged = _playerPlayTimeChanged;
  91. @synthesize playerBufferTimeChanged = _playerBufferTimeChanged;
  92. @synthesize playerDidToEnd = _playerDidToEnd;
  93. @synthesize bufferTime = _bufferTime;
  94. @synthesize playState = _playState;
  95. @synthesize loadState = _loadState;
  96. @synthesize assetURL = _assetURL;
  97. @synthesize playerPrepareToPlay = _playerPrepareToPlay;
  98. @synthesize playerReadyToPlay = _playerReadyToPlay;
  99. @synthesize playerPlayStateChanged = _playerPlayStateChanged;
  100. @synthesize playerLoadStateChanged = _playerLoadStateChanged;
  101. @synthesize seekTime = _seekTime;
  102. @synthesize muted = _muted;
  103. @synthesize volume = _volume;
  104. @synthesize presentationSize = _presentationSize;
  105. @synthesize isPlaying = _isPlaying;
  106. @synthesize rate = _rate;
  107. @synthesize isPreparedToPlay = _isPreparedToPlay;
  108. @synthesize shouldAutoPlay = _shouldAutoPlay;
  109. @synthesize scalingMode = _scalingMode;
  110. @synthesize playerPlayFailed = _playerPlayFailed;
  111. @synthesize presentationSizeChanged = _presentationSizeChanged;
  112. - (instancetype)init {
  113. self = [super init];
  114. if (self) {
  115. _scalingMode = ZFPlayerScalingModeAspectFit;
  116. _shouldAutoPlay = YES;
  117. }
  118. return self;
  119. }
  120. - (void)prepareToPlay {
  121. if (!_assetURL) return;
  122. _isPreparedToPlay = YES;
  123. [self initializePlayer];
  124. if (self.shouldAutoPlay) {
  125. [self play];
  126. }
  127. self.loadState = ZFPlayerLoadStatePrepare;
  128. if (self.playerPrepareToPlay) self.playerPrepareToPlay(self, self.assetURL);
  129. }
  130. - (void)reloadPlayer {
  131. self.seekTime = self.currentTime;
  132. [self prepareToPlay];
  133. }
  134. - (void)play {
  135. if (!_isPreparedToPlay) {
  136. [self prepareToPlay];
  137. } else {
  138. [self.player play];
  139. self.player.rate = self.rate;
  140. self->_isPlaying = YES;
  141. self.playState = ZFPlayerPlayStatePlaying;
  142. }
  143. }
  144. - (void)pause {
  145. [self.player pause];
  146. self->_isPlaying = NO;
  147. self.playState = ZFPlayerPlayStatePaused;
  148. [_playerItem cancelPendingSeeks];
  149. [_asset cancelLoading];
  150. }
  151. - (void)stop {
  152. [_playerItemKVO safelyRemoveAllObservers];
  153. self.loadState = ZFPlayerLoadStateUnknown;
  154. self.playState = ZFPlayerPlayStatePlayStopped;
  155. if (self.player.rate != 0) [self.player pause];
  156. [self.player removeTimeObserver:_timeObserver];
  157. [self.player replaceCurrentItemWithPlayerItem:nil];
  158. _timeObserver = nil;
  159. [[NSNotificationCenter defaultCenter] removeObserver:_itemEndObserver name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
  160. _itemEndObserver = nil;
  161. _isPlaying = NO;
  162. _player = nil;
  163. _assetURL = nil;
  164. _playerItem = nil;
  165. _isPreparedToPlay = NO;
  166. self->_currentTime = 0;
  167. self->_totalTime = 0;
  168. self->_bufferTime = 0;
  169. self.isReadyToPlay = NO;
  170. }
  171. - (void)replay {
  172. @weakify(self)
  173. [self seekToTime:0 completionHandler:^(BOOL finished) {
  174. @strongify(self)
  175. [self play];
  176. }];
  177. }
  178. - (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
  179. if (self.totalTime > 0) {
  180. CMTime seekTime = CMTimeMake(time, 1);
  181. [_player seekToTime:seekTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:completionHandler];
  182. } else {
  183. self.seekTime = time;
  184. }
  185. }
  186. - (UIImage *)thumbnailImageAtCurrentTime {
  187. AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:_asset];
  188. CMTime expectedTime = _playerItem.currentTime;
  189. CGImageRef cgImage = NULL;
  190. imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
  191. imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
  192. cgImage = [imageGenerator copyCGImageAtTime:expectedTime actualTime:NULL error:NULL];
  193. if (!cgImage) {
  194. imageGenerator.requestedTimeToleranceBefore = kCMTimePositiveInfinity;
  195. imageGenerator.requestedTimeToleranceAfter = kCMTimePositiveInfinity;
  196. cgImage = [imageGenerator copyCGImageAtTime:expectedTime actualTime:NULL error:NULL];
  197. }
  198. UIImage *image = [UIImage imageWithCGImage:cgImage];
  199. return image;
  200. }
  201. #pragma mark - private method
  202. /// Calculate buffer progress
  203. - (NSTimeInterval)availableDuration {
  204. NSArray *timeRangeArray = _playerItem.loadedTimeRanges;
  205. CMTime currentTime = [_player currentTime];
  206. BOOL foundRange = NO;
  207. CMTimeRange aTimeRange = {0};
  208. if (timeRangeArray.count) {
  209. aTimeRange = [[timeRangeArray objectAtIndex:0] CMTimeRangeValue];
  210. if (CMTimeRangeContainsTime(aTimeRange, currentTime)) {
  211. foundRange = YES;
  212. }
  213. }
  214. if (foundRange) {
  215. CMTime maxTime = CMTimeRangeGetEnd(aTimeRange);
  216. NSTimeInterval playableDuration = CMTimeGetSeconds(maxTime);
  217. if (playableDuration > 0) {
  218. return playableDuration;
  219. }
  220. }
  221. return 0;
  222. }
  223. - (void)initializePlayer {
  224. _asset = [AVURLAsset URLAssetWithURL:self.assetURL options:self.requestHeader];
  225. _playerItem = [AVPlayerItem playerItemWithAsset:_asset];
  226. _player = [AVPlayer playerWithPlayerItem:_playerItem];
  227. [self enableAudioTracks:YES inPlayerItem:_playerItem];
  228. ZFPlayerPresentView *presentView = (ZFPlayerPresentView *)self.view;
  229. presentView.player = _player;
  230. self.scalingMode = _scalingMode;
  231. if (@available(iOS 9.0, *)) {
  232. _playerItem.canUseNetworkResourcesForLiveStreamingWhilePaused = NO;
  233. }
  234. if (@available(iOS 10.0, *)) {
  235. _playerItem.preferredForwardBufferDuration = 5;
  236. _player.automaticallyWaitsToMinimizeStalling = NO;
  237. }
  238. [self itemObserving];
  239. }
  240. /// Playback speed switching method
  241. - (void)enableAudioTracks:(BOOL)enable inPlayerItem:(AVPlayerItem*)playerItem {
  242. for (AVPlayerItemTrack *track in playerItem.tracks){
  243. if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo]) {
  244. track.enabled = enable;
  245. }
  246. }
  247. }
  248. /**
  249. * 缓冲较差时候回调这里
  250. */
  251. - (void)bufferingSomeSecond {
  252. // playbackBufferEmpty会反复进入,因此在bufferingOneSecond延时播放执行完之前再调用bufferingSomeSecond都忽略
  253. if (self.isBuffering || self.playState == ZFPlayerPlayStatePlayStopped) return;
  254. /// 没有网络
  255. if ([ZFReachabilityManager sharedManager].networkReachabilityStatus == ZFReachabilityStatusNotReachable) return;
  256. self.isBuffering = YES;
  257. // 需要先暂停一小会之后再播放,否则网络状况不好的时候时间在走,声音播放不出来
  258. [self.player pause];
  259. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  260. // 如果此时用户已经暂停了,则不再需要开启播放了
  261. if (!self.isPlaying && self.loadState == ZFPlayerLoadStateStalled) {
  262. self.isBuffering = NO;
  263. return;
  264. }
  265. [self play];
  266. // 如果执行了play还是没有播放则说明还没有缓存好,则再次缓存一段时间
  267. self.isBuffering = NO;
  268. if (!self.playerItem.isPlaybackLikelyToKeepUp) [self bufferingSomeSecond];
  269. });
  270. }
  271. - (void)itemObserving {
  272. [_playerItemKVO safelyRemoveAllObservers];
  273. _playerItemKVO = [[ZFKVOController alloc] initWithTarget:_playerItem];
  274. [_playerItemKVO safelyAddObserver:self
  275. forKeyPath:kStatus
  276. options:NSKeyValueObservingOptionNew
  277. context:nil];
  278. [_playerItemKVO safelyAddObserver:self
  279. forKeyPath:kPlaybackBufferEmpty
  280. options:NSKeyValueObservingOptionNew
  281. context:nil];
  282. [_playerItemKVO safelyAddObserver:self
  283. forKeyPath:kPlaybackLikelyToKeepUp
  284. options:NSKeyValueObservingOptionNew
  285. context:nil];
  286. [_playerItemKVO safelyAddObserver:self
  287. forKeyPath:kLoadedTimeRanges
  288. options:NSKeyValueObservingOptionNew
  289. context:nil];
  290. [_playerItemKVO safelyAddObserver:self
  291. forKeyPath:kPresentationSize
  292. options:NSKeyValueObservingOptionNew
  293. context:nil];
  294. CMTime interval = CMTimeMakeWithSeconds(self.timeRefreshInterval > 0 ? self.timeRefreshInterval : 0.1, NSEC_PER_SEC);
  295. @weakify(self)
  296. _timeObserver = [self.player addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
  297. @strongify(self)
  298. if (!self) return;
  299. NSArray *loadedRanges = self.playerItem.seekableTimeRanges;
  300. if (self.isPlaying && self.loadState == ZFPlayerLoadStateStalled) self.player.rate = self.rate;
  301. if (loadedRanges.count > 0) {
  302. if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
  303. }
  304. }];
  305. _itemEndObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
  306. @strongify(self)
  307. if (!self) return;
  308. self.playState = ZFPlayerPlayStatePlayStopped;
  309. if (self.playerDidToEnd) self.playerDidToEnd(self);
  310. }];
  311. }
  312. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  313. dispatch_async(dispatch_get_main_queue(), ^{
  314. if ([keyPath isEqualToString:kStatus]) {
  315. if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
  316. if (!self.isReadyToPlay) {
  317. self.isReadyToPlay = YES;
  318. self.loadState = ZFPlayerLoadStatePlaythroughOK;
  319. if (self.playerReadyToPlay) self.playerReadyToPlay(self, self.assetURL);
  320. }
  321. if (self.seekTime) {
  322. [self seekToTime:self.seekTime completionHandler:nil];
  323. self.seekTime = 0;
  324. }
  325. if (self.isPlaying) [self play];
  326. self.player.muted = self.muted;
  327. NSArray *loadedRanges = self.playerItem.seekableTimeRanges;
  328. if (loadedRanges.count > 0) {
  329. /// Fix https://github.com/renzifeng/ZFPlayer/issues/475
  330. if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
  331. }
  332. } else if (self.player.currentItem.status == AVPlayerItemStatusFailed) {
  333. self.playState = ZFPlayerPlayStatePlayFailed;
  334. NSError *error = self.player.currentItem.error;
  335. if (self.playerPlayFailed) self.playerPlayFailed(self, error);
  336. }
  337. } else if ([keyPath isEqualToString:kPlaybackBufferEmpty]) {
  338. // When the buffer is empty
  339. if (self.playerItem.playbackBufferEmpty) {
  340. self.loadState = ZFPlayerLoadStateStalled;
  341. [self bufferingSomeSecond];
  342. }
  343. } else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUp]) {
  344. // When the buffer is good
  345. if (self.playerItem.playbackLikelyToKeepUp) {
  346. self.loadState = ZFPlayerLoadStatePlayable;
  347. if (self.isPlaying) [self.player play];
  348. }
  349. } else if ([keyPath isEqualToString:kLoadedTimeRanges]) {
  350. NSTimeInterval bufferTime = [self availableDuration];
  351. self->_bufferTime = bufferTime;
  352. if (self.playerBufferTimeChanged) self.playerBufferTimeChanged(self, bufferTime);
  353. } else if ([keyPath isEqualToString:kPresentationSize]) {
  354. self->_presentationSize = self.playerItem.presentationSize;
  355. if (self.presentationSizeChanged) {
  356. self.presentationSizeChanged(self, self->_presentationSize);
  357. }
  358. } else {
  359. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  360. }
  361. });
  362. }
  363. #pragma mark - getter
  364. - (UIView *)view {
  365. if (!_view) {
  366. _view = [[ZFPlayerPresentView alloc] init];
  367. }
  368. return _view;
  369. }
  370. - (float)rate {
  371. return _rate == 0 ?1:_rate;
  372. }
  373. - (NSTimeInterval)totalTime {
  374. NSTimeInterval sec = CMTimeGetSeconds(self.player.currentItem.duration);
  375. if (isnan(sec)) {
  376. return 0;
  377. }
  378. return sec;
  379. }
  380. - (NSTimeInterval)currentTime {
  381. NSTimeInterval sec = CMTimeGetSeconds(self.playerItem.currentTime);
  382. if (isnan(sec) || sec < 0) {
  383. return 0;
  384. }
  385. return sec;
  386. }
  387. #pragma mark - setter
  388. - (void)setPlayState:(ZFPlayerPlaybackState)playState {
  389. _playState = playState;
  390. if (self.playerPlayStateChanged) self.playerPlayStateChanged(self, playState);
  391. }
  392. - (void)setLoadState:(ZFPlayerLoadState)loadState {
  393. _loadState = loadState;
  394. if (self.playerLoadStateChanged) self.playerLoadStateChanged(self, loadState);
  395. }
  396. - (void)setAssetURL:(NSURL *)assetURL {
  397. if (self.player) [self stop];
  398. //edit by lcy begin
  399. _assetURL = [KTVHTTPCache proxyURLWithOriginalURL:assetURL];//assetURL;
  400. //edit by lcy end
  401. [self prepareToPlay];
  402. }
  403. - (void)setRate:(float)rate {
  404. _rate = rate;
  405. if (self.player && fabsf(_player.rate) > 0.00001f) {
  406. self.player.rate = rate;
  407. }
  408. }
  409. - (void)setMuted:(BOOL)muted {
  410. _muted = muted;
  411. self.player.muted = muted;
  412. }
  413. - (void)setScalingMode:(ZFPlayerScalingMode)scalingMode {
  414. _scalingMode = scalingMode;
  415. ZFPlayerPresentView *presentView = (ZFPlayerPresentView *)self.view;
  416. switch (scalingMode) {
  417. case ZFPlayerScalingModeNone:
  418. presentView.videoGravity = AVLayerVideoGravityResizeAspect;
  419. break;
  420. case ZFPlayerScalingModeAspectFit:
  421. presentView.videoGravity = AVLayerVideoGravityResizeAspect;
  422. break;
  423. case ZFPlayerScalingModeAspectFill:
  424. presentView.videoGravity = AVLayerVideoGravityResizeAspectFill;
  425. break;
  426. case ZFPlayerScalingModeFill:
  427. presentView.videoGravity = AVLayerVideoGravityResize;
  428. break;
  429. default:
  430. break;
  431. }
  432. }
  433. - (void)setVolume:(float)volume {
  434. _volume = MIN(MAX(0, volume), 1);
  435. self.player.volume = volume;
  436. }
  437. @end
  438. #pragma clang diagnostic pop