PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/ioshuanwu/ioshuanwu/Source/TheThird/FMGAVPlayer/FMGVideoPlayView.m

https://gitlab.com/Mr.Tomato/avplayer
Objective C | 485 lines | 331 code | 96 blank | 58 comment | 31 complexity | b56d22c289c284014311c252128f4acd MD5 | raw file
  1. //
  2. // FMGVideoPlayView.m
  3. // 02-远程视频播放(AVPlayer)
  4. //
  5. // Created by apple on 15/8/16.
  6. // Copyright (c) 2015年 xiaomage. All rights reserved.
  7. //
  8. #import "FMGVideoPlayView.h"
  9. #import "FullViewController.h"
  10. #define kRandomColor [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1]
  11. #define font [UIFont systemFontOfSize:15]
  12. @interface FMGVideoPlayView()<CFDanmakuDelegate>
  13. // 播放器的Layer
  14. @property (weak, nonatomic) AVPlayerLayer *playerLayer;
  15. @property (weak, nonatomic) IBOutlet UIImageView *imageView;
  16. @property (weak, nonatomic) IBOutlet UIView *toolView;
  17. @property (weak, nonatomic) IBOutlet UILabel *timeLabel;
  18. // 记录当前是否显示了工具栏
  19. @property (assign, nonatomic) BOOL isShowToolView;
  20. /* 定时器 */
  21. @property (nonatomic, strong) NSTimer *progressTimer;
  22. /* 工具栏的显示和隐藏 */
  23. @property (nonatomic, strong) NSTimer *showTimer;
  24. /* 工具栏展示的时间 */
  25. @property (assign, nonatomic) NSTimeInterval showTime;
  26. @property (nonatomic, strong) NSTimer * timer;
  27. #pragma mark - 监听事件的处理
  28. - (IBAction)playOrPause:(UIButton *)sender;
  29. - (IBAction)switchOrientation:(UIButton *)sender;
  30. - (IBAction)slider;
  31. - (IBAction)startSlider;
  32. - (IBAction)sliderValueChange;
  33. - (IBAction)tapAction:(UITapGestureRecognizer *)sender;
  34. - (IBAction)swipeAction:(UISwipeGestureRecognizer *)sender;
  35. - (IBAction)swipeRight:(UISwipeGestureRecognizer *)sender;
  36. @property (weak, nonatomic) IBOutlet UIImageView *forwardImageView;
  37. @property (weak, nonatomic) IBOutlet UIImageView *backImageView;
  38. @end
  39. @implementation FMGVideoPlayView
  40. // 快速创建View的方法
  41. + (instancetype)videoPlayView
  42. {
  43. // static FMGVideoPlayView *fm = nil;
  44. // static dispatch_once_t once_token;
  45. // if (fm == nil) {
  46. // dispatch_once(&once_token, ^{
  47. // fm = [[[NSBundle mainBundle] loadNibNamed:@"FMGVideoPlayView" owner:nil options:nil] firstObject];
  48. // });
  49. // }
  50. // return fm;
  51. return [[[NSBundle mainBundle] loadNibNamed:@"FMGVideoPlayView" owner:nil options:nil] firstObject];
  52. }
  53. - (AVPlayer *)player
  54. {
  55. if (!_player) {
  56. // 初始化Player和Layer
  57. _player = [[AVPlayer alloc] init];
  58. }
  59. return _player;
  60. }
  61. - (void)awakeFromNib
  62. {
  63. self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
  64. [self.imageView.layer addSublayer:self.playerLayer];
  65. // 设置工具栏的状态
  66. self.toolView.alpha = 0;
  67. self.isShowToolView = NO;
  68. self.forwardImageView.alpha = 0;
  69. self.backImageView.alpha = 0;
  70. // 设置进度条的内容
  71. [self.progressSlider setThumbImage:[UIImage imageNamed:@"thumbImage"] forState:UIControlStateNormal];
  72. [self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage"] forState:UIControlStateNormal];
  73. [self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage"] forState:UIControlStateNormal];
  74. // 设置按钮的状态
  75. self.playOrPauseBtn.selected = NO;
  76. [self showToolView:YES];
  77. [self setupDanmakuView];
  78. [self setupDanmakuData];
  79. }
  80. #pragma mark - 观察者对应的方法
  81. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  82. {
  83. if ([keyPath isEqualToString:@"status"]) {
  84. AVPlayerItemStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
  85. if (AVPlayerItemStatusReadyToPlay == status) {
  86. [self removeProgressTimer];
  87. [self addProgressTimer];
  88. [_danmakuView start];
  89. } else {
  90. [self removeProgressTimer];
  91. }
  92. }
  93. }
  94. - (NSTimeInterval)availableDuration {
  95. NSArray *loadedTimeRanges = [[self.player currentItem] loadedTimeRanges];
  96. CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
  97. float startSeconds = CMTimeGetSeconds(timeRange.start);
  98. float durationSeconds = CMTimeGetSeconds(timeRange.duration);
  99. NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度
  100. return result;
  101. }
  102. #pragma mark - 重新布局
  103. - (void)layoutSubviews
  104. {
  105. [super layoutSubviews];
  106. self.playerLayer.frame = self.bounds;
  107. }
  108. #pragma mark - 设置播放的视频
  109. - (void)setUrlString:(NSString *)urlString
  110. {
  111. _urlString = urlString;
  112. NSURL *url = [NSURL URLWithString:urlString];
  113. if (self.player.currentItem) {
  114. [self.player.currentItem removeObserver:self forKeyPath:@"status"];
  115. }
  116. AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
  117. self.currentItem = item;
  118. [self.player replaceCurrentItemWithPlayerItem:self.currentItem];
  119. [self.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
  120. // 添加视频播放结束通知
  121. [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(moviePlayDidEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_currentItem];
  122. }
  123. - (void)moviePlayDidEnd:(NSNotification *)notification {
  124. __weak typeof(self) weakSelf = self;
  125. [self.player seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
  126. [weakSelf.progressSlider setValue:0.0 animated:YES];
  127. weakSelf.playOrPauseBtn.selected = NO;
  128. }];
  129. }
  130. // 是否显示工具的View
  131. - (IBAction)tapAction:(UITapGestureRecognizer *)sender {
  132. [self showToolView:!self.isShowToolView];
  133. // [self removeShowTimer];
  134. // if (self.isShowToolView) {
  135. // [self showToolView:YES];
  136. // }
  137. }
  138. - (IBAction)swipeAction:(UISwipeGestureRecognizer *)sender {
  139. [self swipeToRight:YES];
  140. }
  141. - (IBAction)swipeRight:(UISwipeGestureRecognizer *)sender {
  142. [self swipeToRight:NO];
  143. }
  144. - (void)swipeToRight:(BOOL)isRight
  145. {
  146. // 1.获取当前播放的时间
  147. NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.currentTime);
  148. if (isRight) {
  149. [UIView animateWithDuration:1 animations:^{
  150. self.forwardImageView.alpha = 1;
  151. } completion:^(BOOL finished) {
  152. self.forwardImageView.alpha = 0;
  153. }];
  154. currentTime += 10;
  155. } else {
  156. [UIView animateWithDuration:1 animations:^{
  157. self.backImageView.alpha = 1;
  158. } completion:^(BOOL finished) {
  159. self.backImageView.alpha = 0;
  160. }];
  161. currentTime -= 10;
  162. }
  163. if (currentTime >= CMTimeGetSeconds(self.player.currentItem.duration)) {
  164. currentTime = CMTimeGetSeconds(self.player.currentItem.duration) - 1;
  165. } else if (currentTime <= 0) {
  166. currentTime = 0;
  167. }
  168. [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  169. [self updateProgressInfo];
  170. }
  171. - (void)showToolView:(BOOL)isShow
  172. {
  173. if (self.progressSlider.tag == 100) {
  174. // [self showToolView:YES];
  175. [self removeShowTimer];
  176. self.progressSlider.tag = 20;
  177. return;
  178. }
  179. [UIView animateWithDuration:1.0 animations:^{
  180. self.toolView.alpha = !self.isShowToolView;
  181. self.isShowToolView = !self.isShowToolView;
  182. }];
  183. }
  184. // 暂停按钮的监听
  185. - (IBAction)playOrPause:(UIButton *)sender {
  186. sender.selected = !sender.selected;
  187. if (sender == nil) {
  188. self.playOrPauseBtn.selected = NO;
  189. }
  190. if (sender.selected) {
  191. [self.player play];
  192. [self addShowTimer];
  193. [self addProgressTimer];
  194. if (_danmakuView.isPrepared) {
  195. if (!_timer) {
  196. _timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(onTimeCount) userInfo:nil repeats:YES];
  197. }
  198. [_danmakuView start];
  199. }
  200. } else {
  201. [self.player pause];
  202. [self removeShowTimer];
  203. [self removeProgressTimer];
  204. if (_timer) {
  205. [_timer invalidate];
  206. _timer = nil;
  207. }
  208. [_danmakuView pause];
  209. }
  210. }
  211. #pragma mark - 定时器操作
  212. - (void)addProgressTimer
  213. {
  214. self.progressTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES];
  215. [[NSRunLoop mainRunLoop] addTimer:self.progressTimer forMode:NSRunLoopCommonModes];
  216. }
  217. - (void)removeProgressTimer
  218. {
  219. [self.progressTimer invalidate];
  220. self.progressTimer = nil;
  221. }
  222. - (void)updateProgressInfo
  223. {
  224. // 1.更新时间
  225. self.timeLabel.text = [self timeString];
  226. self.progressSlider.value = CMTimeGetSeconds(self.player.currentTime) / CMTimeGetSeconds(self.player.currentItem.duration);
  227. if(self.progressSlider.value == 1)
  228. {
  229. self.progressSlider.value = 0;
  230. self.progressSlider.tag = 100;
  231. // [self playOrPause:nil];
  232. // [self sliderValueChange];
  233. self.player = nil;
  234. self.playOrPauseBtn.selected = NO;
  235. self.toolView.alpha = 1;
  236. [self removeProgressTimer];
  237. [self removeShowTimer];
  238. self.timeLabel.text = @"00:00/00:00";
  239. return;
  240. }
  241. }
  242. - (NSString *)timeString
  243. {
  244. NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);
  245. NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentTime);
  246. // if (self.player == nil) {
  247. // return @"00:00/00:00";
  248. // }
  249. return [self stringWithCurrentTime:currentTime duration:duration];
  250. }
  251. - (void)addShowTimer
  252. {
  253. self.showTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateShowTime) userInfo:nil repeats:YES];
  254. [[NSRunLoop mainRunLoop] addTimer:self.showTimer forMode:NSRunLoopCommonModes];
  255. }
  256. - (void)removeShowTimer
  257. {
  258. [self.showTimer invalidate];
  259. self.showTimer = nil;
  260. }
  261. - (void)updateShowTime
  262. {
  263. self.showTime += 1;
  264. if (self.showTime > 2.0) {
  265. [self tapAction:nil];
  266. [self removeShowTimer];
  267. self.showTime = 0;
  268. }
  269. }
  270. #pragma mark - 通过代理方法实现切换屏幕的方向
  271. - (IBAction)switchOrientation:(UIButton *)sender {
  272. sender.selected = !sender.selected;
  273. [_delegate videoplayViewSwitchOrientation:sender.selected];
  274. // [self videoplayViewSwitchOrientation:sender.selected];
  275. }
  276. - (IBAction)slider {
  277. [self addProgressTimer];
  278. NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * self.progressSlider.value;
  279. [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
  280. }
  281. - (IBAction)startSlider {
  282. [self removeProgressTimer];
  283. }
  284. - (IBAction)sliderValueChange {
  285. [self removeProgressTimer];
  286. [self removeShowTimer];
  287. if (self.progressSlider.value == 1) {
  288. self.progressSlider.value = 0;
  289. }
  290. NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * self.progressSlider.value;
  291. NSTimeInterval duration = CMTimeGetSeconds(self.player.currentItem.duration);
  292. self.timeLabel.text = [self stringWithCurrentTime:currentTime duration:duration];
  293. [self addShowTimer];
  294. [self addProgressTimer];
  295. }
  296. - (NSString *)stringWithCurrentTime:(NSTimeInterval)currentTime duration:(NSTimeInterval)duration
  297. {
  298. // if (currentTime == duration) {
  299. // currentTime = 0;
  300. //
  301. //// self.player.currentTime = currentTime;
  302. //// [self updateProgressInfo];
  303. //// [self sliderValueChange];
  304. //// self.progressSlider.value = 0;
  305. // self.playOrPauseBtn.selected = NO;
  306. // self.toolView.alpha = 1;
  307. //
  308. // [self removeProgressTimer];
  309. // [self removeShowTimer];
  310. // self.player = nil;
  311. //
  312. // }
  313. NSInteger dMin = duration / 60;
  314. NSInteger dSec = (NSInteger)duration % 60;
  315. NSInteger cMin = currentTime / 60;
  316. NSInteger cSec = (NSInteger)currentTime % 60;
  317. NSString *durationString = [NSString stringWithFormat:@"%02ld:%02ld", dMin, dSec];
  318. NSString *currentString = [NSString stringWithFormat:@"%02ld:%02ld", cMin, cSec];
  319. return [NSString stringWithFormat:@"%@/%@", currentString, durationString];
  320. }
  321. #pragma mark - 懒加载代码
  322. - (FullViewController *)fullVc
  323. {
  324. if (_fullVc == nil) {
  325. _fullVc = [[FullViewController alloc] init];
  326. }
  327. return _fullVc;
  328. }
  329. #pragma mark - 弹幕
  330. - (void)setupDanmakuView
  331. {
  332. CGRect rect = self.frame;
  333. _danmakuView = [[CFDanmakuView alloc] initWithFrame:rect];
  334. _danmakuView.duration = 6.5;
  335. _danmakuView.centerDuration = 2.5;
  336. _danmakuView.lineHeight = 17;
  337. _danmakuView.maxShowLineCount = 8;
  338. _danmakuView.maxCenterLineCount = 1;
  339. _danmakuView.delegate = self;
  340. [self addSubview:_danmakuView];
  341. }
  342. - (void)setupDanmakuData
  343. {
  344. NSString *danmakufile = [[NSBundle mainBundle] pathForResource:@"danmakufile" ofType:nil];
  345. NSArray *danmakusDicts = [NSArray arrayWithContentsOfFile:danmakufile];
  346. NSMutableArray* danmakus = [NSMutableArray array];
  347. for (NSDictionary* dict in danmakusDicts) {
  348. CFDanmaku* danmaku = [[CFDanmaku alloc] init];
  349. NSMutableAttributedString *contentStr = [[NSMutableAttributedString alloc] initWithString:dict[@"m"] attributes:@{NSFontAttributeName : font, NSForegroundColorAttributeName : kRandomColor}];
  350. NSString* emotionName = [NSString stringWithFormat:@"smile_%zd", arc4random_uniform(90)];
  351. UIImage* emotion = [UIImage imageNamed:emotionName];
  352. NSTextAttachment* attachment = [[NSTextAttachment alloc] init];
  353. attachment.image = emotion;
  354. attachment.bounds = CGRectMake(0, -font.lineHeight*0.3, font.lineHeight*1.5, font.lineHeight*1.5);
  355. NSAttributedString* emotionAttr = [NSAttributedString attributedStringWithAttachment:attachment];
  356. [contentStr appendAttributedString:emotionAttr];
  357. danmaku.contentStr = contentStr;
  358. NSString* attributesStr = dict[@"p"];
  359. NSArray* attarsArray = [attributesStr componentsSeparatedByString:@","];
  360. danmaku.timePoint = [[attarsArray firstObject] doubleValue] / 1000;
  361. danmaku.position = [attarsArray[1] integerValue];
  362. // if (danmaku.position != 0) {
  363. [danmakus addObject:danmaku];
  364. // }
  365. }
  366. [_danmakuView prepareDanmakus:danmakus];
  367. }
  368. - (void)onTimeCount
  369. {
  370. _progressSlider.value+=0.1/120;
  371. if (_progressSlider.value>120.0) {
  372. _progressSlider.value=0;
  373. }
  374. }
  375. - (NSTimeInterval)danmakuViewGetPlayTime:(CFDanmakuView *)danmakuView
  376. {
  377. if(_progressSlider.value == 1.0) [_danmakuView stop]
  378. ;
  379. return _progressSlider.value*120.0;
  380. }
  381. - (BOOL)danmakuViewIsBuffering:(CFDanmakuView *)danmakuView
  382. {
  383. return NO;
  384. }
  385. - (void)dealloc
  386. {
  387. [self.currentItem removeObserver:self forKeyPath:@"status" context:nil];
  388. }
  389. @end