// // EZPlaybackViewController.m // EZOpenSDKDemo // // Created by DeJohn Dong on 15/11/3. // Copyright © 2015年 Ezviz. All rights reserved. // #import "EZPlaybackViewController.h" #import "UIViewController+EZBackPop.h" #import "DDCollectionViewFlowLayout.h" #import "MJRefresh.h" #import "EZRecordDownloader.h" #import "EZDeviceRecordDownloadTask.h" #import "EZCloudRecordDownloadTask.h" #import "EZRecordCell.h" #import "DDKit.h" #import "EZCloudRecordFile.h" #import "EZDeviceRecordFile.h" #import "EZPlayer.h" #import "HIKLoadView.h" #import "Masonry.h" #import "EZCameraInfo.h" #import "MBProgressHUD.h" #import "Toast+UIView.h" #import "EZCustomTableView.h" #import "EZStreamPlayer.h" @interface EZPlaybackViewController () { BOOL _isOpenSound; BOOL _isPlaying; NSTimeInterval _playSeconds; //播放秒数 NSTimeInterval _duringSeconds; //录像时长 EZDeviceRecordFile *_deviceRecord; EZCloudRecordFile *_cloudRecord; BOOL _isShowToolbox; NSArray *cloudRate; NSArray *sdCardRate; NSArray *cloudRateStr; NSArray *sdCardRateStr; // BOOL _isDoBack; } @property (nonatomic, strong) NSIndexPath *selectedIndexPath; @property (nonatomic, strong) NSMutableArray *records; @property (nonatomic, strong) NSDate *beginTime; @property (nonatomic, strong) NSDate *endTime; @property (nonatomic, strong) EZPlayer *player; @property (nonatomic, strong) EZStreamPlayer *streamPlayer; @property (nonatomic, strong) HIKLoadView *loadingView; @property (nonatomic) BOOL isSelectedDevice; @property (nonatomic, weak) IBOutlet UIView *playerView; @property (nonatomic, weak) IBOutlet UILabel *largeTitleLabel; @property (nonatomic, weak) IBOutlet UIDatePicker *datePicker; @property (nonatomic, weak) IBOutlet UITextField *dateTextField; @property (nonatomic, weak) IBOutlet UIToolbar *dateToolbar; @property (nonatomic, weak) IBOutlet UIButton *dateButton; @property (nonatomic, weak) IBOutlet UICollectionView *playbackList; @property (nonatomic, weak) IBOutlet UIView *toolView; @property (nonatomic, weak) IBOutlet UIButton *cloudButton; @property (nonatomic, weak) IBOutlet UIButton *deviceButton; @property (nonatomic, weak) IBOutlet UIView *selectedImageView; @property (nonatomic, weak) IBOutlet UIView *playerToolbox; @property (nonatomic, weak) IBOutlet UIButton *voiceButton; @property (nonatomic, weak) IBOutlet UIButton *playButton; @property (nonatomic, weak) IBOutlet UIButton *largeButton; @property (weak, nonatomic) IBOutlet UIButton *rateBtn; @property (weak, nonatomic) IBOutlet UIButton *streamPlayBtn; @property (nonatomic, weak) IBOutlet UILabel *playTimeLabel; @property (nonatomic, weak) IBOutlet UILabel *duringTimeLabel; @property (nonatomic, weak) IBOutlet UISlider *duringSlider; @property (nonatomic, weak) IBOutlet UIButton *largeBackButton; @property (nonatomic, weak) IBOutlet NSLayoutConstraint *selectedImageViewConstraint; @property (nonatomic, weak) IBOutlet NSLayoutConstraint *playerToolboxConstraint; @property (nonatomic, strong) NSTimer *playbackTimer; @property (nonatomic, strong) NSTimer *rateBtnTimer; @property (nonatomic, weak) IBOutlet UIImageView *noVideoImageView; @property (nonatomic, weak) IBOutlet UILabel *noVideoLabel; @property (nonatomic, strong) NSURLSessionDataTask *operation; @property (nonatomic, strong) EZCameraInfo *cameraInfo; @property (nonatomic, copy) NSString *verifyCode; @property (nonatomic, strong) EZRecordDownloader *downloader; @property (nonatomic, strong) EZCustomTableView *cloudRateView; @property (nonatomic, strong) EZCustomTableView *sdCardRateView; @property (weak, nonatomic) IBOutlet UILabel *streamTypeLabel; @property(nonatomic,assign)BOOL selectedCell; @end @implementation EZPlaybackViewController - (void)dealloc { NSLog(@"%@ dealloc", self.class); [EZOPENSDK releasePlayer:_player]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.navigationController.navigationBar.hidden = NO;//2021-02-01 } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.isAutorotate = YES; self.largeTitleLabel.text = self.deviceInfo.deviceName; self.largeTitleLabel.hidden = YES; if(!_records) _records = [NSMutableArray new]; DDCollectionViewFlowLayout *flowLayout = [[DDCollectionViewFlowLayout alloc] init]; flowLayout.delegate = self; [self.playbackList setCollectionViewLayout:flowLayout]; [self addHeaderRefresh]; //demo只获取了设备的第一个通道 if([self.deviceInfo.deviceType containsString:@"CAS"]) //hub { _cameraInfo = [[EZCameraInfo alloc]init]; _cameraInfo.deviceSerial = _hubCoDevSerial; _cameraInfo.cameraNo = _cameraIndex; _player = [EZOPENSDK createPlayerWithDeviceSerial:_cameraInfo.deviceSerial cameraNo:_cameraInfo.cameraNo]; } else { _cameraInfo = [self.deviceInfo.cameraInfo dd_objectAtIndex:_cameraIndex]; _player = [EZOPENSDK createPlayerWithDeviceSerial:_cameraInfo.deviceSerial cameraNo:_cameraInfo.cameraNo]; } _player.delegate = self; [_player setPlayerView:_playerView]; //判断设备是否加密,加密就从demo的内存中获取设备验证码填入到播放器的验证码接口里,本demo只处理内存存储,本地持久化存储用户自行完成 if (self.deviceInfo.isEncrypt) { _verifyCode = [[GlobalKit shareKit].deviceVerifyCodeBySerial objectForKey:_cameraInfo.deviceSerial]; [_player setPlayVerifyCode:_verifyCode]; } if(!_loadingView) _loadingView = [[HIKLoadView alloc] initWithHIKLoadViewStyle:HIKLoadViewStyleSqureClockWise]; [self.view insertSubview:_loadingView aboveSubview:self.playerView]; [_loadingView mas_makeConstraints:^(MASConstraintMaker *make) { make.width.height.mas_equalTo(@14); make.centerX.mas_equalTo(self.playerView.mas_centerX); make.centerY.mas_equalTo(self.playerView.mas_centerY); }]; [_loadingView stopSquareClockwiseAnimation]; _isOpenSound = YES; [self.duringSlider setThumbImage:[UIImage imageNamed:@"slider"] forState:UIControlStateNormal]; [self.duringSlider setThumbImage:[UIImage imageNamed:@"slider_sel"] forState:UIControlStateHighlighted]; self.cloudButton.selected = YES; self.largeBackButton.hidden = YES; self.dateTextField.inputView = self.datePicker; self.dateTextField.inputAccessoryView = self.dateToolbar; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"MM-dd ▽"; [self.dateButton setTitle:[dateFormatter stringFromDate:self.datePicker.date] forState:UIControlStateNormal]; dateFormatter.dateFormat = @"MM-dd △"; [self.dateButton setTitle:[dateFormatter stringFromDate:self.datePicker.date] forState:UIControlStateSelected]; _isShowToolbox = YES; cloudRate = @[@(EZOPENSDK_PLAY_RATE_1), @(EZOPENSDK_PLAY_RATE_4), @(EZOPENSDK_PLAY_RATE_8), @(EZOPENSDK_PLAY_RATE_16), @(EZOPENSDK_PLAY_RATE_32)]; sdCardRate = @[@(EZOPENSDK_PLAY_RATE_1), @(EZOPENSDK_PLAY_RATE_4), @(EZOPENSDK_PLAY_RATE_8), @(EZOPENSDK_PLAY_RATE_16)]; cloudRateStr = @[@"x1",@"x4",@"x8",@"x16",@"x32"]; sdCardRateStr = @[@"x1",@"x4",@"x8",@"x16"]; } - (void)viewWillDisappear:(BOOL)animated { [self invalidateTimer]; if (self.rateBtnTimer) { [self.rateBtnTimer invalidate]; self.rateBtnTimer = nil; } [self.loadingView stopSquareClockwiseAnimation]; [_player closeSound]; [_player stopPlayback]; [super viewWillDisappear:animated]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ - (UIInterfaceOrientationMask)supportedInterfaceOrientations { return UIInterfaceOrientationMaskAllButUpsideDown; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { self.navigationController.navigationBarHidden = NO; self.toolView.hidden = NO; self.playbackList.hidden = NO; self.largeBackButton.hidden = YES; self.largeTitleLabel.hidden = YES; self.playerToolboxConstraint.constant = 60.0f; self.largeButton.hidden = NO; self.voiceButton.hidden = NO; self.playButton.hidden = NO; if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) { self.playerToolboxConstraint.constant = 23.0f; self.playButton.hidden = YES; self.voiceButton.hidden = YES; self.largeButton.hidden = YES; self.toolView.hidden = YES; self.largeTitleLabel.hidden = NO; self.playbackList.hidden = YES; self.largeBackButton.hidden = NO; self.navigationController.navigationBarHidden = YES; } } #pragma mark - UIAlertViewDelegate Methods - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (alertView.alertViewStyle == UIAlertViewStyleSecureTextInput) { if (buttonIndex == 1) { NSString *checkCode = [alertView textFieldAtIndex:0].text; [[GlobalKit shareKit].deviceVerifyCodeBySerial setValue:checkCode forKey:_cameraInfo.deviceSerial]; self.verifyCode = checkCode; [self.player setPlayVerifyCode:checkCode]; [self doPlayback]; } } else { if (buttonIndex == 1) { [self showSetPassword]; return; } } } #pragma mark - UICollectionViewDataSource Methods - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return [_records count]; } - (NSInteger)collectionView:(UICollectionView *)collectionView layout:(DDCollectionViewFlowLayout *)layout numberOfColumnsInSection:(NSInteger)section { return 3; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { EZRecordCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"RecordCell" forIndexPath:indexPath]; cell.deviceSerial = _cameraInfo.deviceSerial; cell.isSelectedDevice = _isSelectedDevice; cell.delegate = self; if(_isSelectedDevice) { [cell setDeviceRecord:[_records dd_objectAtIndex:indexPath.row] selected:(indexPath.row == self.selectedIndexPath.row)]; } else { [cell setCloudRecord:[_records dd_objectAtIndex:_records.count- 1 -indexPath.row] selected:(indexPath.row == self.selectedIndexPath.row)]; } return cell; } #pragma mark - UICollectionViewDelegate Methods - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(106 * [UIScreen mainScreen].bounds.size.width / 320.0f, 80.0 * [UIScreen mainScreen].bounds.size.width / 320.0f); } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { if (!self.selectedCell) { self.selectedCell = YES; [self performSelector:@selector(repeatDelay) withObject:nil afterDelay:1.0f]; [self invalidateTimer]; // [_player stopPlayback]; if(_isSelectedDevice) { _deviceRecord = [_records dd_objectAtIndex:indexPath.row]; [_player startPlaybackFromDevice:_deviceRecord]; } else { _cloudRecord = [_records dd_objectAtIndex:_records.count - 1 - indexPath.row]; [_player startPlaybackFromCloud:_cloudRecord]; } [self duringTimeShow]; self.selectedIndexPath = indexPath; [collectionView reloadData]; [self.loadingView startSquareClcokwiseAnimation]; } } #pragma mark - ValidateCode Methods - (void)repeatDelay{ self.selectedCell = NO; } - (void)showSetPassword { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"device_input_verify_code", @"请输入视频图片加密密码") message:NSLocalizedString(@"device_verify_code_tip", @"您的视频已加密,请输入密码进行查看,初始密码为机身标签上的验证码,如果没有验证码,请输入ABCDEF(密码区分大小写)") delegate:self cancelButtonTitle:NSLocalizedString(@"cancel", @"取消") otherButtonTitles:NSLocalizedString(@"done", @"确定"), nil]; alertView.alertViewStyle = UIAlertViewStyleSecureTextInput; [alertView show]; } - (void)showRetry { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"device_tip_title", @"温馨提示") message:NSLocalizedString(@"device_verify_code_wrong", @"设备密码错误") delegate:self cancelButtonTitle:NSLocalizedString(@"cancel", @"取消") otherButtonTitles:NSLocalizedString(@"retry", @"重试"), nil]; [alertView show]; } #pragma mark - EZRecordCellDelegate Methods - (void) didClickDownlodBtn:(id)recordFile { NSDateFormatter *dateformatter = [[NSDateFormatter alloc] init]; dateformatter.dateFormat = @"yyyyMMddHHmmss"; if (_isSelectedDevice) { EZDeviceRecordFile *deviceFile = (EZDeviceRecordFile *)recordFile; NSString *path = [NSString stringWithFormat:@"%@/ezopensdk/DeviceRecord/%@.ps",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject], [dateformatter stringFromDate:deviceFile.startTime]]; NSLog(@"path: %@ ", path); [self startDeviceRecordDownload:path deviceFile:deviceFile]; } else { EZCloudRecordFile *cloudFile = (EZCloudRecordFile *)recordFile; NSString *path = [NSString stringWithFormat:@"%@/ezopensdk/CloudRecord/%@.ps",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject], [dateformatter stringFromDate:cloudFile.startTime]]; NSLog(@"path: %@ ", path); [self startCloudRecordDownload:path cloudFile:cloudFile]; } } - (void) startDeviceRecordDownload:(NSString*)path deviceFile:(EZDeviceRecordFile *)deviceFile { //创建下载任务 [[EZDeviceRecordDownloadTask alloc]initTaskWithID:_selectedIndexPath.row DeviceRecordFileInfo:deviceFile deviceSerial:_cameraInfo.deviceSerial cameraNo:self.cameraInfo.cameraNo verifyCode:self.verifyCode savePath:path completion:^(EZDeviceRecordDownloadTask * _Nonnull task) { //设置回调函数 __weak typeof(task) weakTask = task; [task setDownloadCallBackWithFinshed:^(EZRecordDownloaderStatus statusCode) { __strong typeof(weakTask) strongTask = weakTask; NSLog(@"statuCode:%ld", (long)statusCode); switch (statusCode) { case EZRecordDownloaderStatusFinish: [self.navigationController.view makeToast:[NSString stringWithFormat:@"SDD Task:%lu-下载成功", (unsigned long)strongTask.taskID]]; break; case EZRecordDownloaderStatusMoreToken: NSLog(@"EZRecordDownloaderStatusMoreToken."); break; default: break; } [[EZRecordDownloader shareInstane] stopDownloadTask:strongTask]; } failed:^(NSError * _Nonnull error) { NSLog(@"EZDeviceRecordDownloader error:%@", error); __strong typeof(weakTask) strongTask = weakTask; if (error.code == 395416 || error.code == 380045) { [self.navigationController.view makeToast:[NSString stringWithFormat:@"SDD Task:%lu-下载限制,达到最大连接数", (unsigned long)strongTask.taskID]]; } else { [self.navigationController.view makeToast:[NSString stringWithFormat:@"SDD Task:%lu-下载失败", (unsigned long)strongTask.taskID]]; } [[EZRecordDownloader shareInstane] stopDownloadTask:strongTask]; }]; if (task) { [self.navigationController.view makeToast:[NSString stringWithFormat:@"SDD Task:%lu-开始下载", (unsigned long)task.taskID]]; //加入下载队列下载 int ret = [[EZRecordDownloader shareInstane] addDownloadTask:task]; if (ret == -1) { [self.navigationController.view makeToast:[NSString stringWithFormat:@"SDD Task:%lu-任务为空", (unsigned long)task.taskID]]; } else if (ret == -2){ [self.navigationController.view makeToast:[NSString stringWithFormat:@"SDD Task:%lu-任务已在下载中", (unsigned long)task.taskID]]; } } }]; } - (void) startCloudRecordDownload:(NSString*)path cloudFile:(EZCloudRecordFile *)cloudFile { EZCloudRecordDownloadTask *task = [[EZCloudRecordDownloadTask alloc] initTaskWithID:_selectedIndexPath.row cloudRecordFile:cloudFile verifyCode:self.verifyCode savePath:path]; //设置回调函数 __weak typeof(task) weakTask = task; [task setDownloadCallBackWithFinshed:^(EZRecordDownloaderStatus statusCode) { __strong typeof(weakTask) strongTask = weakTask; NSLog(@"statuCode:%ld", (long)statusCode); switch (statusCode) { case EZRecordDownloaderStatusFinish: [self.navigationController.view makeToast:[NSString stringWithFormat:@"CD Task:%lu-下载成功", (unsigned long)strongTask.taskID]]; break; case EZRecordDownloaderStatusMoreToken: NSLog(@"EZRecordDownloaderStatusMoreToken."); break; default: break; } [[EZRecordDownloader shareInstane] stopDownloadTask:strongTask]; } failed:^(NSError * _Nonnull error) { NSLog(@"EZDeviceRecordDownloader error:%@", error); __strong typeof(weakTask) strongTask = weakTask; [self.navigationController.view makeToast:[NSString stringWithFormat:@"CD Task:%lu-下载失败", (unsigned long)strongTask.taskID]]; [[EZRecordDownloader shareInstane] stopDownloadTask:strongTask]; }]; if (task) { [self.navigationController.view makeToast:[NSString stringWithFormat:@"CD Task:%lu-开始下载", (unsigned long)task.taskID]]; //加入下载队列下载 int ret = [[EZRecordDownloader shareInstane] addDownloadTask:task]; if (ret == -1) { [self.navigationController.view makeToast:[NSString stringWithFormat:@"CD Task:%lu-任务为空", (unsigned long)task.taskID]]; } else if (ret == -2){ [self.navigationController.view makeToast:[NSString stringWithFormat:@"CD Task:%lu-任务已在下载中", (unsigned long)task.taskID]]; } } } #pragma mark - PlayerDelegate Methods - (void)player:(EZPlayer *)player didPlayFailed:(NSError *)error { [self invalidateTimer]; [player stopPlayback]; NSLog(@"player: %@ didPlayFailed: %@", player, error); //如果是需要验证码或者是验证码错误 if (error.code == EZ_SDK_NEED_VALIDATECODE) { [self showSetPassword]; return; } else if (error.code == EZ_SDK_VALIDATECODE_NOT_MATCH) { [self showRetry]; return; } [UIView dd_showDetailMessage:[NSString stringWithFormat:@"%d", (int)error.code]]; if (error.code == 400409) { _isPlaying = NO; [self.playButton setImage:[UIImage imageNamed:@"preview_play_btn_sel"] forState:UIControlStateHighlighted]; [self.playButton setImage:[UIImage imageNamed:@"preview_play_btn"] forState:UIControlStateNormal]; } } - (void)player:(EZPlayer *)player didReceivedMessage:(NSInteger)messageCode { NSLog(@"player: %@ didReceivedMessage: %d", player, (int)messageCode); if(messageCode == PLAYER_PLAYBACK_START) { _isPlaying = YES; [self.playButton setImage:[UIImage imageNamed:@"pause_sel"] forState:UIControlStateHighlighted]; [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal]; [self.loadingView stopSquareClockwiseAnimation]; if (!_isOpenSound) { [player closeSound]; } [self invalidateTimer]; self.playbackTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(playBoxToolRefresh:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.playbackTimer forMode:NSRunLoopCommonModes]; [self performSelector:@selector(hiddenPlayerToolbox:) withObject:nil afterDelay:5.0f]; NSLog(@"GetStreamFetchType:%d", [self.player getStreamFetchType]); // [self showStreamFetchType]; } else if (messageCode == PLAYER_PLAYBACK_STOP) { [self invalidateTimer]; self.duringSlider.value = 1.0; self.playTimeLabel.text = self.duringTimeLabel.text; [self.playButton setImage:[UIImage imageNamed:@"preview_play_btn_sel"] forState:UIControlStateHighlighted]; [self.playButton setImage:[UIImage imageNamed:@"preview_play_btn"] forState:UIControlStateNormal]; } else if (messageCode == PLAYER_PLAYSPEED_LOWER) { [self.navigationController.view makeToast:@"回放倍速降速通知"]; } } - (void) invalidateTimer { if(self.playbackTimer) { [self.playbackTimer invalidate]; self.playbackTimer = nil; } } - (IBAction)showToolBar:(id)sender { if(!_isShowToolbox){ _isShowToolbox = YES; [UIView animateWithDuration:0.3 animations:^{ self.playerToolbox.alpha = 1.0f; }]; [self performSelector:@selector(hiddenPlayerToolbox:) withObject:nil afterDelay:5.0f]; } else{ [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hiddenPlayerToolbox:) object:nil]; [self hiddenPlayerToolbox:nil]; } } - (IBAction)hiddenPlayerToolbox:(id)sender { // [UIView animateWithDuration:0.3 // animations:^{ // self.playerToolbox.alpha = 0.0f; // } // completion:^(BOOL finished) { // _isShowToolbox = NO; // }]; } #pragma mark - MJRefresh Methods - (void)addHeaderRefresh { [self.operation cancel]; [self.playbackList.header endRefreshing]; __weak __typeof(self) weakSelf = self; self.playbackList.header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ weakSelf.noVideoImageView.hidden = YES; weakSelf.noVideoLabel.hidden = YES; if (weakSelf.isSelectedDevice) { weakSelf.operation = [EZOPENSDK searchRecordFileFromDevice:_cameraInfo.deviceSerial cameraNo:_cameraInfo.cameraNo beginTime:weakSelf.beginTime endTime:weakSelf.endTime completion:^(NSArray *deviceRecords, NSError *error) { NSLog(@"deviceRecords is %@, error is %@", deviceRecords, error); if (!error) { [weakSelf.records removeAllObjects]; if(deviceRecords.count == 0) { weakSelf.noVideoLabel.hidden = NO; weakSelf.noVideoImageView.hidden = NO; [weakSelf.playbackList reloadData]; [weakSelf.playbackList.header endRefreshing]; return; } if (deviceRecords.count > 0) { [weakSelf.records addObjectsFromArray:deviceRecords]; [weakSelf.playbackList reloadData]; [weakSelf.playbackList.header endRefreshing]; [weakSelf doPlayback]; } } else { [UIView dd_showMessage:[NSString stringWithFormat:@"error code is %d",(int) error.code] onParentView:self.view]; [weakSelf.playbackList.header endRefreshing]; [weakSelf.records removeAllObjects]; [weakSelf.playbackList reloadData]; } }]; } else { weakSelf.operation = [EZOPENSDK searchRecordFileFromCloud:_cameraInfo.deviceSerial cameraNo:_cameraInfo.cameraNo beginTime:weakSelf.beginTime endTime:weakSelf.endTime completion:^(NSArray *cloudRecords, NSError *error) { NSLog(@"cloudRecords is %@, error is %@", cloudRecords, error); if (error) { if (error.code > 0) { [UIView dd_showMessage:[NSString stringWithFormat:@"error code is %d",(int) error.code] onParentView:self.view]; } [weakSelf.playbackList.header endRefreshing]; [weakSelf.records removeAllObjects]; [weakSelf.playbackList reloadData]; return ; } [weakSelf.records removeAllObjects]; if(cloudRecords.count == 0) { weakSelf.noVideoLabel.hidden = NO; weakSelf.noVideoImageView.hidden = NO; [weakSelf.playbackList reloadData]; [weakSelf.playbackList.header endRefreshing]; return; } if (cloudRecords.count > 0) { [weakSelf.records addObjectsFromArray:cloudRecords]; [weakSelf.playbackList reloadData]; [weakSelf.playbackList.header endRefreshing]; [weakSelf doPlayback]; } }]; } }]; self.playbackList.header.automaticallyChangeAlpha = YES; [self.playbackList.header beginRefreshing]; } #pragma mark - Action Methods - (void)doPlayback { if (_isSelectedDevice) { if (self.selectedIndexPath) { _deviceRecord = [_records dd_objectAtIndex:self.selectedIndexPath.row]; } else { _deviceRecord = [_records firstObject]; } [_player startPlaybackFromDevice:_deviceRecord]; } else { if (self.selectedIndexPath) { _cloudRecord = [_records dd_objectAtIndex:_records.count - 1 - self.selectedIndexPath.row]; [_player startPlaybackFromCloud:_cloudRecord]; } else { _cloudRecord = [_records lastObject]; [_player startPlaybackFromCloud:_cloudRecord]; } } [self duringTimeShow]; [self.loadingView startSquareClcokwiseAnimation]; _isPlaying = YES; } - (IBAction)large:(id)sender { NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"]; } - (IBAction)largeBack:(id)sender { NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"]; } - (IBAction)voiceButtonClicked:(id)sender { if(_isOpenSound){ [_player closeSound]; [self.voiceButton setImage:[UIImage imageNamed:@"preview_unvoice_btn_sel"] forState:UIControlStateHighlighted]; [self.voiceButton setImage:[UIImage imageNamed:@"preview_unvoice_btn"] forState:UIControlStateNormal]; } else { [_player openSound]; [self.voiceButton setImage:[UIImage imageNamed:@"preview_voice_btn_sel"] forState:UIControlStateHighlighted]; [self.voiceButton setImage:[UIImage imageNamed:@"preview_voice_btn"] forState:UIControlStateNormal]; } _isOpenSound = !_isOpenSound; } #pragma mark - EZCustomTableViewDelegate - (void) EZCustomTableView:(EZCustomTableView *)customTableView didSelectedTableViewCell:(NSIndexPath *)indexPath { [self hideRateView]; if(_isSelectedDevice) { EZPlaybackRate rate = [sdCardRate[indexPath.row] intValue]; [_player setPlaybackRate:rate mode:0]; } else { EZPlaybackRate rate = [cloudRate[indexPath.row] intValue]; [_player setPlaybackRate:rate mode:0]; } } - (EZCustomTableView *)sdCardRateView { if (!_sdCardRateView) { _sdCardRateView = [[EZCustomTableView alloc] initTableViewWith:sdCardRateStr delegate:self]; _sdCardRateView.hidden = YES; [self.view addSubview:_sdCardRateView]; [_sdCardRateView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.equalTo(self.rateBtn); make.width.mas_equalTo(70); make.height.mas_equalTo(150); }]; } return _sdCardRateView; } - (EZCustomTableView *)cloudRateView { if (!_cloudRateView) { _cloudRateView = [[EZCustomTableView alloc] initTableViewWith:cloudRateStr delegate:self]; _cloudRateView.hidden = YES; [self.view addSubview:_cloudRateView]; [_cloudRateView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.equalTo(self.rateBtn); make.width.mas_equalTo(70); make.height.mas_equalTo(150); }]; } return _cloudRateView; } - (IBAction)clickRateBtn:(UIButton *)sender { self.rateBtnTimer = [NSTimer scheduledTimerWithTimeInterval:2.5f target:self selector:@selector(hideRateView) userInfo:nil repeats:NO]; if(_isSelectedDevice) { self.sdCardRateView.hidden = NO; } else { self.cloudRateView.hidden = NO; } } - (void) hideRateView { self.cloudRateView.hidden = YES; self.sdCardRateView.hidden = YES; [self.rateBtnTimer invalidate]; self.rateBtnTimer = nil; } - (IBAction)duringValueChange:(id)sender { NSDate *offsetTime = nil; if(_isSelectedDevice) { offsetTime = [_deviceRecord.startTime dateByAddingTimeInterval:_duringSeconds * self.duringSlider.value]; } else { offsetTime = [_cloudRecord.startTime dateByAddingTimeInterval:_duringSeconds * self.duringSlider.value]; } [self invalidateTimer]; [_player seekPlayback:offsetTime]; [self.loadingView startSquareClcokwiseAnimation]; } - (IBAction)playButtonClicked:(id)sender { if(_isPlaying) { [_player pausePlayback]; [self.playButton setImage:[UIImage imageNamed:@"preview_play_btn_sel"] forState:UIControlStateHighlighted]; [self.playButton setImage:[UIImage imageNamed:@"preview_play_btn"] forState:UIControlStateNormal]; if(_playbackTimer && !_isSelectedDevice) { [_playbackTimer invalidate]; _playbackTimer = nil; } } else { [_player resumePlayback]; [self.playButton setImage:[UIImage imageNamed:@"pause_sel"] forState:UIControlStateHighlighted]; [self.playButton setImage:[UIImage imageNamed:@"pause"] forState:UIControlStateNormal]; if(!_isSelectedDevice) [self.loadingView startSquareClcokwiseAnimation]; } _isPlaying = !_isPlaying; } - (IBAction)cloudButtonClicked:(id)sender { if (self.player) { [self invalidateTimer]; [self.player stopPlayback]; } self.selectedIndexPath = nil; [UIView animateWithDuration:0.3 animations:^{ self.selectedImageViewConstraint.constant = 0; [self.toolView setNeedsUpdateConstraints]; [self.toolView layoutIfNeeded]; } completion:^(BOOL finished) { self.cloudButton.selected = YES; self.deviceButton.selected = NO; }]; _isSelectedDevice = NO; [self.records removeAllObjects]; [self.playbackList reloadData]; [self addHeaderRefresh]; } - (IBAction)deviceButtonClicked:(id)sender { if (self.player) { [self invalidateTimer]; [self.player stopPlayback]; } self.selectedIndexPath = nil; [UIView animateWithDuration:0.3 animations:^{ self.selectedImageViewConstraint.constant = self.view.bounds.size.width / 2.0f; [self.toolView setNeedsUpdateConstraints]; [self.toolView layoutIfNeeded]; } completion:^(BOOL finished) { self.cloudButton.selected = NO; self.deviceButton.selected = YES; }]; _isSelectedDevice = YES; [self.records removeAllObjects]; [self.playbackList reloadData]; [self addHeaderRefresh]; } - (IBAction)cancel:(id)sender { [self.dateTextField resignFirstResponder]; self.dateButton.selected = NO; } - (IBAction)confirm:(id)sender { [self.dateTextField resignFirstResponder]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"MM-dd ▽"; [self.dateButton setTitle:[dateFormatter stringFromDate:self.datePicker.date] forState:UIControlStateNormal]; dateFormatter.dateFormat = @"MM-dd △"; [self.dateButton setTitle:[dateFormatter stringFromDate:self.datePicker.date] forState:UIControlStateSelected]; self.dateButton.selected = NO; [self.playbackList.header beginRefreshing]; } - (IBAction)dateButtonClicked:(id)sender { [self.dateTextField becomeFirstResponder]; self.datePicker.maximumDate = [NSDate date]; self.dateButton.selected = YES; } - (void)duringTimeShow { if(_isSelectedDevice) { _duringSeconds = [_deviceRecord.stopTime timeIntervalSinceDate:_deviceRecord.startTime]; } else { _duringSeconds = [_cloudRecord.stopTime timeIntervalSinceDate:_cloudRecord.startTime]; } if(_duringSeconds >= 3600) { int hour = (int)_duringSeconds / 3600; int minute = ((int)_duringSeconds % 3600) / 60; int seconds = (int)_duringSeconds % 60; self.duringTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, seconds]; self.playTimeLabel.text = @"00:00:00"; } else { int minute = (int)_duringSeconds / 60; int seconds = (int)_duringSeconds % 60; self.duringTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d", minute, seconds]; self.playTimeLabel.text = @"00:00"; } } - (void)playBoxToolRefresh:(NSTimer *)timer { NSDate *currentTime = [_player getOSDTime]; NSLog(@"getOSDTime === %@", currentTime); if(_isSelectedDevice) { _playSeconds = [currentTime timeIntervalSinceDate:_deviceRecord.startTime]; } else { _playSeconds = [currentTime timeIntervalSinceDate:_cloudRecord.startTime]; } if(_playSeconds >= 3600) { int hour = (int)_playSeconds / 3600; int minute = ((int)_playSeconds % 3600) / 60; int seconds = (int)_playSeconds % 60; self.playTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, seconds]; } else { int minute = (int)_playSeconds / 60; int seconds = (int)_playSeconds % 60; self.playTimeLabel.text = [NSString stringWithFormat:@"%02d:%02d", minute, seconds]; } self.duringSlider.value = _playSeconds/_duringSeconds; } - (void) showStreamFetchType { int type = [self.player getStreamFetchType]; if (type >= 0) { self.streamTypeLabel.hidden = NO; switch (type) { case 0: self.streamTypeLabel.text = @"取流方式:流媒体"; break; case 1: self.streamTypeLabel.text = @"取流方式:P2P"; break; case 2: self.streamTypeLabel.text = @"取流方式:内网直连"; break; case 3: self.streamTypeLabel.text = @"取流方式:外网直连"; break; case 8: self.streamTypeLabel.text = @"取流方式:反向直连"; break; case 9: self.streamTypeLabel.text = @"取流方式:NetSDK"; break; default: self.streamTypeLabel.text = @"取流方式:unknown"; break; } } } #pragma mark - Get Methods - (NSDate *)beginTime { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyy-MM-dd"; NSString *beginTimeString = [NSString stringWithFormat:@"%@ 00:00:00", [dateFormatter stringFromDate:self.datePicker.date]]; dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; _beginTime = [dateFormatter dateFromString:beginTimeString]; return _beginTime; } - (NSDate *)endTime { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyy-MM-dd"; NSString *beginTimeString = [NSString stringWithFormat:@"%@ 23:59:59", [dateFormatter stringFromDate:self.datePicker.date]]; dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss"; _endTime = [dateFormatter dateFromString:beginTimeString]; return _endTime; } #pragma mark - stream player - (IBAction)clickStreamBtn:(UIButton *)sender { sender.selected = !sender.selected; if (sender.selected) { self.streamPlayer = [EZStreamPlayer createPlayerWithDeviceSerial:_cameraInfo.deviceSerial cameraNo:self.cameraInfo.cameraNo]; self.streamPlayer.delegate = self; [self.streamPlayer setVerifyCode:self.verifyCode]; [self.streamPlayer startDevicePlayback:_deviceRecord]; } else { [self.streamPlayer stopDevicePlayback]; [self.streamPlayer destoryPlayer]; self.streamPlayer = nil; } } - (void)streamPlayer:(EZStreamPlayer *)player didPlayFailed:(NSError *)error { dispatch_async(dispatch_get_main_queue(), ^{ self.streamPlayBtn.selected = NO; [self.navigationController.view makeToast:[NSString stringWithFormat:@"ErrorCode:%ld", (long)error.code]]; }); [self.streamPlayer stopDevicePlayback]; [self.streamPlayer destoryPlayer]; self.streamPlayer = nil; } - (void)streamPlayer:(EZStreamPlayer *)player didReceivedMessage:(EZStreamPlayerMsgType)msgType { dispatch_async(dispatch_get_main_queue(), ^{ NSString *msg; switch (msgType) { case EZStreamPlayerMsgTypeRealPlayStart: msg = @"开启预览成功"; break; case EZStreamPlayerMsgTypeRealPlayClose: msg = @"关闭预览成功"; break; case EZStreamPlayerMsgTypePlayBackStart: msg = @"开启设备回放成功"; break; case EZStreamPlayerMsgTypePlayBackClose: msg = @"关闭设备回放成功"; break; default: break; } [self.navigationController.view makeToast:msg]; }); } - (void)streamPlayer:(EZStreamPlayer *)player didReceivedData:(EZStreamDataType)dataType data:(int8_t *)data length:(int)dataLength { if (dataType == EZStreamDataTypeHeader) { dispatch_async(dispatch_get_main_queue(), ^{ [self.navigationController.view makeToast:@"开始写入文件"]; }); } else if (dataType == EZStreamDataTypeStreamEnd) { dispatch_async(dispatch_get_main_queue(), ^{ self.streamPlayBtn.selected = NO; }); [self.streamPlayer stopDevicePlayback]; [self.streamPlayer destoryPlayer]; self.streamPlayer = nil; } } @end