TCVodPlayViewController.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. //
  2. // TCVodPlayViewController.m
  3. // TCLVBIMDemo
  4. //
  5. // Created by annidyfeng on 2017/9/15.
  6. // Copyright © 2017年 tencent. All rights reserved.
  7. //
  8. #import "TCVodPlayViewController.h"
  9. #import "TCVideoPublishController.h"
  10. #import "TCVideoRecordViewController.h"
  11. #import <mach/mach.h>
  12. #import <UIImageView+WebCache.h>
  13. #import "BGBaseAppDelegate.h"
  14. #import "TCConstants.h"
  15. #import <Accelerate/Accelerate.h>
  16. #import <UShareUI/UMSocialUIManager.h>
  17. //#import "TCLoginModel.h"
  18. #import "NSString+Common.h"
  19. #import "TCPlayViewCell.h"
  20. #import "TCUserInfoModel.h"
  21. #import <TXLiteAVSDK_Professional/TXVodPlayer.h>
  22. #define RTMP_URL ASLocalizedString(@"请输入或扫二维码获取播放地址")
  23. #define CACHE_PLAYER 3
  24. #define PLAY_CLICK @"PLAY_CLICK" //当前播放器启动播放
  25. #define PLAY_PREPARE @"PLAY_PREPARE" //当前播放器收到 PLAY_PREPARE 事件
  26. #define PLAY_REVIEW @"PLAY_REVIEW" //当前视频的审核状态,只有审核通过才能播放
  27. typedef NS_ENUM(NSInteger,DragDirection){
  28. DragDirection_Down,
  29. DragDirection_Up,
  30. };
  31. @interface TCVodPlayViewController ()
  32. @end
  33. @implementation TCVodPlayViewController
  34. {
  35. TXLivePlayConfig* _config;
  36. long long _trackingTouchTS;
  37. BOOL _startSeek;
  38. BOOL _videoPause;
  39. BOOL _videoFinished;
  40. BOOL _appIsInterrupt;
  41. float _sliderValue;
  42. BOOL _isInVC;
  43. NSString *_logMsg;
  44. NSString *_rtmpUrl;
  45. UIView *_videoParentView;
  46. BOOL _isErrorAlert; //是否已经弹出了错误提示框,用于保证在同时收到多个错误通知时,只弹一个错误提示框
  47. BOOL _statusBarHidden;
  48. BOOL _navigationBarHidden;
  49. BOOL _beginDragging;
  50. UITableView* _tableView;
  51. NSArray* _liveInfos;
  52. NSMutableArray* _playerList;
  53. NSInteger _liveInfoIndex;
  54. TCPlayViewCell * _currentCell;
  55. TXVodPlayer * _currentPlayer;
  56. DragDirection _dragDirection;
  57. MBProgressHUD* _hub;
  58. }
  59. -(id)initWithPlayInfoS:(NSArray<TCLiveInfo *>*) liveInfos liveInfo:(TCLiveInfo *)liveInfo videoIsReady:(videoIsReadyBlock)videoIsReady;
  60. {
  61. self = [super initWithPlayInfo:liveInfo videoIsReady:videoIsReady];
  62. if (self) {
  63. _videoPause = NO;
  64. _videoFinished = YES;
  65. _isInVC = NO;
  66. _log_switch = YES;
  67. _liveInfos = liveInfos;
  68. _liveInfoIndex = [liveInfos indexOfObject:liveInfo];
  69. _playerList = [NSMutableArray array];
  70. _isErrorAlert = NO;
  71. _dragDirection = DragDirection_Down;
  72. [self initPlayer];
  73. [self addNotify];
  74. }
  75. return self;
  76. }
  77. - (void)addNotify{
  78. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];
  79. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackGround:) name:UIApplicationDidEnterBackgroundNotification object:nil];
  80. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
  81. }
  82. -(void)viewDidLoad{
  83. [super viewDidLoad];
  84. self.view.backgroundColor = [UIColor blackColor];
  85. // [self joinGroup];
  86. /*预加载UI 背景图*/
  87. UIImage *backImage = self.liveInfo.userinfo.frontcoverImage;
  88. UIImage *clipImage = nil;
  89. if (backImage) {
  90. CGFloat backImageNewHeight = self.view.height;
  91. CGFloat backImageNewWidth = backImageNewHeight * backImage.size.width / backImage.size.height;
  92. UIImage *gsImage = [self gsImage:backImage withGsNumber:10];
  93. UIImage *scaleImage = [self scaleImage:gsImage scaleToSize:CGSizeMake(backImageNewWidth, backImageNewHeight)];
  94. clipImage = [self clipImage:scaleImage inRect:CGRectMake((backImageNewWidth - self.view.width)/2, (backImageNewHeight - self.view.height)/2, self.view.width, self.view.height)];
  95. }
  96. UIImageView *backgroundImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
  97. backgroundImageView.image = clipImage;
  98. backgroundImageView.contentMode = UIViewContentModeScaleToFill;
  99. backgroundImageView.backgroundColor = [UIColor blackColor];
  100. [self.view addSubview:backgroundImageView];
  101. //视频画面父view
  102. _videoParentView = [[UIView alloc] initWithFrame:self.view.bounds];
  103. // _videoParentView.tag = FULL_SCREEN_PLAY_VIDEO_VIEW;
  104. // [self.view addSubview:_videoParentView];
  105. const CGFloat rowHeight = SCREEN_HEIGHT;
  106. _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
  107. _tableView.delegate = self;
  108. _tableView.dataSource = self;
  109. _tableView.pagingEnabled = YES;
  110. _tableView.showsVerticalScrollIndicator = NO;
  111. _tableView.showsHorizontalScrollIndicator = NO;
  112. _tableView.estimatedRowHeight = 0;
  113. _tableView.estimatedSectionFooterHeight = 0;
  114. _tableView.estimatedSectionHeaderHeight = 0;
  115. _tableView.rowHeight = rowHeight;// - statusBarHeight;
  116. if (@available(iOS 11, *)) {
  117. _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
  118. }
  119. [self.view addSubview:_tableView];
  120. [_tableView reloadData];
  121. [_tableView setContentOffset:CGPointMake(0, rowHeight * _liveInfoIndex) animated:NO];
  122. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_liveInfoIndex inSection:0];
  123. // [_tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
  124. _currentCell = [_tableView cellForRowAtIndexPath:indexPath];
  125. [self resumePlayer];
  126. }
  127. -(void)viewDidAppear:(BOOL)animated{
  128. [super viewDidAppear:animated];
  129. _isInVC = YES;
  130. }
  131. -(void)viewWillAppear:(BOOL)animated{
  132. [super viewWillAppear:animated];
  133. _navigationBarHidden = self.navigationController.navigationBar.hidden;
  134. [self.navigationController setNavigationBarHidden:YES];
  135. _statusBarHidden = [UIApplication sharedApplication].statusBarHidden;
  136. [[UIApplication sharedApplication]setStatusBarHidden:YES];
  137. if (_videoPause && _currentPlayer) {
  138. //这里如果是从录制界面,或则其他播放界面过来的,要重新startPlay,因为AudioSession有可能被修改了,导致当前视频播放有异常
  139. NSMutableDictionary *param = [self getPlayerParam:_currentPlayer];
  140. [_currentPlayer startVodPlay:param[@"playUrl"]];
  141. // [_currentPlayer startPlay:param[@"playUrl"]];
  142. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
  143. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  144. _videoPause = NO;
  145. }
  146. }
  147. -(void)viewWillDisappear:(BOOL)animated{
  148. [super viewWillDisappear:animated];
  149. [self.navigationController setNavigationBarHidden:_navigationBarHidden];
  150. [[UIApplication sharedApplication]setStatusBarHidden:_statusBarHidden];
  151. if (!_videoPause && _currentPlayer) {
  152. [self clickPlayVod];
  153. }
  154. }
  155. - (void)didReceiveMemoryWarning {
  156. [super didReceiveMemoryWarning];
  157. // Dispose of any resources that can be recreated.
  158. }
  159. //在低系统(如7.1.2)可能收不到这个回调,请在onAppDidEnterBackGround和onAppWillEnterForeground里面处理打断逻辑
  160. - (void) onAudioSessionEvent: (NSNotification *) notification
  161. {
  162. NSDictionary *info = notification.userInfo;
  163. AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
  164. if (type == AVAudioSessionInterruptionTypeBegan) {
  165. if (_appIsInterrupt == NO) {
  166. if (!_videoPause) {
  167. [_currentPlayer pause];
  168. }
  169. _appIsInterrupt = YES;
  170. }
  171. }else{
  172. AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
  173. if (options == AVAudioSessionInterruptionOptionShouldResume) {
  174. if (_appIsInterrupt == YES) {
  175. if (!_videoPause) {
  176. [_currentPlayer resume];
  177. }
  178. _appIsInterrupt = NO;
  179. }
  180. }
  181. }
  182. }
  183. - (void)onAppDidEnterBackGround:(UIApplication*)app {
  184. if (_appIsInterrupt == NO) {
  185. if (!_videoPause) {
  186. [_currentPlayer pause];
  187. }
  188. _appIsInterrupt = YES;
  189. }
  190. }
  191. - (void)onAppWillEnterForeground:(UIApplication*)app {
  192. if (_appIsInterrupt == YES) {
  193. if (!_videoPause) {
  194. [_currentPlayer resume];
  195. }
  196. _appIsInterrupt = NO;
  197. }
  198. }
  199. - (void)initPlayer{
  200. int playerCount = 0;
  201. int liveIndex = (int)_liveInfoIndex;
  202. int liveIndexOffset = - CACHE_PLAYER / 2;
  203. if (_liveInfoIndex <= CACHE_PLAYER / 2) {
  204. liveIndex = 0;
  205. liveIndexOffset = 0;
  206. }
  207. if (_liveInfoIndex >= _liveInfos.count - CACHE_PLAYER / 2 - 1) {
  208. liveIndex = (int)_liveInfos.count - CACHE_PLAYER;
  209. liveIndexOffset = 0;
  210. }
  211. while (playerCount < CACHE_PLAYER) {
  212. TXVodPlayer *player = [[TXVodPlayer alloc] init];
  213. player.isAutoPlay = NO;
  214. TCLiveInfo *info = _liveInfos[liveIndex + liveIndexOffset];
  215. NSString *playUrl = [self checkHttps:info.playurl];
  216. NSMutableDictionary *param = [NSMutableDictionary dictionary];
  217. [param setObject:player forKey:@"player"];
  218. [param setObject:playUrl forKey:@"playUrl"];
  219. [param setObject:@(NO) forKey:PLAY_CLICK];
  220. [param setObject:@(NO) forKey:PLAY_PREPARE];
  221. [param setObject:@(info.reviewStatus) forKey:PLAY_REVIEW];
  222. [_playerList addObject:param];
  223. playerCount ++;
  224. liveIndexOffset ++;
  225. }
  226. }
  227. - (void)resetPlayer{
  228. int liveIndexOffset = - CACHE_PLAYER / 2;
  229. for(NSMutableDictionary *playerParam in _playerList){
  230. //先停掉所有的播放器
  231. TXVodPlayer *player = playerParam[@"player"];
  232. if ([playerParam[PLAY_REVIEW] intValue] == ReviewStatus_Normal) {
  233. [player stopPlay];
  234. [player removeVideoWidget];
  235. }
  236. //播放器重新对应 -> playeUrl
  237. if (_liveInfoIndex + liveIndexOffset >= 0 && _liveInfoIndex + liveIndexOffset < _liveInfos.count) {
  238. TCLiveInfo *info = _liveInfos[_liveInfoIndex + liveIndexOffset];
  239. NSString *playUrl = [self checkHttps:info.playurl];
  240. [playerParam setObject:playUrl forKey:@"playUrl"];
  241. [playerParam setObject:@(NO) forKey:PLAY_CLICK];
  242. [playerParam setObject:@(NO) forKey:PLAY_PREPARE];
  243. [playerParam setObject:@(info.reviewStatus) forKey:PLAY_REVIEW];
  244. }
  245. liveIndexOffset ++;
  246. }
  247. }
  248. - (void)loadNextPlayer{
  249. //找到下一个player预加载
  250. int index = (int)[_playerList indexOfObject:[self getPlayerParam:_currentPlayer]];
  251. switch (_dragDirection) {
  252. case DragDirection_Down:
  253. {
  254. //向下拖动,预加载下一个播放器
  255. if (index < _playerList.count - 1) {
  256. NSMutableDictionary *param = _playerList[index + 1];
  257. if (![param[PLAY_CLICK] boolValue]) {
  258. [self startPlay:param];
  259. }
  260. }
  261. }
  262. break;
  263. case DragDirection_Up:
  264. {
  265. //向上拖动,预加载上一个播放器
  266. if (index > 0) {
  267. NSMutableDictionary *param = _playerList[index - 1];
  268. if (![param[PLAY_CLICK] boolValue]) {
  269. [self startPlay:param];
  270. }
  271. }
  272. }
  273. break;
  274. default:
  275. break;
  276. }
  277. }
  278. - (void)resumePlayer{
  279. //先暂停上一个播放器
  280. if (_currentPlayer) {
  281. [_currentPlayer seek:0];
  282. [_currentPlayer pause];
  283. }
  284. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
  285. //开启下一个播放器
  286. BOOL findPlayer = NO;
  287. for (int i = 0; i < _playerList.count; i ++) {
  288. NSMutableDictionary *playParam = _playerList[i];
  289. NSString *playUrl = [playParam objectForKey:@"playUrl"];
  290. if ([playUrl isEqualToString:[self playUrl]]) {
  291. findPlayer = YES;
  292. _currentPlayer = (TXVodPlayer *)[playParam objectForKey:@"player"];
  293. [_currentPlayer setupVideoWidget:_currentCell.videoParentView insertIndex:0];
  294. // [_currentPlayer setRenderRotation:HOME_ORIENTATION_DOWN];
  295. //判断播放器是否启动播放,如果没有,先启动播放
  296. if (![playParam[PLAY_CLICK] boolValue]) {
  297. [self startPlay:playParam];
  298. }
  299. //判断播放器是否收到 PLAY_PREPARE 事件,如果收到,直接resume播放,如果没收到,在播放回调里面resume播放
  300. if ([playParam[PLAY_PREPARE] boolValue]) {
  301. [_currentPlayer resume];
  302. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
  303. }
  304. //边界检查,防止越界
  305. if (_liveInfoIndex < CACHE_PLAYER / 2 || _liveInfoIndex > _liveInfos.count - CACHE_PLAYER / 2 - 1) {
  306. break;
  307. }
  308. //缓存播放器切换
  309. if (i > CACHE_PLAYER / 2) {
  310. int needMove = i - CACHE_PLAYER / 2;
  311. for (int j = 0; j < needMove; j ++) {
  312. NSMutableDictionary *oldParam = _playerList[j];
  313. TXVodPlayer *player = [oldParam objectForKey:@"player"];
  314. if ([oldParam[PLAY_REVIEW] intValue] == ReviewStatus_Normal) {
  315. [player stopPlay];
  316. [player removeVideoWidget];
  317. }
  318. TCLiveInfo *liveInfo = _liveInfos[_liveInfoIndex + 1 + j];
  319. NSString *playUrl = [self checkHttps:liveInfo.playurl];
  320. NSMutableDictionary *newParam = [NSMutableDictionary dictionary];
  321. [newParam setObject:player forKey:@"player"];
  322. [newParam setObject:playUrl forKey:@"playUrl"];
  323. [newParam setObject:@(NO) forKey:PLAY_CLICK];
  324. [newParam setObject:@(NO) forKey:PLAY_PREPARE];
  325. [newParam setObject:@(liveInfo.reviewStatus) forKey:PLAY_REVIEW];
  326. [_playerList removeObject:oldParam];
  327. [_playerList addObject:newParam];
  328. }
  329. }
  330. if (i < CACHE_PLAYER / 2){
  331. int needMove = CACHE_PLAYER / 2 - i;
  332. for (int j = 0; j < needMove; j ++) {
  333. NSMutableDictionary *oldParam = _playerList[CACHE_PLAYER - 1 - j];
  334. TXVodPlayer *player = [oldParam objectForKey:@"player"];
  335. if ([oldParam[PLAY_REVIEW] intValue] == ReviewStatus_Normal) {
  336. [player stopPlay];
  337. [player removeVideoWidget];
  338. }
  339. TCLiveInfo *liveInfo = _liveInfos[_liveInfoIndex - 1 - j];
  340. NSString *playUrl = [self checkHttps:liveInfo.playurl];
  341. NSMutableDictionary *newParam = [NSMutableDictionary dictionary];
  342. [newParam setObject:player forKey:@"player"];
  343. [newParam setObject:playUrl forKey:@"playUrl"];
  344. [newParam setObject:@(NO) forKey:PLAY_CLICK];
  345. [newParam setObject:@(NO) forKey:PLAY_PREPARE];
  346. [newParam setObject:@(liveInfo.reviewStatus) forKey:PLAY_REVIEW];
  347. [_playerList removeObject:oldParam];
  348. [_playerList insertObject:newParam atIndex:0];
  349. }
  350. }
  351. //这里注意break,防止逻辑错误
  352. break;
  353. }
  354. }
  355. if (!findPlayer) {
  356. //重新对应 player <-> playUrl
  357. [self resetPlayer];
  358. //启动当前播放器
  359. NSMutableDictionary *playerParam = _playerList[CACHE_PLAYER / 2];
  360. _currentPlayer = playerParam[@"player"];
  361. [_currentPlayer setupVideoWidget:_currentCell.videoParentView insertIndex:0];
  362. [_currentPlayer setRenderRotation:HOME_ORIENTATION_DOWN];
  363. [self startPlay:playerParam];
  364. }
  365. //预加载下一个播放器
  366. [self loadNextPlayer];
  367. }
  368. -(BOOL)startPlay:(NSMutableDictionary *)playerParam{
  369. NSString *playUrl = playerParam[@"playUrl"];
  370. if (![self checkPlayUrl:playUrl]) {
  371. return NO;
  372. }
  373. TXVodPlayer *voidPlayer = (TXVodPlayer *)playerParam[@"player"];
  374. if(voidPlayer != nil)
  375. {
  376. TXVodPlayConfig *cfg = voidPlayer.config;
  377. if (cfg == nil) {
  378. cfg = [TXVodPlayConfig new];
  379. }
  380. cfg.cacheFolderPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:@"/txcache"];
  381. cfg.maxCacheItems = 5;
  382. voidPlayer.config = cfg;
  383. voidPlayer.vodDelegate = self;
  384. voidPlayer.isAutoPlay = NO;
  385. voidPlayer.enableHWAcceleration = YES;
  386. [voidPlayer setRenderRotation:HOME_ORIENTATION_DOWN];
  387. [voidPlayer setRenderMode:RENDER_MODE_FILL_EDGE];
  388. voidPlayer.loop = YES;
  389. //经过审核的视频才启动播放
  390. if ([playerParam[PLAY_REVIEW] intValue] == ReviewStatus_Normal) {
  391. [playerParam setObject:@(YES) forKey:PLAY_CLICK];
  392. int result = [voidPlayer startVodPlay:playUrl];
  393. if( result != 0)
  394. {
  395. [self toastTip:[NSString stringWithFormat:@"%@%d", kErrorMsgRtmpPlayFailed, result]];
  396. [self closeVCWithRefresh:YES popViewController:YES];
  397. return NO;
  398. }
  399. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  400. }
  401. }
  402. _startSeek = NO;
  403. NSString* ver = [TXLiveBase getSDKVersionStr];
  404. _logMsg = [NSString stringWithFormat:@"rtmp sdk version: %@",ver];
  405. [_currentCell.logicView.logViewEvt setText:_logMsg];
  406. return YES;
  407. }
  408. -(BOOL)startVodPlay{
  409. [self clearLog];
  410. NSString* ver = [TXLiveBase getSDKVersionStr];
  411. _logMsg = [NSString stringWithFormat:@"rtmp sdk version: %@",ver];
  412. [_currentCell.logicView.logViewEvt setText:_logMsg];
  413. _currentPlayer.vodDelegate = self;
  414. NSMutableDictionary *playerParam = [self getPlayerParam:_currentPlayer];
  415. [playerParam setObject:@(NO) forKey:PLAY_PREPARE];
  416. [self resumePlayer];
  417. return YES;
  418. }
  419. - (void)stopRtmp{
  420. for (NSMutableDictionary *param in _playerList) {
  421. TXVodPlayer *player = param[@"player"];
  422. player.vodDelegate = nil;
  423. [player stopPlay];
  424. [player removeVideoWidget];
  425. }
  426. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  427. }
  428. - (NSString *)playUrl{
  429. TCLiveInfo *liveInfo = _liveInfos[_liveInfoIndex];
  430. NSString *playUrl = [self checkHttps:liveInfo.playurl];
  431. return playUrl;
  432. }
  433. - (NSMutableDictionary *)getPlayerParam:(TXVodPlayer *)player{
  434. for (NSMutableDictionary *param in _playerList) {
  435. if ([[param objectForKey:@"player"] isEqual:player]) {
  436. return param;
  437. }
  438. }
  439. return nil;
  440. }
  441. #pragma mark - UI EVENT
  442. -(void)closeVC:(BOOL)isRefresh popViewController:(BOOL)popViewController{
  443. [self closeVCWithRefresh:isRefresh popViewController:popViewController];
  444. // [UMSocialUIManager dismissShareMenuView];
  445. }
  446. - (void)closeVCWithRefresh:(BOOL)refresh popViewController: (BOOL)popViewController {
  447. [self stopRtmp];
  448. [[NSNotificationCenter defaultCenter] removeObserver:self];
  449. if (refresh) {
  450. dispatch_async(dispatch_get_main_queue(), ^{
  451. [[NSNotificationCenter defaultCenter] postNotificationName:kTCLivePlayError object:self];
  452. });
  453. }
  454. if (popViewController) {
  455. [self.navigationController popViewControllerAnimated:YES];
  456. }
  457. }
  458. -(void)clickPlayVod{
  459. if (!_videoFinished) {
  460. if (_videoPause) {
  461. [_currentPlayer resume];
  462. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
  463. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  464. } else {
  465. [_currentPlayer pause];
  466. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
  467. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  468. }
  469. _videoPause = !_videoPause;
  470. }
  471. else {
  472. [_currentPlayer resume];
  473. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
  474. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  475. }
  476. }
  477. -(void)clickScreen:(UITapGestureRecognizer *)gestureRecognizer{
  478. //todo
  479. }
  480. - (void)clickLog:(UIButton*)btn {
  481. if (_log_switch == YES)
  482. {
  483. _currentCell.logicView.statusView.hidden = YES;
  484. _currentCell.logicView.logViewEvt.hidden = YES;
  485. [btn setImage:[UIImage imageNamed:@"log"] forState:UIControlStateNormal];
  486. _currentCell.logicView.cover.hidden = YES;
  487. _log_switch = NO;
  488. }
  489. else
  490. {
  491. _currentCell.logicView.statusView.hidden = NO;
  492. _currentCell.logicView.logViewEvt.hidden = NO;
  493. [btn setImage:[UIImage imageNamed:@"log2"] forState:UIControlStateNormal];
  494. _currentCell.logicView.cover.alpha = 0.5;
  495. _currentCell.logicView.cover.hidden = NO;
  496. _log_switch = YES;
  497. }
  498. _log_switch = YES;
  499. // 2019年08月24日
  500. btn.hidden = YES;
  501. }
  502. - (void)clickChorus:(UIButton *)button {
  503. NSLog(@"MorningLight");
  504. // if([TCLoginParam shareInstance].isExpired){
  505. // [[AppDelegate sharedAppDelegate] enterLoginUI];
  506. // return;
  507. // }
  508. [TCUtil report:xiaoshipin_videochorus userName:nil code:0 msg:ASLocalizedString(@"合唱事件")];
  509. if (_currentPlayer.isPlaying) {
  510. [self clickPlayVod];
  511. }
  512. _hub = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
  513. _hub.mode = MBProgressHUDModeText;
  514. _hub.label.text = NSLocalizedString(@"TCVodPlay.VideoLoading", nil);
  515. __weak __typeof(self) weakSelf = self;
  516. NSMutableDictionary *playerParam = [self getPlayerParam:_currentPlayer];
  517. [TCUtil downloadVideo:playerParam[@"playUrl"] cachePath:nil process:^(CGFloat process) {
  518. [weakSelf onloadVideoProcess:process];
  519. } complete:^(NSString *videoPath) {
  520. [weakSelf onloadVideoComplete:videoPath];
  521. }];
  522. }
  523. -(void)onloadVideoProcess:(CGFloat)process {
  524. _hub.label.text = [NSString stringWithFormat: NSLocalizedString(@"TCVodPlay.VideoLoadingFmt", nil),(int)(process * 100)];
  525. }
  526. -(void)onloadVideoComplete:(NSString *)videoPath {
  527. if (videoPath) {
  528. TCVideoRecordViewController *vc = [[TCVideoRecordViewController alloc] init];
  529. vc.videoPath = videoPath;
  530. [self.navigationController pushViewController:vc animated:YES];
  531. [_hub hideAnimated:YES];
  532. }else{
  533. _hub.label.text = NSLocalizedString(@"TCVodPlay.VideoLoadFailed", nil);
  534. [_hub hideAnimated:YES afterDelay:1.0];
  535. }
  536. }
  537. #pragma mark UISlider - play seek
  538. -(void)onSeek:(UISlider *)slider{
  539. [_currentPlayer seek:_sliderValue];
  540. _trackingTouchTS = [[NSDate date]timeIntervalSince1970]*1000;
  541. _startSeek = NO;
  542. }
  543. -(void)onSeekBegin:(UISlider *)slider{
  544. _startSeek = YES;
  545. }
  546. -(void)onDrag:(UISlider *)slider {
  547. float progress = slider.value;
  548. int intProgress = progress + 0.5;
  549. _currentCell.logicView.playLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",(int)intProgress / 3600,(int)(intProgress / 60), (int)(intProgress % 60)];
  550. _sliderValue = slider.value;
  551. }
  552. #pragma mark TXVodPlayListener
  553. -(void) onPlayEvent:(TXVodPlayer *)player event:(int)EvtID withParam:(NSDictionary*)param
  554. {
  555. NSDictionary* dict = param;
  556. dispatch_async(dispatch_get_main_queue(), ^{
  557. //player 收到准备好事件,记录下状态,下次可以直接resume
  558. if (EvtID == PLAY_EVT_VOD_PLAY_PREPARED) {
  559. NSMutableDictionary *playerParam = [self getPlayerParam:player];
  560. [playerParam setObject:@(YES) forKey:PLAY_PREPARE];
  561. if ([_currentPlayer isEqual:player]){
  562. [player resume];
  563. }
  564. }
  565. // //暂时不需要旋转逻辑
  566. // if (EvtID == PLAY_EVT_CHANGE_RESOLUTION) {
  567. // if (player.width > player.height) {
  568. // [player setRenderRotation:HOME_ORIENTATION_RIGHT];
  569. // }
  570. // }
  571. //只处理当前播放器的Event事件
  572. if (![_currentPlayer isEqual:player]) return;
  573. [self report:EvtID];
  574. if (EvtID == PLAY_EVT_VOD_PLAY_PREPARED) {
  575. //收到PREPARED事件的时候 resume播放器
  576. [_currentPlayer resume];
  577. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal];
  578. } else if (EvtID == PLAY_EVT_PLAY_BEGIN) {
  579. _videoFinished = NO;
  580. } else if (EvtID == PLAY_EVT_RCV_FIRST_I_FRAME) {
  581. if (!_isInVC) {
  582. self.videoIsReady();
  583. }
  584. } else if (EvtID == PLAY_EVT_PLAY_PROGRESS && !_videoFinished) {
  585. if (_startSeek) return ;
  586. // 避免滑动进度条松开的瞬间可能出现滑动条瞬间跳到上一个位置
  587. long long curTs = [[NSDate date]timeIntervalSince1970]*1000;
  588. if (llabs(curTs - _trackingTouchTS) < 500) {
  589. return;
  590. }
  591. _trackingTouchTS = curTs;
  592. float progress = [dict[EVT_PLAY_PROGRESS] floatValue];
  593. int intProgress = progress + 0.5;
  594. _currentCell.logicView.playLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",(int)(intProgress / 3600), (int)(intProgress / 60), (int)(intProgress % 60)];
  595. [_currentCell.logicView.playProgress setValue:progress];
  596. float duration = [dict[EVT_PLAY_DURATION] floatValue];
  597. int intDuration = duration + 0.5;
  598. if (duration > 0 && _currentCell.logicView.playProgress.maximumValue != duration) {
  599. [_currentCell.logicView.playProgress setMaximumValue:duration];
  600. _currentCell.logicView.playDuration.text = [NSString stringWithFormat:@"%02d:%02d:%02d",(int)(intDuration / 3600), (int)(intDuration / 60 % 60), (int)(intDuration % 60)];
  601. }
  602. return ;
  603. } else if (EvtID == PLAY_ERR_NET_DISCONNECT || EvtID == PLAY_EVT_PLAY_END) {
  604. // [self stopRtmp];
  605. [_currentPlayer pause];
  606. _videoPause = NO;
  607. _videoFinished = YES;
  608. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  609. [_currentCell.logicView.playProgress setValue:0];
  610. _currentCell.logicView.playLabel.text = @"00:00:00";
  611. [_currentCell.logicView.playBtn setImage:[UIImage imageNamed:@"start"] forState:UIControlStateNormal];
  612. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  613. } else if (EvtID == PLAY_EVT_PLAY_LOADING){
  614. }
  615. });
  616. }
  617. -(void)report:(int)EvtID
  618. {
  619. if (EvtID == PLAY_EVT_RCV_FIRST_I_FRAME) {
  620. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"视频播放成功")];
  621. }
  622. else if(EvtID == PLAY_ERR_NET_DISCONNECT){
  623. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"网络断连,且经多次重连抢救无效,可以放弃治疗,更多重试请自行重启播放")];
  624. }
  625. else if(EvtID == PLAY_ERR_GET_RTMP_ACC_URL_FAIL){
  626. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"获取加速拉流地址失败")];
  627. }
  628. else if(EvtID == PLAY_ERR_FILE_NOT_FOUND){
  629. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"播放文件不存在")];
  630. }
  631. else if(EvtID == PLAY_ERR_HEVC_DECODE_FAIL){
  632. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"H265解码失败")];
  633. }
  634. else if(EvtID == PLAY_ERR_HLS_KEY){
  635. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"HLS解码key获取失败")];
  636. }
  637. else if(EvtID == PLAY_ERR_GET_PLAYINFO_FAIL){
  638. [TCUtil report:xiaoshipin_vodplay userName:nil code:EvtID msg:ASLocalizedString(@"获取点播文件信息失败")];
  639. }
  640. }
  641. -(void) onNetStatus:(TXVodPlayer *)player withParam:(NSDictionary*)param
  642. {
  643. }
  644. -(void) appendLog:(NSString*) evt time:(NSDate*) date mills:(int)mil
  645. {
  646. NSDateFormatter* format = [[NSDateFormatter alloc] init];
  647. format.dateFormat = @"hh:mm:ss";
  648. NSString* time = [format stringFromDate:date];
  649. NSString* log = [NSString stringWithFormat:@"[%@.%-3.3d] %@", time, mil, evt];
  650. if (_logMsg == nil) {
  651. _logMsg = @"";
  652. }
  653. _logMsg = [NSString stringWithFormat:@"%@\n%@", _logMsg, log];
  654. [_currentCell.logicView.logViewEvt setText:_logMsg];
  655. }
  656. - (void)clickShare:(UIButton *)button {
  657. [self shareLive];
  658. }
  659. #pragma mark UITableViewDelegate
  660. //- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  661. //{
  662. // return self.view.height;
  663. //}
  664. - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
  665. {
  666. return 0;
  667. }
  668. - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
  669. {
  670. return 0;
  671. }
  672. #pragma mark UITableViewDataSource
  673. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
  674. {
  675. return _liveInfos.count;
  676. }
  677. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  678. {
  679. static NSString *reuseIdentifier = @"reuseIdentifier";
  680. TCPlayViewCell *cell = (TCPlayViewCell *)[_tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
  681. if (cell == nil) {
  682. cell = [[TCPlayViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
  683. }
  684. cell.delegate = self;
  685. [cell setLiveInfo:_liveInfos[indexPath.row]];
  686. return cell;
  687. }
  688. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
  689. {
  690. _beginDragging = YES;
  691. }
  692. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
  693. {
  694. CGPoint rect = scrollView.contentOffset;
  695. NSInteger index = rect.y / self.view.height;
  696. if (_beginDragging && _liveInfoIndex != index) {
  697. if (index > _liveInfoIndex) {
  698. _dragDirection = DragDirection_Down;
  699. }else{
  700. _dragDirection = DragDirection_Up;
  701. }
  702. _liveInfoIndex = index;
  703. _currentCell = [_tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:_liveInfoIndex inSection:0]];
  704. [self resumePlayer];
  705. _beginDragging = NO;
  706. }
  707. }
  708. #pragma mark Utils
  709. - (void)clearLog {
  710. _logMsg = @"";
  711. [_currentCell.logicView.statusView setText:@""];
  712. [_currentCell.logicView.logViewEvt setText:@""];
  713. }
  714. -(NSString *)checkHttps:(NSString *)playUrl{
  715. NSStringCheck(playUrl);
  716. if ([playUrl hasPrefix:@"http:"]) {
  717. playUrl = [playUrl stringByReplacingOccurrencesOfString:@"http:" withString:@"https:"];
  718. }
  719. return playUrl;
  720. }
  721. -(BOOL)checkPlayUrl:(NSString*)playUrl {
  722. if ([playUrl hasPrefix:@"https:"] || [playUrl hasPrefix:@"http:"]) {
  723. if ([playUrl rangeOfString:@".flv"].length > 0) {
  724. } else if ([playUrl rangeOfString:@".m3u8"].length > 0){
  725. } else if ([playUrl rangeOfString:@".mp4"].length > 0){
  726. } else {
  727. [self toastTip:ASLocalizedString(@"播放地址不合法,点播目前仅支持flv,hls,mp4播放方式!")];
  728. return NO;
  729. }
  730. } else {
  731. [self toastTip:ASLocalizedString(@"播放地址不合法,点播目前仅支持flv,hls,mp4播放方式!")];
  732. return NO;
  733. }
  734. return YES;
  735. }
  736. @end