ZFAVPlayerManager.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. #if __has_include(<ZFPlayer/ZFPlayer.h>)
  27. #import <ZFPlayer/ZFKVOController.h>
  28. #import <ZFPlayer/ZFPlayerConst.h>
  29. #import <ZFPlayer/ZFReachabilityManager.h>
  30. #else
  31. #import "ZFKVOController.h"
  32. #import "ZFPlayerConst.h"
  33. #import "ZFReachabilityManager.h"
  34. #endif
  35. #pragma clang diagnostic push
  36. #pragma clang diagnostic ignored"-Wdeprecated-declarations"
  37. /*!
  38. * Refresh interval for timed observations of AVPlayer
  39. */
  40. static NSString *const kStatus = @"status";
  41. static NSString *const kLoadedTimeRanges = @"loadedTimeRanges";
  42. static NSString *const kPlaybackBufferEmpty = @"playbackBufferEmpty";
  43. static NSString *const kPlaybackLikelyToKeepUp = @"playbackLikelyToKeepUp";
  44. static NSString *const kPresentationSize = @"presentationSize";
  45. @interface ZFPlayerPresentView : UIView
  46. @property (nonatomic, strong) AVPlayer *player;
  47. /// default is AVLayerVideoGravityResizeAspect.
  48. @property (nonatomic, strong) AVLayerVideoGravity videoGravity;
  49. @end
  50. @implementation ZFPlayerPresentView
  51. + (Class)layerClass {
  52. return [AVPlayerLayer class];
  53. }
  54. - (AVPlayerLayer *)avLayer {
  55. return (AVPlayerLayer *)self.layer;
  56. }
  57. - (void)setPlayer:(AVPlayer *)player {
  58. if (player == _player) return;
  59. self.avLayer.player = player;
  60. }
  61. - (void)setVideoGravity:(AVLayerVideoGravity)videoGravity {
  62. if (videoGravity == self.videoGravity) return;
  63. [self avLayer].videoGravity = videoGravity;
  64. }
  65. - (AVLayerVideoGravity)videoGravity {
  66. return [self avLayer].videoGravity;
  67. }
  68. @end
  69. @interface ZFAVPlayerManager () {
  70. id _timeObserver;
  71. id _itemEndObserver;
  72. ZFKVOController *_playerItemKVO;
  73. }
  74. @property (nonatomic, strong) AVPlayerLayer *playerLayer;
  75. @property (nonatomic, assign) BOOL isBuffering;
  76. @property (nonatomic, assign) BOOL isReadyToPlay;
  77. @property (nonatomic, strong) AVAssetImageGenerator *imageGenerator;
  78. @end
  79. @implementation ZFAVPlayerManager
  80. @synthesize view = _view;
  81. @synthesize currentTime = _currentTime;
  82. @synthesize totalTime = _totalTime;
  83. @synthesize playerPlayTimeChanged = _playerPlayTimeChanged;
  84. @synthesize playerBufferTimeChanged = _playerBufferTimeChanged;
  85. @synthesize playerDidToEnd = _playerDidToEnd;
  86. @synthesize bufferTime = _bufferTime;
  87. @synthesize playState = _playState;
  88. @synthesize loadState = _loadState;
  89. @synthesize assetURL = _assetURL;
  90. @synthesize playerPrepareToPlay = _playerPrepareToPlay;
  91. @synthesize playerReadyToPlay = _playerReadyToPlay;
  92. @synthesize playerPlayStateChanged = _playerPlayStateChanged;
  93. @synthesize playerLoadStateChanged = _playerLoadStateChanged;
  94. @synthesize seekTime = _seekTime;
  95. @synthesize muted = _muted;
  96. @synthesize volume = _volume;
  97. @synthesize presentationSize = _presentationSize;
  98. @synthesize isPlaying = _isPlaying;
  99. @synthesize rate = _rate;
  100. @synthesize isPreparedToPlay = _isPreparedToPlay;
  101. @synthesize shouldAutoPlay = _shouldAutoPlay;
  102. @synthesize scalingMode = _scalingMode;
  103. @synthesize playerPlayFailed = _playerPlayFailed;
  104. @synthesize presentationSizeChanged = _presentationSizeChanged;
  105. - (instancetype)init {
  106. self = [super init];
  107. if (self) {
  108. _scalingMode = ZFPlayerScalingModeAspectFit;
  109. _shouldAutoPlay = YES;
  110. }
  111. return self;
  112. }
  113. - (void)prepareToPlay {
  114. if (!_assetURL) return;
  115. _isPreparedToPlay = YES;
  116. [self initializePlayer];
  117. if (self.shouldAutoPlay) {
  118. [self play];
  119. }
  120. self.loadState = ZFPlayerLoadStatePrepare;
  121. if (self.playerPrepareToPlay) self.playerPrepareToPlay(self, self.assetURL);
  122. }
  123. - (void)reloadPlayer {
  124. self.seekTime = self.currentTime;
  125. [self prepareToPlay];
  126. }
  127. - (void)play {
  128. if (!_isPreparedToPlay) {
  129. [self prepareToPlay];
  130. } else {
  131. [self.player play];
  132. self.player.rate = self.rate;
  133. self->_isPlaying = YES;
  134. self.playState = ZFPlayerPlayStatePlaying;
  135. }
  136. }
  137. - (void)pause {
  138. [self.player pause];
  139. self->_isPlaying = NO;
  140. self.playState = ZFPlayerPlayStatePaused;
  141. [_playerItem cancelPendingSeeks];
  142. [_asset cancelLoading];
  143. }
  144. - (void)stop {
  145. [_playerItemKVO safelyRemoveAllObservers];
  146. self.loadState = ZFPlayerLoadStateUnknown;
  147. self.playState = ZFPlayerPlayStatePlayStopped;
  148. if (self.player.rate != 0) [self.player pause];
  149. [_playerItem cancelPendingSeeks];
  150. [_asset cancelLoading];
  151. [self.player removeTimeObserver:_timeObserver];
  152. [self.player replaceCurrentItemWithPlayerItem:nil];
  153. self.presentationSize = CGSizeZero;
  154. _timeObserver = nil;
  155. [[NSNotificationCenter defaultCenter] removeObserver:_itemEndObserver name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];
  156. _itemEndObserver = nil;
  157. _isPlaying = NO;
  158. _player = nil;
  159. _assetURL = nil;
  160. _playerItem = nil;
  161. _isPreparedToPlay = NO;
  162. self->_currentTime = 0;
  163. self->_totalTime = 0;
  164. self->_bufferTime = 0;
  165. self.isReadyToPlay = NO;
  166. }
  167. - (void)replay {
  168. @zf_weakify(self)
  169. [self seekToTime:0 completionHandler:^(BOOL finished) {
  170. @zf_strongify(self)
  171. if (finished) {
  172. [self play];
  173. }
  174. }];
  175. }
  176. - (void)seekToTime:(NSTimeInterval)time completionHandler:(void (^ __nullable)(BOOL finished))completionHandler {
  177. if (self.totalTime > 0) {
  178. [_player.currentItem cancelPendingSeeks];
  179. int32_t timeScale = _player.currentItem.asset.duration.timescale;
  180. CMTime seekTime = CMTimeMakeWithSeconds(time, timeScale);
  181. [_player seekToTime:seekTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:completionHandler];
  182. } else {
  183. self.seekTime = time;
  184. }
  185. }
  186. - (UIImage *)thumbnailImageAtCurrentTime {
  187. CMTime expectedTime = self.playerItem.currentTime;
  188. CGImageRef cgImage = NULL;
  189. self.imageGenerator.requestedTimeToleranceBefore = kCMTimeZero;
  190. self.imageGenerator.requestedTimeToleranceAfter = kCMTimeZero;
  191. cgImage = [self.imageGenerator copyCGImageAtTime:expectedTime actualTime:NULL error:NULL];
  192. if (!cgImage) {
  193. self.imageGenerator.requestedTimeToleranceBefore = kCMTimePositiveInfinity;
  194. self.imageGenerator.requestedTimeToleranceAfter = kCMTimePositiveInfinity;
  195. cgImage = [self.imageGenerator copyCGImageAtTime:expectedTime actualTime:NULL error:NULL];
  196. }
  197. UIImage *image = [UIImage imageWithCGImage:cgImage];
  198. return image;
  199. }
  200. - (void)thumbnailImageAtCurrentTime:(void(^)(UIImage *))handler {
  201. CMTime expectedTime = self.playerItem.currentTime;
  202. [self.imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:expectedTime]] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
  203. if (handler) {
  204. UIImage *finalImage = [UIImage imageWithCGImage:image];
  205. handler(finalImage);
  206. }
  207. }];
  208. }
  209. #pragma mark - private method
  210. /// Calculate buffer progress
  211. - (NSTimeInterval)availableDuration {
  212. NSArray *timeRangeArray = _playerItem.loadedTimeRanges;
  213. CMTime currentTime = [_player currentTime];
  214. BOOL foundRange = NO;
  215. CMTimeRange aTimeRange = {0};
  216. if (timeRangeArray.count) {
  217. aTimeRange = [[timeRangeArray objectAtIndex:0] CMTimeRangeValue];
  218. if (CMTimeRangeContainsTime(aTimeRange, currentTime)) {
  219. foundRange = YES;
  220. }
  221. }
  222. if (foundRange) {
  223. CMTime maxTime = CMTimeRangeGetEnd(aTimeRange);
  224. NSTimeInterval playableDuration = CMTimeGetSeconds(maxTime);
  225. if (playableDuration > 0) {
  226. return playableDuration;
  227. }
  228. }
  229. return 0;
  230. }
  231. - (void)initializePlayer {
  232. _asset = [AVURLAsset URLAssetWithURL:self.assetURL options:self.requestHeader];
  233. _playerItem = [AVPlayerItem playerItemWithAsset:_asset];
  234. _player = [AVPlayer playerWithPlayerItem:_playerItem];
  235. _imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:_asset];
  236. [self enableAudioTracks:YES inPlayerItem:_playerItem];
  237. ZFPlayerPresentView *presentView = [[ZFPlayerPresentView alloc] init];
  238. presentView.player = _player;
  239. self.view.playerView = presentView;
  240. self.scalingMode = _scalingMode;
  241. if (@available(iOS 9.0, *)) {
  242. _playerItem.canUseNetworkResourcesForLiveStreamingWhilePaused = NO;
  243. }
  244. if (@available(iOS 10.0, *)) {
  245. _playerItem.preferredForwardBufferDuration = 5;
  246. /// 关闭AVPlayer默认的缓冲延迟播放策略,提高首屏播放速度
  247. _player.automaticallyWaitsToMinimizeStalling = NO;
  248. }
  249. [self itemObserving];
  250. }
  251. /// Playback speed switching method
  252. - (void)enableAudioTracks:(BOOL)enable inPlayerItem:(AVPlayerItem*)playerItem {
  253. for (AVPlayerItemTrack *track in playerItem.tracks){
  254. if ([track.assetTrack.mediaType isEqual:AVMediaTypeVideo]) {
  255. track.enabled = enable;
  256. }
  257. }
  258. }
  259. /**
  260. * 缓冲较差时候回调这里
  261. */
  262. - (void)bufferingSomeSecond {
  263. // playbackBufferEmpty会反复进入,因此在bufferingOneSecond延时播放执行完之前再调用bufferingSomeSecond都忽略
  264. if (self.isBuffering || self.playState == ZFPlayerPlayStatePlayStopped) return;
  265. /// 没有网络
  266. if ([ZFReachabilityManager sharedManager].networkReachabilityStatus == ZFReachabilityStatusNotReachable) return;
  267. self.isBuffering = YES;
  268. // 需要先暂停一小会之后再播放,否则网络状况不好的时候时间在走,声音播放不出来
  269. [self pause];
  270. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  271. // 如果此时用户已经暂停了,则不再需要开启播放了
  272. if (!self.isPlaying && self.loadState == ZFPlayerLoadStateStalled) {
  273. self.isBuffering = NO;
  274. return;
  275. }
  276. [self play];
  277. // 如果执行了play还是没有播放则说明还没有缓存好,则再次缓存一段时间
  278. self.isBuffering = NO;
  279. if (!self.playerItem.isPlaybackLikelyToKeepUp) [self bufferingSomeSecond];
  280. });
  281. }
  282. - (void)itemObserving {
  283. [_playerItemKVO safelyRemoveAllObservers];
  284. _playerItemKVO = [[ZFKVOController alloc] initWithTarget:_playerItem];
  285. [_playerItemKVO safelyAddObserver:self
  286. forKeyPath:kStatus
  287. options:NSKeyValueObservingOptionNew
  288. context:nil];
  289. [_playerItemKVO safelyAddObserver:self
  290. forKeyPath:kPlaybackBufferEmpty
  291. options:NSKeyValueObservingOptionNew
  292. context:nil];
  293. [_playerItemKVO safelyAddObserver:self
  294. forKeyPath:kPlaybackLikelyToKeepUp
  295. options:NSKeyValueObservingOptionNew
  296. context:nil];
  297. [_playerItemKVO safelyAddObserver:self
  298. forKeyPath:kLoadedTimeRanges
  299. options:NSKeyValueObservingOptionNew
  300. context:nil];
  301. [_playerItemKVO safelyAddObserver:self
  302. forKeyPath:kPresentationSize
  303. options:NSKeyValueObservingOptionNew
  304. context:nil];
  305. CMTime interval = CMTimeMakeWithSeconds(self.timeRefreshInterval > 0 ? self.timeRefreshInterval : 0.1, NSEC_PER_SEC);
  306. @zf_weakify(self)
  307. _timeObserver = [self.player addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
  308. @zf_strongify(self)
  309. if (!self) return;
  310. NSArray *loadedRanges = self.playerItem.seekableTimeRanges;
  311. if (self.isPlaying && self.loadState == ZFPlayerLoadStateStalled) self.player.rate = self.rate;
  312. if (loadedRanges.count > 0) {
  313. if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
  314. }
  315. }];
  316. _itemEndObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
  317. @zf_strongify(self)
  318. if (!self) return;
  319. self.playState = ZFPlayerPlayStatePlayStopped;
  320. if (self.playerDidToEnd) self.playerDidToEnd(self);
  321. }];
  322. }
  323. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  324. dispatch_async(dispatch_get_main_queue(), ^{
  325. if ([keyPath isEqualToString:kStatus]) {
  326. if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
  327. if (!self.isReadyToPlay) {
  328. self.isReadyToPlay = YES;
  329. self.loadState = ZFPlayerLoadStatePlaythroughOK;
  330. if (self.playerReadyToPlay) self.playerReadyToPlay(self, self.assetURL);
  331. }
  332. if (self.seekTime) {
  333. if (self.shouldAutoPlay) [self pause];
  334. @zf_weakify(self)
  335. [self seekToTime:self.seekTime completionHandler:^(BOOL finished) {
  336. @zf_strongify(self)
  337. if (finished) {
  338. if (self.shouldAutoPlay) [self play];
  339. }
  340. }];
  341. self.seekTime = 0;
  342. } else {
  343. if (self.shouldAutoPlay && self.isPlaying) [self play];
  344. }
  345. self.player.muted = self.muted;
  346. NSArray *loadedRanges = self.playerItem.seekableTimeRanges;
  347. if (loadedRanges.count > 0) {
  348. if (self.playerPlayTimeChanged) self.playerPlayTimeChanged(self, self.currentTime, self.totalTime);
  349. }
  350. } else if (self.player.currentItem.status == AVPlayerItemStatusFailed) {
  351. self.playState = ZFPlayerPlayStatePlayFailed;
  352. self->_isPlaying = NO;
  353. NSError *error = self.player.currentItem.error;
  354. NSLog(@"%@",error);
  355. if (self.playerPlayFailed) self.playerPlayFailed(self, error);
  356. }
  357. } else if ([keyPath isEqualToString:kPlaybackBufferEmpty]) {
  358. // When the buffer is empty
  359. if (self.playerItem.playbackBufferEmpty) {
  360. self.loadState = ZFPlayerLoadStateStalled;
  361. [self bufferingSomeSecond];
  362. }
  363. } else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUp]) {
  364. // When the buffer is good
  365. if (self.playerItem.playbackLikelyToKeepUp) {
  366. self.loadState = ZFPlayerLoadStatePlayable;
  367. if (self.isPlaying) [self.player play];
  368. }
  369. } else if ([keyPath isEqualToString:kLoadedTimeRanges]) {
  370. NSTimeInterval bufferTime = [self availableDuration];
  371. self->_bufferTime = bufferTime;
  372. if (self.playerBufferTimeChanged) self.playerBufferTimeChanged(self, bufferTime);
  373. } else if ([keyPath isEqualToString:kPresentationSize]) {
  374. self.presentationSize = self.playerItem.presentationSize;
  375. } else {
  376. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  377. }
  378. });
  379. }
  380. #pragma mark - getter
  381. - (ZFPlayerView *)view {
  382. if (!_view) {
  383. ZFPlayerView *view = [[ZFPlayerView alloc] init];
  384. _view = view;
  385. }
  386. return _view;
  387. }
  388. - (AVPlayerLayer *)avPlayerLayer {
  389. ZFPlayerPresentView *view = (ZFPlayerPresentView *)self.view.playerView;
  390. return [view avLayer];
  391. }
  392. - (float)rate {
  393. return _rate == 0 ?1:_rate;
  394. }
  395. - (NSTimeInterval)totalTime {
  396. NSTimeInterval sec = CMTimeGetSeconds(self.player.currentItem.duration);
  397. if (isnan(sec)) {
  398. return 0;
  399. }
  400. return sec;
  401. }
  402. - (NSTimeInterval)currentTime {
  403. NSTimeInterval sec = CMTimeGetSeconds(self.playerItem.currentTime);
  404. if (isnan(sec) || sec < 0) {
  405. return 0;
  406. }
  407. return sec;
  408. }
  409. #pragma mark - setter
  410. - (void)setPlayState:(ZFPlayerPlaybackState)playState {
  411. _playState = playState;
  412. if (self.playerPlayStateChanged) self.playerPlayStateChanged(self, playState);
  413. }
  414. - (void)setLoadState:(ZFPlayerLoadState)loadState {
  415. _loadState = loadState;
  416. if (self.playerLoadStateChanged) self.playerLoadStateChanged(self, loadState);
  417. }
  418. - (void)setAssetURL:(NSURL *)assetURL {
  419. if (self.player) [self stop];
  420. _assetURL = assetURL;
  421. [self prepareToPlay];
  422. }
  423. - (void)setRate:(float)rate {
  424. _rate = rate;
  425. if (self.player && fabsf(_player.rate) > 0.00001f) {
  426. self.player.rate = rate;
  427. }
  428. }
  429. - (void)setMuted:(BOOL)muted {
  430. _muted = muted;
  431. self.player.muted = muted;
  432. }
  433. - (void)setScalingMode:(ZFPlayerScalingMode)scalingMode {
  434. _scalingMode = scalingMode;
  435. ZFPlayerPresentView *presentView = (ZFPlayerPresentView *)self.view.playerView;
  436. self.view.scalingMode = scalingMode;
  437. switch (scalingMode) {
  438. case ZFPlayerScalingModeNone:
  439. presentView.videoGravity = AVLayerVideoGravityResizeAspect;
  440. break;
  441. case ZFPlayerScalingModeAspectFit:
  442. presentView.videoGravity = AVLayerVideoGravityResizeAspect;
  443. break;
  444. case ZFPlayerScalingModeAspectFill:
  445. presentView.videoGravity = AVLayerVideoGravityResizeAspectFill;
  446. break;
  447. case ZFPlayerScalingModeFill:
  448. presentView.videoGravity = AVLayerVideoGravityResize;
  449. break;
  450. default:
  451. break;
  452. }
  453. }
  454. - (void)setVolume:(float)volume {
  455. _volume = MIN(MAX(0, volume), 1);
  456. self.player.volume = _volume;
  457. }
  458. - (void)setPresentationSize:(CGSize)presentationSize {
  459. _presentationSize = presentationSize;
  460. self.view.presentationSize = presentationSize;
  461. if (self.presentationSizeChanged) {
  462. self.presentationSizeChanged(self, self.presentationSize);
  463. }
  464. }
  465. @end
  466. #pragma clang diagnostic pop