//
//  EZRealPlayViewController.m
//  EZOpenSDKDemo
//
//  Created by DeJohn Dong on 15/10/28.
//  Copyright © 2015年 Ezviz. All rights reserved.
//

#import "EZRealPlayViewController.h"
#import <sys/sysctl.h>
#import <mach/mach.h>
#import <Photos/Photos.h>
#import "FCFileManager.h"
#import "EZBusinessTool.h"
#import "EZStreamConfigLoader.h"
#import "EZScreenOrientationTool.h"
#import "UIViewController+EZBackPop.h"
#import "UIAlertController+DeviceVerifyCodeBySerial.h"

#import "EZDeviceInfo.h"
#import "EZCameraInfo.h"
#import "EZSubDeviceInfo.h"
#import "EZDevicePtzAngleInfo.h"
#import "EZPMPlayPrivateTokenInfo.h"
#import "EZDeviceDetailPublicInfo.h"
#import "EZVideoQualityInfo.h"
#import "EZPlayWaterMarkConfig.h"

#import "EZPlayer.h"
#import "MBProgressHUD.h"
#import "HIKLoadView.h"
#import "YSHorDashRadiusBoard.h"
#import "YSVerDashRadiusBoard.h"
#import "EZWaterMarkView.h"
#import "FecViewLayoutHelper.h"

#define MinimumZoomScale 1.0
#define MaximumZoomScale 4.0

@interface EZRealPlayViewController ()<EZPlayerDelegate, UIScrollViewDelegate> {
    BOOL _isPressed;///< 半双工对讲是否开启
    BOOL _isSetPasswordAlertShow;///< 输入密码alert是否显示中，有时候会回调两次，导致弹出两个验证码弹出框，加此属性确保只会出现一次
    NSInteger _streamPrevious;///< 播放接收到的码流总大小
    BOOL isDevicePtzAngleInited;///< 设备云台比例尺是否初始化过，如果是NO，接收到数据后需要初始化下
    NSDate *startTime;///< 取流开始时间
}

@property (nonatomic, assign) BOOL isOpenSound;///< 播放器声音是否开启
@property (nonatomic, assign) BOOL isPlaying;///< 播放器是否在播放中
@property (nonatomic, assign) BOOL isStartingTalk;///< 是否在启动对讲，开启预览的时候需要设置为NO
@property (nonatomic, strong) NSTimer *playTimer;///< 播放每秒接收码流大小定时器
@property (nonatomic, strong) NSTimer *recordTimer;///< 录像定时器
@property (nonatomic, assign) NSTimeInterval seconds;///< 录像时长秒数
@property (nonatomic, strong) CALayer *orangeLayer;///< 录像橙色圆点
@property (nonatomic, copy) NSString *filePath;///< 录像文件地址
@property (nonatomic, copy) NSString *filePath2;///< 双目设备第二个轨道录像文件地址
@property (nonatomic, strong) EZPlayer *player;///< 预览播放器
@property (nonatomic, strong) EZPlayer *talkPlayer;///< 对讲播放器
@property (nonatomic, strong) HIKLoadView *loadingView;///< 播放加载进程UI
@property (weak, nonatomic) MBProgressHUD *voiceHud;///< 对讲开始提示框

@property (weak, nonatomic) IBOutlet UIButton *playerPlayButton;///< 播放视图正中间播放按钮
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *playerPlayBottonCenterYConstraint;/// 播放视图正中间播放按钮 Y轴中心点
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;///< 播放视图的父视图，可以通过手指捏合实现缩放
@property (weak, nonatomic) IBOutlet UIView *playerView;///< 播放视图
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *playerViewAspectContraint;///< 画面播放窗口宽高比
@property (weak, nonatomic) IBOutlet UIView *playerViewDividLine;///< 两个播放窗口间的分割线
@property (weak, nonatomic) IBOutlet UIScrollView *secondScrollView;///< 播放视图的父视图，可以通过手指捏合实现缩放
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *secondScrollViewAspectContraint;///< 双目设备第二个画面播放窗口宽高比
@property (nonatomic, strong) NSLayoutConstraint *secondScrollViewNewAspectContraint;///< 双目设备第二个画面播放窗口宽高比16:9
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *secondScrollViewTopMarginContraint;/// 与第一个ScrollView的间距
@property (weak, nonatomic) IBOutlet UIView *secondPlayerView;///< 双目设备第二个镜头播放视图
@property (weak, nonatomic) IBOutlet UIImageView *coverImage;///< 封面图
@property (weak, nonatomic) IBOutlet UIImageView *coverImage2;///< 双目设备第二个画面的封面图

@property (weak, nonatomic) IBOutlet UILabel *localRecordLabel;///< 录像计时时长显示Label
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *localRecrodContraint;///< 录像计时时长显示Label约束

@property (weak, nonatomic) IBOutlet UIView *toolBar;///< 工具栏(开始/暂停、声音、清晰度)
@property (weak, nonatomic) IBOutlet UIButton *playButton;///< 播放/暂停
@property (weak, nonatomic) IBOutlet UIButton *voiceButton;///< 声音开关
@property (weak, nonatomic) IBOutlet UIButton *qualityButton;///< 清晰度
@property (weak, nonatomic) IBOutlet UIButton *emptyButton;///< 预览每秒接收到的码流大小显示
@property (weak, nonatomic) IBOutlet UIButton *largeButton;///< 放大
@property (weak, nonatomic) IBOutlet UIButton *largeBackButton;///< 放大返回
@property (weak, nonatomic) IBOutlet UIView *bottomView;///< 播放视图下方视图
@property (weak, nonatomic) IBOutlet UIButton *controlButton;///< 云台
@property (weak, nonatomic) IBOutlet UIButton *talkButton;///< 对讲
@property (weak, nonatomic) IBOutlet UIButton *watermarkButton;///< 水印
@property (weak, nonatomic) IBOutlet UIButton *captureButton;///< 截图
@property (weak, nonatomic) IBOutlet UIButton *localRecordButton;///< 录像
@property (weak, nonatomic) IBOutlet UIButton *captureUploadButton;///< 云录制截图上传
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *controlBtnContraint;///< 云台控制按钮y轴中心偏移量

@property (weak, nonatomic) IBOutlet UIView *ptzView;///< 云台操控父视图
@property (weak, nonatomic) IBOutlet UIButton *ptzControlButton;///< 云台操控圆盘
@property (weak, nonatomic) IBOutlet UIButton *ptzUpButton;///< 云台操控-上
@property (weak, nonatomic) IBOutlet UIButton *ptzLeftButton;///< 云台操控-左
@property (weak, nonatomic) IBOutlet UIButton *ptzDownButton;///< 云台操控-下
@property (weak, nonatomic) IBOutlet UIButton *ptzRightButton;///< 云台操控-右
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *ptzViewContraint;///<云台视图顶部距离约束
@property (nonatomic, strong) YSHorDashRadiusBoard *horDashBoard;///< 云台水平比例尺
@property (nonatomic, strong) YSVerDashRadiusBoard *verDashBoard;///< 云台垂直比例尺

@property (weak, nonatomic) IBOutlet UIView *talkView;///< 对讲父视图
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *talkViewContraint;///<对讲视图顶部距离约束
@property (weak, nonatomic) IBOutlet UIImageView *speakImageView;///< 对讲图片
@property (weak, nonatomic) IBOutlet UIView *talkVoiceChangeView;///< 对讲变声视图
@property (weak, nonatomic) IBOutlet UILabel *localLoudnessLabel;///< 本地音量大小显示Label

@property (weak, nonatomic) IBOutlet UIView *fecView;///< 鱼眼设备模式矫正视图
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *fecViewContraint;///< 鱼眼矫正顶部距离
@property (weak, nonatomic) IBOutlet UIButton *placeWallBtn;///< 壁装
@property (weak, nonatomic) IBOutlet UIButton *placeFloorBtn;///< 底装
@property (weak, nonatomic) IBOutlet UIButton *placeCeilingBtn;///< 顶装
@property (weak, nonatomic) IBOutlet UIButton *correct4PtzBtn;///< 4分屏
@property (weak, nonatomic) IBOutlet UIButton *correct5PtzBtn;///< 5分屏
@property (weak, nonatomic) IBOutlet UIButton *correctFull5PtzBtn;///< 全景5分屏
@property (weak, nonatomic) IBOutlet UIButton *correctLatBtn;///< 维度拉伸
@property (weak, nonatomic) IBOutlet UIButton *correctARCHorBtn;///< ARC水平
@property (weak, nonatomic) IBOutlet UIButton *correctARCVerBtn;///< ARC垂直
@property (weak, nonatomic) IBOutlet UIButton *correctWideAngleBtn;///< 广角
@property (weak, nonatomic) IBOutlet UIButton *correct180Btn;///< 180°全景
@property (weak, nonatomic) IBOutlet UIButton *correct360Btn;///< 360°全景
@property (weak, nonatomic) IBOutlet UIButton *correctCycBtn;///< 柱状
@property (nonatomic, strong) NSArray<UIButton *> *fecCorrectTypeButtons;

@property (weak, nonatomic) IBOutlet UILabel *largeTitleLabel;///< 标题
@property (weak, nonatomic) IBOutlet UILabel *messageLabel;///< 信息Label
@property (weak, nonatomic) IBOutlet UILabel *streamTypeLabel;///< 取流方式展示Label
@property (weak, nonatomic) IBOutlet UILabel *moreStreamInfoLabel;///< 更多码流信息开关
@property (weak, nonatomic) IBOutlet UIView *moreStreamInfoView;///< 更多码流信息父视图
@property (weak, nonatomic) IBOutlet UILabel *resolutionRatioLabel;///< 分辨率
@property (weak, nonatomic) IBOutlet UILabel *videoEncodeTypeLabel;///< 视频编码格式
@property (weak, nonatomic) IBOutlet UILabel *currentHDStatus;///< 解码方式Label
@property (weak, nonatomic) IBOutlet UILabel *zoomSizeLabel;///< 放大倍数Label

@property (nonatomic, strong) FecViewLayoutHelper *fecViewLayoutHelper;///< 鱼眼设备矫正模式视图调整辅助类
@property (nonatomic, assign) EZFecPlaceType fecPlaceType;///< 鱼眼安装模式
@property (nonatomic, assign) EZFecCorrectType fecCorrectType;///< 鱼眼矫正模式

@property (nonatomic, assign) BOOL isQulityModeAuto;///< 当前设备本地清晰度 是否是"自动"
@property (nonatomic, strong) EZCameraInfo *cameraInfo;///< 设备通道信息对象
@property (nonatomic, assign) BOOL isMultiChannelDevice;///< 是否是双目设备，如果接入的设备没有双目设备，可以忽略相关逻辑
@property (nonatomic, strong) EZDeviceDetailPublicInfo *deviceDetailPublicInfo;///< 取流详情数据

@property (nonatomic, strong) EZWaterMarkView *waterMarkView;///< 水印设置视图
@property (nonatomic, strong) EZPlayWaterMarkConfig *waterMarkConfig;///< 水印配置
@property (nonatomic, assign) BOOL isWaterMarkShow;///< 是否显示水印

@end

@implementation EZRealPlayViewController

- (instancetype)init {
    if (self = [super init]) {
        self = [EZStoryBoardTool getViewController:@"EZMain" andIdentifier:@"EZRealPlayViewController"];
    }
    return self;
}

#pragma mark - Life Cycle | 生命周期

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setData];// 数据初始化
    [self setUI];// 视图初始化设置
    [self setEZPlayer];// 播放器初始化
    /// 鱼眼设备专用设置，如果没有鱼眼设备，不需要如下代码
    [self setFecUI];
    /// 鱼眼设备专用设置，如果没有鱼眼设备，不需要如上代码
    
    [self playerStartRealPlay];// 开始播放
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.ptzViewContraint.constant = self.bottomView.frame.size.height;
    self.talkViewContraint.constant = self.fecViewContraint.constant = self.ptzViewContraint.constant;
}

- (void)viewWillDisappear:(BOOL)animated {
    // 停止对讲
    [_talkPlayer stopVoiceTalk];
    // 先停止播放（停止渲染线程），再清除水印，确保渲染线程不再访问水印数据
    if (_isPlaying) {
        [self playerStopRealPlay];
        _isPlaying = NO;
    }
    // 清除水印渲染数据
    if (self.isWaterMarkShow) {
        [_player clearWaterMarkFont];
        self.isWaterMarkShow = NO;
    }
    // 移除水印view的回调，view 跟随 VC 自然释放，不主动 removeFromSuperview
    if (self.waterMarkView) {
        self.waterMarkView.waterMarkInfoChangedBlock = nil;
        self.waterMarkView.waterMarkInfoClearBlock = nil;
        self.waterMarkView = nil;
    }
    // 结束本地录像
    if (self.localRecordButton.selected) {
        [_player stopLocalRecordExt:^(BOOL ret) {
            NSLog(@"%d", ret);
            [_recordTimer invalidate];
            _recordTimer = nil;
            self.localRecordLabel.hidden = YES;
            [self saveRecordToPhotosAlbum:_filePath];
            _filePath = nil;
            if (self.isMultiChannelDevice) {
                [self saveRecordToPhotosAlbum:_filePath2];
                _filePath2 = nil;
            }
        }];
    }
    
    NSLog(@"viewWillDisappear");
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    NSLog(@"viewDidDisappear");
    [super viewDidDisappear:animated];
    // 结束playTimer定时器
    if (_playTimer) {
        [_playTimer invalidate];
        _playTimer = nil;
    }
    // 如果正在预览，停止预览
    if (_isPlaying) {
        [self.playButton sendActionsForControlEvents:UIControlEventTouchUpInside];
    }
    // 如果存在对讲player，停止对讲
    if (_talkPlayer) {
        [_talkPlayer stopVoiceTalk];
    }
}

- (void)dealloc {
    NSLog(@"%@ dealloc", self.class);
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // 释放前先清除水印，确保后台解码线程不再访问水印配置
    [_player clearWaterMarkFont];
    [EZOPENSDK releasePlayer:_player];
    [EZOPENSDK releasePlayer:_talkPlayer];
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration {
    // 横屏
    BOOL isLandscape = toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
                       toInterfaceOrientation == UIInterfaceOrientationLandscapeRight;
    self.navigationController.navigationBarHidden = isLandscape;
    self.localRecrodContraint.constant = isLandscape ? 50 : 10;
    self.toolBar.hidden = isLandscape;
    self.largeTitleLabel.hidden = !isLandscape;
    self.largeBackButton.hidden = !isLandscape;
    self.bottomView.hidden = isLandscape;
}

#pragma mark - PlayerDelegate Methods | 播放器消息回调

/** 播放器播放失败消息回调 */
- (void)player:(EZPlayer *)player didPlayFailed:(NSError *)error {
    if ([player isEqual:_player]) {// 播放失败
        NSLog(@"player: %@, didPlayFailed: %@", player, error);
        [self playerPlayResult:NO];
        self.playerPlayButton.hidden = YES;
        self.playButton.enabled = YES;
        // 如果是需要验证码或者是验证码错误
        if (error.code == EZ_SDK_NEED_VALIDATECODE) {
            if (!_isSetPasswordAlertShow) {
                [self showSetPassword];
            }
        } else if (error.code == EZ_SDK_VALIDATECODE_NOT_MATCH) {
            [self showRetry];
        } else {
            self.messageLabel.text = [NSString stringWithFormat:@"%@(%d)",NSLocalizedString(@"device_play_fail", @"播放失败"), (int)error.code];
            self.messageLabel.hidden = NO;
            [EZToast show:[NSString stringWithFormat:@"error code: %d, userInfo: %@", (int)error.code, error.userInfo]];
        }
    } else {// 对讲失败
        [_talkPlayer stopVoiceTalk];
        [self.voiceHud hide:YES];
        [UIView animateWithDuration:0.3
                         animations:^{
            self.talkViewContraint.constant = self.bottomView.frame.size.height;
            [self.bottomView setNeedsUpdateConstraints];
            [self.bottomView layoutIfNeeded];
        } completion:^(BOOL finished) {
            self.speakImageView.alpha = 0;
            self.talkView.hidden = YES;
        }];
        if (error.code == EZ_SDK_NOT_SUPPORT_TALK) {
            [EZToast show:[NSString stringWithFormat:@"%@(%d)",NSLocalizedString(@"not_support_talk", @"设备不支持对讲"), (int)error.code]];
        } else {
            [EZToast show:[NSString stringWithFormat:@"error code: %d, userInfo: %@", (int)error.code, error.userInfo]];
        }
    }
}

/** 播放器播放成功消息回调 */
- (void)player:(EZPlayer *)player didReceivedMessage:(NSInteger)messageCode {
    NSLog(@"player: %@, didReceivedMessage: %d", player, (int)messageCode);
    if (messageCode == PLAYER_REALPLAY_START) {// 播放成功
        // 1.视图状态设置
        [self playerPlayResult:YES];
        self.messageLabel.hidden = YES;
        // 2.设置取流方式 & 更多 UI
        [self showStreamFetchType];
        self.moreStreamInfoLabel.hidden = NO;
        self.moreStreamInfoView.hidden = YES;
        // 3.获取设备详情 & 刷新视图
        [self getDeviceDetailInfoAndRefreshUI];
        // 4.声音设置 Sound setting
        if (!_isOpenSound) {
            [_player closeSound];
        }
        // 5.开启计时，每秒刷新UI
        [_playTimer invalidate];
        _playTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(refreshPlayUI) userInfo:nil repeats:YES];
        /// 鱼眼设备专用设置，如果没有鱼眼设备，不需要如下代码
        if ([FecViewLayoutHelper isFecDevice:self.deviceInfo]) {// 鱼眼设备预览矫正模式下会隐藏playerView，重新开启预览时需要重置。
            [self.fecViewLayoutHelper openFecCorrect:self.fecCorrectType fecPlaceType:self.fecPlaceType];
        }
        /// 鱼眼设备专用设置，如果没有鱼眼设备，不需要如上代码
    } else if (messageCode == PLAYER_VOICE_TALK_START) {// 对讲成功
        self.messageLabel.hidden = YES;
        /**
         非国标设备需要关闭播放器player的声音，设备和手机对讲都是通过talkPlayer来传输音频数据的，所以需要关闭player播放器的声音，否则画面码流和对讲码流中同时播放声音，导致回音(萤石设备都是萤石协议，属于非国标设备)
         国标设备不能关闭。国标设备taklPlayer只负责采集手机端的声音，设备端的声音是通过player来播放的 */
        if (self.deviceInfo.devProtoEnum == 0) {
            [_player closeSound];
        }
        [self.voiceHud hide:YES];
        [self.bottomView bringSubviewToFront:self.talkView];
        self.talkView.hidden = NO;
        self.speakImageView.alpha = 0;
        NSInteger isSupportTalk = [EZBusinessTool isSupportTalk:self.deviceInfo cameraInfo:self.cameraInfo];
        self.speakImageView.highlighted = (isSupportTalk == 1);
        self.speakImageView.userInteractionEnabled = (isSupportTalk == 3);
        [UIView animateWithDuration:0.3
                         animations:^{
            self.talkViewContraint.constant = 0;
            self.speakImageView.alpha = 1.0;
            [self.bottomView setNeedsUpdateConstraints];
            [self.bottomView layoutIfNeeded];
        } completion:^(BOOL finished) {
        }];
    } else if (messageCode == PLAYER_VOICE_TALK_END) {// 对讲结束
        // 对讲结束后把player播放器的声音重新打开
        if (_isOpenSound) {
            [_player openSound];
        }
    } else if (messageCode == PLAYER_NET_CHANGED) {// 网络变更，需要重新取流
        [self playerStopRealPlay];
        [self playerStartRealPlay];
    } else if (messageCode == PLAYER_VIDEO_LEVEL_AUTO_IMPROVE) {// 清晰度升档，EZPlayer.enableDeviceAutoVideolevel开关打开后才会有此回调
        if (_isQulityModeAuto && !self.localRecordButton.selected) {// 录制的时候不能切换，涉及到分辨率的变化，会影响转码
            NSInteger higherVideoLevel = [EZBusinessTool getHigherVideoLevel:self.cameraInfo.videoLevel deviceDetailPublicInfo:self.deviceDetailPublicInfo];
            if (higherVideoLevel > self.cameraInfo.videoLevel) {
                [self qualityAutoAction:[EZVideoQualityInfo initWithVideoQualityName:[EZBusinessTool getDeviceQualityTitle:higherVideoLevel] videoLevel:higherVideoLevel]];
            }
        }
    } else if (messageCode == PLAYER_VIDEO_LEVEL_AUTO_REDUCE) {// 清晰度降档，EZPlayer.enableDeviceAutoVideolevel开关打开后才会有此回调
        if (_isQulityModeAuto && !self.localRecordButton.selected) {
            NSInteger lowerVideoLevel = [EZBusinessTool getLowerVideoLevel:self.cameraInfo.videoLevel deviceDetailPublicInfo:self.deviceDetailPublicInfo];
            if (lowerVideoLevel < self.cameraInfo.videoLevel) {
                [self qualityAutoAction:[EZVideoQualityInfo initWithVideoQualityName:[EZBusinessTool getDeviceQualityTitle:lowerVideoLevel] videoLevel:lowerVideoLevel]];
            }
        }
    }
}

/** 设备云台角度数据回调 */
- (void)player:(EZPlayer *)player didReceivedDevicePtzAngleInfo:(EZDevicePtzAngleInfo *)info {
    if (!self.horDashBoard.hidden || !self.verDashBoard.hidden) {
        [self.horDashBoard updatePercent:[info horCurAngPercent] isSupportNoLimit:NO];
        [self.verDashBoard updatePercent:[info verCurAngPercent] animated:NO];
    }
    if (!isDevicePtzAngleInited) {
        isDevicePtzAngleInited = YES;
        [self.horDashBoard updatePercent:[info horCurAngPercent] isSupportNoLimit:NO];
        [self.verDashBoard updatePercent:[info verCurAngPercent] animated:NO];
    }
}

/**
 * 智能门锁开门token回调
 * 如果门锁设备没有回调开门token的话，请确认该设备的远程开锁开关是否打开。萤石云视频App入口：设备设置-开锁设置-远程开锁-开启远程开锁
 */
- (void)player:(EZPlayer *)player playPrivateTokenInfo:(EZPMPlayPrivateTokenInfo *)tokenInfo {
    NSLog(@"token--->%@", tokenInfo.token);
}

/** 对讲时采集时的音频大小；必须调用`EZPlayer.setVoiceTalkLoudnessInterval`设置回调时间间隔后才会有回调 */
- (void)player:(EZPlayer *)player didReceivedVoiceTalkLoudness:(float)loudness {
    NSLog(@"didReceivedVoiceTalkLoudness--->%f", loudness);
    self.localLoudnessLabel.text = [NSString stringWithFormat:@"%0.2f db", loudness];
}

#pragma mark - ValidateCode Methods | 验证码检验

/** 显示输入密码alert */
- (void)showSetPassword {
    [UIAlertController showAlertWithDeviceSerial:self.deviceInfo.deviceSerial confirmBlock:^(NSString * _Nonnull code) {
        [self.player setPlayVerifyCode:code];
        [self playerStartRealPlay];
        _isSetPasswordAlertShow = NO;
    } cancelBlock:^{
        _isSetPasswordAlertShow = NO;
    }];
    _isSetPasswordAlertShow = YES;
}

/** 显示密码错误alert */
- (void)showRetry {
    [UIAlertController showDeviceSerialRetryAlert:^{
        [self showSetPassword];
    }];
}

#pragma mark - Action Methods | 点击事件

/** 暂停/播放Action */
- (IBAction)playButtonClicked:(id)sender {
    if (_isPlaying) {
        // 暂停前获取最后一帧作为封面
        CGFloat scale = [UIScreen mainScreen].scale;
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            UIImage *image = (self.isWaterMarkShow && !self.isMultiChannelDevice) ? [_player captureRenderPictureWithWidth:self.playerView.width*scale height:self.playerView.height*scale] : [_player capturePicture:100];
            UIImage *image2;
            if (self.isMultiChannelDevice) {
                image2 = [_player capturePicture:100 streamId:1];
            }
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.coverImage setImage:image];
                if (self.isMultiChannelDevice) {
                    [self.coverImage2 setImage:image2];
                }
                [self playerPlayResult:NO];
                self.playButton.enabled = YES;
            });
        });
    } else {
        [self playerStartRealPlay];
    }
}

/** 声音控制Action */
- (IBAction)voiceButtonClicked:(id)sender {
    if (_isOpenSound) {
        [_player closeSound];
        [self.voiceButton setImage:[UIImage systemImageNamed:@"speaker.slash"] forState:UIControlStateNormal];
    } else {
        [_player openSound];
        [self.voiceButton setImage:[UIImage systemImageNamed:@"speaker.wave.2"] forState:UIControlStateNormal];
    }
    _isOpenSound = !_isOpenSound;
}

/** 清晰度列表显示Action */
- (IBAction)qualityButtonClicked:(id)sender {
    BOOL isSupportDeviceAutoVideolevel = [EZBusinessTool isSupportDeviceAutoVideolevel:self.deviceInfo cameraInfo:self.cameraInfo];
    NSString *currentVideoQuality = [EZBusinessTool getDeviceQualityTitle:_cameraInfo.videoLevel];
    // 默认清晰度
    NSArray<EZVideoQualityInfo *> *videoQualityInfos =
    @[[EZVideoQualityInfo initWithVideoQualityName:NSLocalizedString(@"device_quality_low", @"流畅") videoLevel:EZVideoLevelLow],
      [EZVideoQualityInfo initWithVideoQualityName:NSLocalizedString(@"device_quality_median", @"均衡") videoLevel:EZVideoLevelMiddle],
      [EZVideoQualityInfo initWithVideoQualityName:NSLocalizedString(@"device_quality_high", @"高清") videoLevel:EZVideoLevelHigh]];
    // 如果设备有上报清晰度数据，使用上报的数据
    if (!kArrayIsEmpty(self.deviceDetailPublicInfo.videoQualityInfos)) {
        videoQualityInfos = self.deviceDetailPublicInfo.videoQualityInfos;
    }
    [self showActionSheetWithTitle:NSLocalizedString(@"device_quality_select_tip", @"请选择设备清晰度") message:nil appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
        for (int i = 0; i < videoQualityInfos.count; i ++) {
            alertMaker.addActionDefaultTitle(videoQualityInfos[i].videoQualityName?:@"");
        }
        // 如果设备支持，清晰度列表中加入"自动"选项 或 "自动(具体清晰度)"
        if (isSupportDeviceAutoVideolevel) {
            if (_isQulityModeAuto) {
                alertMaker.addActionDefaultTitle([NSString stringWithFormat:NSLocalizedString(@"device_quality_auto_select", @"自动(%@)"), currentVideoQuality]);
            } else {
                alertMaker.addActionDefaultTitle(NSLocalizedString(@"device_quality_auto", @"自动"));
            }
        }
        alertMaker.addActionCancelTitle(NSLocalizedString(@"cancel", @"取消"));
    } actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
        // 支持自动清晰度时，会加入"自动" & "取消"选项，"取消"选项的索引即为videoQualityInfos.count + 1；不支持时，取消索引为videoQualityInfos.count
        BOOL isCancelAction = (isSupportDeviceAutoVideolevel && buttonIndex == videoQualityInfos.count + 1) || (!isSupportDeviceAutoVideolevel && buttonIndex == videoQualityInfos.count);
        if (!isCancelAction) {
            _isQulityModeAuto = isSupportDeviceAutoVideolevel && buttonIndex == videoQualityInfos.count;
            // 本地缓存设备当前状态是否是"自动清晰度"，不支持的不用管理状态
            if (isSupportDeviceAutoVideolevel) {
                [kUserDefaults setBool:_isQulityModeAuto forKey:[NSString stringWithFormat:@"QualityAuto_%@", self.deviceInfo.deviceSerial]];
                [kUserDefaults synchronize];
            }
            // 选择"自动"，重新取流，接收到通知消息后再切换清晰度；
            if (_isQulityModeAuto) {
                [self playerStopRealPlay];
                [self.player enableDeviceAutoVideoLevel];
                [self playerStartRealPlay];
            } else {// 选择具体的清晰度时，切换清晰度
                [self qualitySelectedClicked:videoQualityInfos[buttonIndex]];
            }
        }
    }];
}

/** 清晰度切换Action */
- (void)qualitySelectedClicked:(EZVideoQualityInfo *)videoQualityInfo {
    [self closeTalkView:nil];
    
    [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    // 如果是正在播放时调用该接口，设置清晰度成功以后必须让EZPlayer调用stopRealPlay再调用startRealPlay重新取流才成完成画面清晰度的切换
    [EZOPENSDK setVideoLevel:_cameraInfo.deviceSerial
                    cameraNo:_cameraInfo.cameraNo
                  videoLevel:(EZVideoLevelType)videoQualityInfo.videoLevel
                  completion:^(NSError *error) {
        [MBProgressHUD hideHUDForView:self.view animated:YES];
        if (error) {
            [self.view makeToast:[NSString stringWithFormat:@"%@", error.description]];
            return;
        }
        [self playerStopRealPlay];
        
        // 本地数据保存 需要更新之前获取到的设备列表信息，开发者自己设置
        _cameraInfo.videoLevel = videoQualityInfo.videoLevel;
        [self.qualityButton setTitle:videoQualityInfo.videoQualityName forState:UIControlStateNormal];
        
        [self playerStartRealPlay];
    }];
}

/** 自动清晰度切换Action */
- (void)qualityAutoAction:(EZVideoQualityInfo *)videoQualityInfo {
#ifdef EZVIZ_OPEN_DEMO
    [EZOPENSDK setVideoLevelAuto:_cameraInfo.deviceSerial
                        cameraNo:_cameraInfo.cameraNo
                      videoLevel:(EZVideoLevelType)videoQualityInfo.videoLevel
                      completion:^(NSError *error) {
        if (error) {
            // 失败不用处理
            return;
        }
        [self getDeviceDetailInfoAndRefreshUI];
    }];
#endif
}

/** 竖屏->横屏Action */
- (IBAction)large:(id)sender {
    /// 鱼眼设备专用设置，如果没有鱼眼设备，不需要如下代码
    if ([FecViewLayoutHelper isFecDevice:self.deviceInfo]) {
        [self fecViewShow];
        return;
    }
    /// 鱼眼设备专用设置，如果没有鱼眼设备，不需要如上代码
    [EZScreenOrientationTool landscape:self];
}

/** 横屏返回竖屏Action */
- (IBAction)largeBack:(id)sender {
    [EZScreenOrientationTool portrait:self];
}

/** 云台控制-页面显示Action */
- (IBAction)ptzViewShow:(id)sender {
    self.ptzView.hidden = NO;
    [self.bottomView bringSubviewToFront:self.ptzView];
    self.ptzControlButton.alpha = 0;
    [UIView animateWithDuration:0.3
                     animations:^{
        self.ptzViewContraint.constant = 0;
        self.ptzControlButton.alpha = 1.0;
        [self.bottomView setNeedsUpdateConstraints];
        [self.bottomView layoutIfNeeded];
    } completion:^(BOOL finished) {
    }];
}

/** 云台控制-页面关闭Action */
- (IBAction)closePtzView:(id)sender {
    [UIView animateWithDuration:0.3
                     animations:^{
        self.ptzControlButton.alpha = 0.0;
        self.ptzViewContraint.constant = self.bottomView.frame.size.height;
        [self.bottomView setNeedsUpdateConstraints];
        [self.bottomView layoutIfNeeded];
    } completion:^(BOOL finished) {
        self.ptzControlButton.alpha = 0;
        self.ptzView.hidden = YES;
    }];
}

/** 云台控制-手指按下Action */
- (IBAction)ptzControlButtonTouchDown:(id)sender {    
    EZPTZCommand command;
    NSString *imageName = nil;
    if (sender == self.ptzLeftButton) {
        self.horDashBoard.hidden = NO;
        command = EZPTZCommandLeft;
        imageName = @"ptz_left_sel";
    } else if (sender == self.ptzDownButton) {
        self.verDashBoard.hidden = NO;
        command = EZPTZCommandDown;
        imageName = @"ptz_bottom_sel";
    } else if (sender == self.ptzRightButton) {
        self.horDashBoard.hidden = NO;
        command = EZPTZCommandRight;
        imageName = @"ptz_right_sel";
    } else {
        self.verDashBoard.hidden = NO;
        command = EZPTZCommandUp;
        imageName = @"ptz_up_sel";
    }
    [self.ptzControlButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateDisabled];
    [EZOPENSDK controlPTZ:_cameraInfo.deviceSerial
                 cameraNo:_cameraInfo.cameraNo
                  command:command
                   action:EZPTZActionStart
                    speed:2
                   result:^(NSError *error) {
        NSLog(@"error is %@", error);
    }];
}

/** 云台控制-手指抬起Action */
- (IBAction)ptzControlButtonTouchUpInside:(id)sender {
    EZPTZCommand command;
    if (sender == self.ptzLeftButton) {
        command = EZPTZCommandLeft;
    } else if (sender == self.ptzDownButton) {
        command = EZPTZCommandDown;
    } else if (sender == self.ptzRightButton) {
        command = EZPTZCommandRight;
    } else {
        command = EZPTZCommandUp;
    }
    [self.ptzControlButton setImage:[UIImage imageNamed:@"ptz_bg"] forState:UIControlStateDisabled];
    [EZOPENSDK controlPTZ:_cameraInfo.deviceSerial
                 cameraNo:_cameraInfo.cameraNo
                  command:command
                   action:EZPTZActionStop
                    speed:3.0
                   result:^(NSError *error) {
        self.horDashBoard.hidden = YES;
        self.verDashBoard.hidden = YES;
    }];
}

/** 对讲Action */
- (IBAction)talkButtonClicked:(id)sender {
    NSInteger isSupportTalk = [EZBusinessTool isSupportTalk:self.deviceInfo cameraInfo:self.cameraInfo];
    if (isSupportTalk != 1 && isSupportTalk != 3 && isSupportTalk != 4) {
        [self.view makeToast:NSLocalizedString(@"not_support_talk", @"设备不支持对讲")
                    duration:1.5
                    position:@"center"];
        return;
    }
    
    EZWeak(self);
    [EZBusinessTool checkMicPermissionResult:^(BOOL enable) {
        EZStrong(self);
        dispatch_async(dispatch_get_main_queue(), ^{
            if (enable) {
                [strongself showActionSheetWithTitle:NSLocalizedString(@"device_talkback_select_tip", @"请选择对讲方式") message:nil appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
                    alertMaker.addActionDefaultTitle(NSLocalizedString(@"device_talkback_current_device", @"当前设备对讲"));
                    alertMaker.addActionDefaultTitle(NSLocalizedString(@"device_talkback_nvr_channel_device", @"NVR通道对讲"));
                    alertMaker.addActionCancelTitle(NSLocalizedString(@"cancel", @"取消"));
                } actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
                    if (buttonIndex == 2) {
                        return;
                    }
                    
                    if (!strongself.voiceHud) {
                        strongself.voiceHud = [MBProgressHUD showHUDAddedTo:strongself.view animated:YES];
                    }
                    strongself.voiceHud.labelText = NSLocalizedString(@"device_restart_talk", @"正在开启对讲，请稍候...");
                    strongself.isStartingTalk = YES;
                    NSString *verifyCode = [[GlobalKit shareKit].deviceVerifyCodeBySerial objectForKey:strongself.deviceInfo.deviceSerial];
                    if (verifyCode) {
                        [strongself.talkPlayer setPlayVerifyCode:verifyCode];
                    }
                    // 加载对讲特殊配置，一般开发者可不用配置，按需设置
                    [EZStreamConfigLoader loadVoicetalkSpecialConfigToPlayer:strongself.talkPlayer];
                    if (buttonIndex == 0) {
                        // 手表为qos对讲
                        if ([strongself.deviceInfo.category isEqualToString:@"KW1"]||[strongself.deviceInfo.deviceType isEqualToString:@"DS-3LHL10"]) {
                            [strongself.talkPlayer startVoiceTalkByQos];
                        } else {
                            [strongself.talkPlayer startVoiceTalk];
                        }
                    } else if (buttonIndex == 1) {
                        // nvr对讲，调用该方法
                        [strongself.talkPlayer startVoiceTalkNeedVoiceChannel:YES];
                    }
                }];
            } else {
                [EZToast show:NSLocalizedString(@"no_mic_permission", @"未开启麦克风权限")];
            }
        });
    }];
}

/** 半双工对讲Action */
- (IBAction)talkPressed:(id)sender {
    if (!_isPressed) {
        self.speakImageView.highlighted = YES;
        [self.talkPlayer audioTalkPressed:YES];
    } else {
        self.speakImageView.highlighted = NO;
        [self.talkPlayer audioTalkPressed:NO];
    }
    _isPressed = !_isPressed;
}

/** 对讲变声Action */
- (IBAction)talkVoiceChangeClicked:(id)sender {
    NSInteger tag = ((UIButton *)sender).tag;
    [self.talkPlayer startVoiceChange:(EZVoiceChangeType)tag complete:^(BOOL ret, NSError *error) {
        if (ret) {
            [EZToast show:@"voice change success"];
            // 更新变声按钮选中状态
            [self updateVoiceChangeButtonState:(UIButton *)sender];
        } else {
            [EZToast show:[NSString stringWithFormat:@"%ld", (long)error.code]];
        }
    }];
}

/** 全双工对讲时，设置手机端是否能听到对端的声音 */
- (IBAction)talkRemoteMuteValueChanged:(id)sender {
    [self.talkPlayer setTalkRemoteMuted:((UISwitch *)sender).on];
}

/** 对讲-页面关闭Action */
- (IBAction)closeTalkView:(id)sender {
    [_talkPlayer stopVoiceTalk];
    [UIView animateWithDuration:0.3
                     animations:^{
        self.speakImageView.alpha = 0.0;
        self.talkViewContraint.constant = self.bottomView.frame.size.height;
        [self.bottomView setNeedsUpdateConstraints];
        [self.bottomView layoutIfNeeded];
    } completion:^(BOOL finished) {
        self.speakImageView.alpha = 0;
        self.talkView.hidden = YES;
    }];
}

/** 水印Action */
- (IBAction)watermarkButtonClicked:(id)sender {
    CGFloat scale = [UIScreen mainScreen].scale;
    EZWaterMarkView *markView = [[EZWaterMarkView alloc] initWithFrame:CGRectMake(0, self.bottomView.height, kScreenWidth, self.bottomView.height) waterMarkConfig:self.waterMarkConfig playerViewSize:CGSizeMake(self.playerView.size.width * scale, self.playerView.size.height * scale)];
    EZWeak(self);
    markView.waterMarkInfoChangedBlock = ^{
        EZStrong(self);
        if (!strongself) return;
        strongself.isWaterMarkShow = YES;
        [strongself.player setWaterMarkFont:strongself.waterMarkConfig];
    };
    markView.waterMarkInfoClearBlock = ^{
        EZStrong(self);
        if (!strongself) return;
        strongself.isWaterMarkShow = NO;
        [strongself.player clearWaterMarkFont];
    };
    [self.bottomView addSubview:markView];
    self.waterMarkView = markView;
    
    [markView show:self];
}

/** 截图Action */
- (IBAction)capture:(id)sender {
    CGFloat scale = [UIScreen mainScreen].scale;
    CGFloat waterMarkWidth = self.playerView.width * scale;
    CGFloat waterMarkHeight = self.playerView.height * scale;
    BOOL isWaterMarkCapture = self.isWaterMarkShow && !self.isMultiChannelDevice;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *image = isWaterMarkCapture
            ? [self->_player captureRenderPictureWithWidth:waterMarkWidth height:waterMarkHeight]
            : [self->_player capturePicture:100];
        [self saveImageToPhotosAlbum:image];
        if (self.isMultiChannelDevice) {// 双目设备第二个播放器截图
            UIImage *image2 = [self->_player capturePicture:100 streamId:1];
            [self saveImageToPhotosAlbum:image2];
        }
    });
}

/** 云录制截图上传Action */
- (IBAction)captureUpload:(id)sender {
    UIImage *image = [_player capturePicture:100];
    [EZOpenSDK uploadSDKCloudRecordImage:UIImageJPEGRepresentation(image, 1.0) completion:^(NSString * _Nonnull storageId, NSError * _Nonnull error) {
        if (error) {
            [EZToast show:[NSString stringWithFormat:@"图片上传失败，错误码: %ld", (long)error.code]];
            return;
        }
        [self showAlertWithTitle:@"上传成功，storageId:" message:storageId appearanceProcess:^(JXTAlertController * _Nonnull alertMaker) {
            alertMaker.addActionCancelTitle(@"取消");
            alertMaker.addActionDefaultTitle(@"复制");
        } actionsBlock:^(NSInteger buttonIndex, UIAlertAction * _Nonnull action, JXTAlertController * _Nonnull alertSelf) {
            if (buttonIndex == 1) {
                UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
                pasteboard.string = storageId;
                [EZToast show:@"已复制到剪贴板"];
            }
        }];
    }];
}

/** 录像Action */
- (IBAction)localButtonClicked:(id)sender {
    // 结束本地录像
    if (self.localRecordButton.selected) {
        if (self.isWaterMarkShow && !self.isMultiChannelDevice) {// 停止水印录制
            [_player stopRenderRecord:^(BOOL ret) {
                [self stopRecord:ret];
            }];
        } else {// 停止普通录制
            [_player stopLocalRecordExt:^(BOOL ret) {
                [self stopRecord:ret];
            }];
        }
    } else {
        // 开始本地录像
        NSDate *date = [NSDate date];
        _filePath = [NSString stringWithFormat:@"%@/%@.mp4", PATH_LocalRecord, [date formattedDateWithFormat:@"yyyyMMddHHmmss"]];
        [FCFileManager createFileAtPath:_filePath overwrite:YES];
        if (self.isMultiChannelDevice) {// 双目设备第二个画面转封装文件路径会拼接上_1
            _filePath2 = [NSString stringWithFormat:@"%@/%@_1.mp4", PATH_LocalRecord, [date formattedDateWithFormat:@"yyyyMMddHHmmss"]];
            [FCFileManager createFileAtPath:_filePath2 overwrite:YES];
        }
        
        self.localRecordLabel.text = @"  00:00";
        
        if (!_recordTimer) {
            _recordTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerStart:) userInfo:nil repeats:YES];
        }
        
        if (self.isWaterMarkShow && !self.isMultiChannelDevice) {// 水印录制 && 非多目设备
            [_player startRenderRecordWithPath:_filePath];
        } else {// 普通录制
            [_player startLocalRecordWithPathExt:_filePath];
        }
        
        self.localRecordLabel.hidden = NO;
        _seconds = 0;
    }
    self.localRecordButton.selected = !self.localRecordButton.selected;
}

- (void)stopRecord:(BOOL)ret {
    NSLog(@"%d", ret);
    [_recordTimer invalidate];
    _recordTimer = nil;
    self.localRecordLabel.hidden = YES;
    [self saveRecordToPhotosAlbum:_filePath];
    _filePath = nil;
    if (self.isMultiChannelDevice) {
        [self saveRecordToPhotosAlbum:_filePath2];
        _filePath2 = nil;
    }
}

/** 鱼眼设备模式矫正-页面显示Action */
- (void)fecViewShow {
    self.fecView.hidden = NO;
    BOOL supportWallType = [FecViewLayoutHelper getSupportInt:EZFecPlaceTypeWall deviceInfo:self.deviceInfo] > 0;
    BOOL supportFloorType = [FecViewLayoutHelper getSupportInt:EZFecPlaceTypeFloor deviceInfo:self.deviceInfo] > 0;
    BOOL supportCeilingType = [FecViewLayoutHelper getSupportInt:EZFecPlaceTypeCeiling deviceInfo:self.deviceInfo] > 0;
    self.placeWallBtn.hidden = !supportWallType;
    self.placeFloorBtn.hidden = !supportFloorType;
    self.placeCeilingBtn.hidden = !supportCeilingType;
    [self setFecCorrectTypeBtnsEnable:self.fecPlaceType];
    [self.bottomView bringSubviewToFront:self.fecView];
    [UIView animateWithDuration:0.3
                     animations:^{
        self.fecViewContraint.constant = 0;
        self.fecView.alpha = 1.0;
        [self.bottomView setNeedsUpdateConstraints];
        [self.bottomView layoutIfNeeded];
    } completion:^(BOOL finished) {
    }];
}

/** 鱼眼矫正模式-页面关闭Action */
- (IBAction)closeFecView:(id)sender {
    [UIView animateWithDuration:0.3
                     animations:^{
        self.fecView.alpha = 0.0;
        self.fecViewContraint.constant = self.bottomView.frame.size.height;
        [self.bottomView setNeedsUpdateConstraints];
        [self.bottomView layoutIfNeeded];
    } completion:^(BOOL finished) {
        self.fecView.alpha = 0;
        self.fecView.hidden = YES;
    }];
}

/** 鱼眼设备安装模式Action */
- (IBAction)fecPlaceTypeAction:(id)sender {
    UIButton *btn = (UIButton *)sender;
    self.fecPlaceType = (EZFecPlaceType)btn.tag;
    [self setFecCorrectTypeBtnsEnable:self.fecPlaceType];
}

/** 根据安装模式和能力集设置哪些矫正模式可用 */
- (void)setFecCorrectTypeBtnsEnable:(EZFecPlaceType)fecPlaceType {
    // 获取安装模式对应的能力集值
    int supportValue = [FecViewLayoutHelper getSupportInt:fecPlaceType deviceInfo:self.deviceInfo];
    [FecViewLayoutHelper setFecCorrectButtonsState:self.fecCorrectTypeButtons supportValue:supportValue];
}

/** 鱼眼设备矫正模式Action */
- (IBAction)fecCorrectTypeAction:(id)sender {
    UIButton *btn = (UIButton *)sender;
    // storyboard中给每个矫正模式按钮配置好了tag值，tag=该矫正模式枚举值
    self.fecCorrectType = (EZFecCorrectType)btn.tag;
    [self.fecViewLayoutHelper openFecCorrect:self.fecCorrectType fecPlaceType:self.fecPlaceType];
}

/** 录像时长显示 & 橙色圆点显示/隐藏
 * 此处的录像时长并不准确。因为如果预览画面因为网络原因卡住了，就无法录制了，但是计时器还是在走的
 */
- (void)timerStart:(NSTimer *)timer {
    NSInteger currentTime = ++_seconds;
    self.localRecordLabel.text = [NSString stringWithFormat:@"  %02d:%02d", (int)currentTime/60, (int)currentTime % 60];
    if (!_orangeLayer) {
        _orangeLayer = [CALayer layer];
        _orangeLayer.frame = CGRectMake(10.0, 8.0, 8.0, 8.0);
        _orangeLayer.backgroundColor = [UIColor dd_hexStringToColor:@"0xff6000"].CGColor;
        _orangeLayer.cornerRadius = 4.0f;
    }
    if (currentTime % 2 == 0) {
        [_orangeLayer removeFromSuperlayer];
    } else {
        [self.localRecordLabel.layer addSublayer:_orangeLayer];
    }
}

#pragma mark - Private Methods | 其他私有方法

/** 播放器初始化 */
- (void)setEZPlayer {
    if (_cameraInfo) {
        // 1.创建预览播放器
        _player = [EZOPENSDK createPlayerWithDeviceSerial:_cameraInfo.deviceSerial cameraNo:_cameraInfo.cameraNo];
        // 2.配置播放器：设置代理，设置验证码，设置播放视图等配置
        _player.delegate = self;
        // 判断设备是否加密，加密就从demo的归档文件中获取设备验证码填入到播放器的验证码接口里
        // 本demo将验证码存储于归档文件中，可根据自己业务需求自行更改
        if (self.deviceInfo.isEncrypt) {
            NSString *verifyCode = [[GlobalKit shareKit].deviceVerifyCodeBySerial objectForKey:self.deviceInfo.deviceSerial];
            [_player setPlayVerifyCode:verifyCode];
        }
        [_player setPlayerView:_playerView];// 设置playView
        if (self.isMultiChannelDevice) {// 双目设备设置第二个playerView
            [_player setPlayerView:_secondPlayerView streamId:1];
        }
        if (_isQulityModeAuto) {
            [_player enableDeviceAutoVideoLevel];
        }
        // 设置解码方式
        [_player setHDPriority:[kUserDefaults boolForKey:[NSString stringWithFormat:@"EZVideoPlayHardDecodingStatus_%@", self.deviceInfo.deviceSerial]]];
        
        // 3.创建对讲播放器
        _talkPlayer = [EZOPENSDK createPlayerWithDeviceSerial:_cameraInfo.deviceSerial cameraNo:_cameraInfo.cameraNo];
        _talkPlayer.delegate = self;
        [_talkPlayer setVoiceTalkLoudnessInterval:0.15f];
    } else if (_url) {
        _player = [EZOPENSDK createPlayerWithUrl:_url];
    }
}

/** 播放器开始播放 */
- (void)playerStartRealPlay {
    self.isStartingTalk = NO;
    [self.playButton setImage:[UIImage systemImageNamed:@"stop.fill"] forState:UIControlStateNormal];
    self.playerPlayButton.hidden = YES;
    self.messageLabel.hidden = YES;
    [self.loadingView startSquareClcokwiseAnimation];
    
    // 加载预览特殊配置，一般开发者可不用配置，按需设置
    [EZStreamConfigLoader loadRealPlaySpecialConfigToPlayer:_player];
    
    startTime = [NSDate date];
    [_player startRealPlay];
}

/** 播放器停止播放 */
- (void)playerStopRealPlay {
    [_player stopRealPlay];
    [_playTimer invalidate];
    _playTimer = nil;
    _streamPrevious = 0;
}

/** 播放器播放 成功 / 失败 */
- (void)playerPlayResult:(BOOL)isSuccess {
    if (isSuccess && startTime) {
        NSTimeInterval duration = [startTime timeIntervalSinceNow];
        [EZToast show:[NSString stringWithFormat:@"cost: %ld ms", (long)(-duration*1000)]];
        startTime = nil;// 置空。避免播放过程中重连回调，再次显示一个超长时间
    }
    [self.loadingView stopSquareClockwiseAnimation];// 停止加载
    self.controlButton.enabled = isSuccess && [EZBusinessTool isSupportPTZ:self.deviceInfo cameraInfo:self.cameraInfo];
    self.talkButton.enabled = [EZBusinessTool isSupportTalk:self.deviceInfo cameraInfo:self.cameraInfo];// 未在预览时也能发起对讲
    self.watermarkButton.enabled = isSuccess && !self.isMultiChannelDevice;// 水印按钮
    self.captureButton.enabled = isSuccess;// 截图按钮
    self.captureUploadButton.enabled = isSuccess;// 截图按钮
    self.localRecordButton.enabled = isSuccess;// 录像按钮
    self.playerPlayButton.hidden = isSuccess;// 播放器正中间播放按钮
    self.coverImage.hidden = isSuccess;
    self.coverImage2.hidden = isSuccess && self.isMultiChannelDevice;
    // 设置水印
    if (isSuccess && self.isWaterMarkShow) {
        [self.player setWaterMarkFont:self.waterMarkConfig];
    }
    // 播放器左下角播放按钮设置
    self.playButton.enabled = isSuccess;
    NSString *normalImageName = isSuccess ? @"stop.fill" : @"play";
    [self.playButton setImage:[UIImage systemImageNamed:normalImageName] forState:UIControlStateNormal];
    
    _isPlaying = isSuccess;
    if (!isSuccess) {
        [self playerStopRealPlay];
    }
}

/** 每秒刷新播放相关UI */
- (void)refreshPlayUI {
    [self showStreamFlow];
    [self showStreamFetchType];
}

/** 显示预览每秒接收到的码流大小 */
- (void)showStreamFlow {
    NSInteger streamCurrent = [_player getStreamFlow];
    CGFloat dif = (streamCurrent-_streamPrevious)/1024.0;
    _streamPrevious = streamCurrent;
    if (dif < 0) {
        return;
    }
    
    NSString *fromatStr = @"%.1f k/s";
    if (dif > 1024) {
        dif = dif/1024;
        fromatStr = @"%.1f MB/s";
    }
    [_emptyButton setTitle:[NSString stringWithFormat:fromatStr, dif] forState:UIControlStateNormal];
}

/** 设置取流方式 */
- (void)showStreamFetchType {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        int type = [self.player getStreamFetchType];
        if (type >= 0) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.streamTypeLabel.hidden = NO;
                self.streamTypeLabel.text = [EZBusinessTool getStreamType:type];
            });
        }
    });
}

/** 显示更多码流信息 */
- (void)showStreamInfoMore {
    self.moreStreamInfoLabel.hidden = YES;
    self.moreStreamInfoView.hidden = NO;
    if (!self.deviceDetailPublicInfo) {
        return;
    }
    self.resolutionRatioLabel.text = [NSString stringWithFormat:@"resolution ratio: %ldx%ld", (long)self.deviceDetailPublicInfo.videoWidth, (long)self.deviceDetailPublicInfo.videoHeight];
    self.videoEncodeTypeLabel.text = [NSString stringWithFormat:@"videoEncode type: %@", [EZBusinessTool getVideoEncodeType:self.deviceDetailPublicInfo.videoEncodeType]];
    self.currentHDStatus.text = [NSString stringWithFormat:@"decode type: %@", [EZBusinessTool getDecodeType:[self.player getHDPriorityStatus]]];
}

/** 获取设备详情信息 & 更新UI */
- (void)getDeviceDetailInfoAndRefreshUI {
    EZWeak(self);
    [_player getDeviceDetailInfo:^(EZDeviceDetailPublicInfo *info) {
        EZStrong(self);
        strongself.deviceDetailPublicInfo = info;
        strongself.cameraInfo.videoLevel = info.videoLevel;
        [strongself.qualityButton setTitle:[EZBusinessTool getDeviceQualityTitle:_cameraInfo.videoLevel] forState:UIControlStateNormal];
        if (!self.moreStreamInfoView.hidden) {
            [strongself showStreamInfoMore];
        }
    }];
}

/** 数据初始化 */
- (void)setData {
    _isOpenSound = YES;
    self.isAutorotate = YES;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
    if ([EZBusinessTool isHubDevice:self.deviceInfo.deviceType]) {// HUB设备，普通开发者无视此分支
        _cameraInfo = [[EZCameraInfo alloc] init];
        _cameraInfo.deviceSerial = _hubCoDevSerial;
        _cameraInfo.cameraNo = _cameraIndex;
    } else {// 非HUB设备
        // 没有通道信息，则取子设备信息；如果自己的设备没有挂载在网关下的话，不用考虑子设备
        NSArray *array = self.deviceInfo.cameraInfo ? self.deviceInfo.cameraInfo : self.deviceInfo.subDeviceInfo;
        _cameraInfo = [array dd_objectAtIndex:_cameraIndex];
        BOOL isSupportDeviceAutoVideolevel = [EZBusinessTool isSupportDeviceAutoVideolevel:self.deviceInfo cameraInfo:self.cameraInfo];
        _isQulityModeAuto = isSupportDeviceAutoVideolevel && [kUserDefaults boolForKey:[NSString stringWithFormat:@"QualityAuto_%@", self.deviceInfo.deviceSerial]];
        _isMultiChannelDevice = [EZBusinessTool isSupportMultiChannel:self.deviceInfo cameraInfo:self.cameraInfo];
    }
}

- (void)applicationDidBecomeActive {
    startTime = [NSDate date];
}

/** 将图片保存到相册 */
- (void)saveImageToPhotosAlbum:(UIImage *)savedImage {
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined) {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if (status == PHAuthorizationStatusAuthorized) {
                UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            }
        }];
    } else {
        if (status == PHAuthorizationStatusAuthorized) {
            UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
        }
    }
}

/** 将录像保存到相册 */
- (void)saveRecordToPhotosAlbum:(NSString *)path {
    if (!path) {
        return;
    }
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    if (status == PHAuthorizationStatusNotDetermined) {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            if (status == PHAuthorizationStatusAuthorized) {
                UISaveVideoAtPathToSavedPhotosAlbum(path, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
            }
        }];
    } else {
        if (status == PHAuthorizationStatusAuthorized) {
            UISaveVideoAtPathToSavedPhotosAlbum(path, self, @selector(imageSavedToPhotosAlbum:didFinishSavingWithError:contextInfo:), NULL);
        }
    }
}

// 指定回调方法
- (void)imageSavedToPhotosAlbum:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSString *message = nil;
    if (!error) {
        message = NSLocalizedString(@"device_save_gallery", @"已保存至手机相册");
    } else {
        message = [error description];
    }
    NSLog(@"imageSavedToPhotosAlbum message: %@", message);
    [EZToast show:message];
}

#pragma mark - UIScrollViewDelegate

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return scrollView == self.scrollView ? self.playerView : self.secondPlayerView;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    if (scrollView.zoomScale > 1.0f) {
        self.zoomSizeLabel.text = [NSString stringWithFormat:@"%0.1fX", scrollView.zoomScale];
        self.zoomSizeLabel.hidden = NO;
        return;
    }
    self.zoomSizeLabel.hidden = YES;
}

#pragma mark - UI

- (void)setUI {
    self.ptzView.hidden = YES;
    self.talkView.hidden = YES;
    self.fecView.hidden = YES;
    self.largeBackButton.hidden = YES;
    
    self.largeTitleLabel.text = self.title;
    [self hidenWatchFunc];
    
    self.controlButton.enabled = NO;
    self.talkButton.enabled = NO;
    self.watermarkButton.enabled = NO;
    self.captureButton.enabled = NO;
    self.captureUploadButton.enabled = NO;
    self.localRecordButton.enabled = NO;
    
    [self.controlButton dd_centerImageAndTitle];
    [self.talkButton dd_centerImageAndTitle];
    [self.watermarkButton dd_centerImageAndTitle];
    [self.captureButton dd_centerImageAndTitle];
    [self.captureUploadButton dd_centerImageAndTitle];
    [self.localRecordButton dd_centerImageAndTitle];
    
    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.scrollView.mas_centerX);
        make.centerY.mas_equalTo(self.scrollView.mas_centerY);
    }];
    // 调整playerPlayButton的Y轴中心点，与loadingView居中对齐
    NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:self.playerPlayButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.loadingView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
    [self.view removeConstraint:self.playerPlayBottonCenterYConstraint];
    [self.view addConstraint:newConstraint];
    
    [self addLine];
    
    self.localRecordLabel.layer.borderColor = [UIColor whiteColor].CGColor;
    self.localRecordLabel.layer.cornerRadius = 12.0f;
    self.localRecordLabel.layer.borderWidth = 1.0f;
    self.localRecordLabel.clipsToBounds = YES;
    self.playButton.enabled = NO;
#ifdef EZVIZ_GLOBAL_DEMO
    self.talkVoiceChangeView.hidden = YES;// 对讲变声不支持海外
    self.captureUploadButton.hidden = YES;// 云录制截图上传不支持海外
    self.controlBtnContraint.constant = 100;
#endif
    
    [self setScrollViewAttribute:self.scrollView];
    [self setScrollViewAttribute:self.secondScrollView];
    
    EZWeak(self);
    [self.moreStreamInfoLabel addActionBlock:^(UIView * _Nullable view) {
        EZStrong(self);
        [strongself showStreamInfoMore];
    }];
    
    NSArray *array = self.deviceInfo.cameraInfo ? self.deviceInfo.cameraInfo : self.deviceInfo.subDeviceInfo;
    EZCameraInfo *cameraInfo = [array dd_objectAtIndex:_cameraIndex];
    self.title = [NSString stringWithFormat:@"%@", cameraInfo.cameraName];
    if (self.isMultiChannelDevice) {// 双目设备设置第二个playerView
        self.title = [NSString stringWithFormat:@"%@", kStringIsEmpty(_deviceInfo.deviceName) ? [NSString stringWithFormat:@"%@(%@)", _deviceInfo.category, _deviceInfo.deviceSerial] : _deviceInfo.deviceName];
        self.largeButton.hidden = YES;
        [self setConstraintForSecondPlayer];
    }
    
    DISPATCH_AFTER(0.1, ^{
        [self.view addSubview:self.horDashBoard];
        [self.view addSubview:self.verDashBoard];
    });
}

/** 双目设备调整约束 */
- (void)setConstraintForSecondPlayer {
    [self.loadingView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.centerY.mas_equalTo(self.scrollView.mas_bottom).offset(2.5);
    }];
    [self.messageLabel mas_updateConstraints:^(MASConstraintMaker *make) {
        make.centerY.mas_equalTo(self.scrollView.mas_bottom).offset(2.5);
    }];
    self.secondScrollView.hidden = NO;
    self.playerViewDividLine.hidden = NO;
    
    // 移除第二个scrollView原有的宽高比约束（storyboard中默认的10000:1隐藏约束）
    NSLayoutConstraint *oldConstraint = self.secondScrollViewAspectContraint;
    [self.secondScrollView removeConstraint:oldConstraint];
    
    // 添加等高约束：第二个scrollView高度等于第一个scrollView高度
    if (!self.secondScrollViewNewAspectContraint) {
        self.secondScrollViewNewAspectContraint = [NSLayoutConstraint constraintWithItem:self.secondScrollView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0];
        self.secondScrollViewNewAspectContraint.priority = UILayoutPriorityRequired;
    }
    [self.secondScrollView.superview addConstraint:self.secondScrollViewNewAspectContraint];
    
    // 调整与第一个播放窗口的间距
    self.secondScrollViewTopMarginContraint.constant = 5;
}

- (void)setScrollViewAttribute:(UIScrollView *)scrollView {
    /**
     * 画面缩放有两种方式：
     * 1.playerView外嵌套UIScrollView，利用UIScrollView的缩放特性做画面缩放。优点：实现简单，缩放画面平滑
     * 2.playerView添加手势监听，调用SDK电子放大api实现画面缩放，可参考EZPlaybackViewController.m中playerView的手势事件。 优点：对于低分辨率的画面，放大后画面比较清晰
     */
    scrollView.minimumZoomScale = MinimumZoomScale;
    scrollView.maximumZoomScale = MaximumZoomScale;
    scrollView.backgroundColor = [UIColor clearColor];
    scrollView.delegate = self;
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.userInteractionEnabled = YES;
    scrollView.multipleTouchEnabled = YES;
    scrollView.pagingEnabled = NO;
}

/** 鱼眼设备专用设置 */
- (void)setFecUI {
    if ([FecViewLayoutHelper isFecDevice:self.deviceInfo]) {
        // 鱼眼设备显示"查看模式" & 调整画面比例为1:1
        [self.largeButton setImage:[UIImage imageNamed:@"view_type_gray"] forState:UIControlStateNormal];// 鱼眼设备不支持全屏，把图标改成查看模式图标
        [self.largeButton setImage:[UIImage imageNamed:@"view_type"] forState:UIControlStateHighlighted];
        self.fecPlaceType = EZFecPlaceTypeCeiling;// demo中默认顶装
        self.fecCorrectType = EZFecCorrectTypeFish;// demo中默认鱼眼（原始码流）
        self.fecCorrectTypeButtons = @[self.correct4PtzBtn, self.correct5PtzBtn, self.correctFull5PtzBtn,
                                       self.correctLatBtn, self.correctARCHorBtn, self.correctARCVerBtn,
                                       self.correctWideAngleBtn, self.correct180Btn, self.correct360Btn,
                                       self.correctCycBtn];
        // 先创建helper，ptz1PlayView创建的时候需要使用helper
        self.fecViewLayoutHelper = [[FecViewLayoutHelper alloc] init];
        self.fecViewLayoutHelper.scrollView = self.scrollView;
        self.fecViewLayoutHelper.playerView = self.playerView;
        self.fecViewLayoutHelper.player = self.player;
        self.fecViewLayoutHelper.playerViewAspectContraint = self.playerViewAspectContraint;
        [self.fecViewLayoutHelper setPlayViewAspectRadioWith1V1];
        
        [self.scrollView addSubview:self.fecViewLayoutHelper.ptz1PlayView];
        [self.scrollView addSubview:self.fecViewLayoutHelper.ptz2PlayView];
        [self.scrollView addSubview:self.fecViewLayoutHelper.ptz3PlayView];
        [self.scrollView addSubview:self.fecViewLayoutHelper.ptz4PlayView];
        [self.scrollView addSubview:self.fecViewLayoutHelper.ptz5PlayView];
        [self.scrollView addSubview:self.fecViewLayoutHelper.ptz6PlayView];
    }
}

- (void)addLine {
    for (UIView *view in self.toolBar.subviews) {
        if ([view isKindOfClass:[UIImageView class]]) {
            [view removeFromSuperview];
        }
    }
    CGFloat averageWidth = [UIScreen mainScreen].bounds.size.width/5.0;
    UIImageView *lineImageView1 = [UIView dd_instanceVerticalLine:20 color:[UIColor grayColor]];
    lineImageView1.frame = CGRectMake(averageWidth, 7, lineImageView1.frame.size.width, lineImageView1.frame.size.height);
    [self.toolBar addSubview:lineImageView1];
    UIImageView *lineImageView2 = [UIView dd_instanceVerticalLine:20 color:[UIColor grayColor]];
    lineImageView2.frame = CGRectMake(averageWidth * 2, 7, lineImageView2.frame.size.width, lineImageView2.frame.size.height);
    [self.toolBar addSubview:lineImageView2];
    UIImageView *lineImageView3 = [UIView dd_instanceVerticalLine:20 color:[UIColor grayColor]];
    lineImageView3.frame = CGRectMake(averageWidth * 3, 7, lineImageView3.frame.size.width, lineImageView3.frame.size.height);
    [self.toolBar addSubview:lineImageView3];
    UIImageView *lineImageView4 = [UIView dd_instanceVerticalLine:20 color:[UIColor grayColor]];
    lineImageView4.frame = CGRectMake(averageWidth * 4, 7, lineImageView4.frame.size.width, lineImageView4.frame.size.height);
    [self.toolBar addSubview:lineImageView4];
}

- (void)hidenWatchFunc {
    if ([self.deviceInfo.category isEqualToString:@"KW1"]) {
        self.qualityButton.alpha = 0.5;
        self.talkButton.alpha = 0.5;
        self.controlButton.alpha = 0.5;
        
        self.qualityButton.userInteractionEnabled = NO;
        self.talkButton.userInteractionEnabled = NO;
        self.controlButton.userInteractionEnabled = NO;
    }
}

/** 更新变声按钮选中状态 */
- (void)updateVoiceChangeButtonState:(UIButton *)selectedButton {
    for (UIView *subview in self.talkVoiceChangeView.subviews) {
        if ([subview isKindOfClass:[UIButton class]]) {
            UIButton *btn = (UIButton *)subview;
            if (btn == selectedButton) {
                // 选中状态：蓝色背景，白色文字
                btn.backgroundColor = [UIColor colorWithRed:0.231 green:0.510 blue:0.965 alpha:1.0];
                [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
            } else {
                // 未选中状态：浅灰色背景，灰色文字
                btn.backgroundColor = [UIColor colorWithRed:0.94 green:0.94 blue:0.94 alpha:1.0];
                [btn setTitleColor:[UIColor colorWithRed:0.6 green:0.6 blue:0.6 alpha:1.0] forState:UIControlStateNormal];
            }
        }
    }
}

- (YSHorDashRadiusBoard *)horDashBoard {
    if (!_horDashBoard) {
        _horDashBoard = [[YSHorDashRadiusBoard alloc] initWithFrame:CGRectMake(kScreenWidth*0.5-24, self.playerView.bottom-30, 56, 20) percent:0.5];
        _horDashBoard.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleLeftMargin;
        _horDashBoard.hidden = YES;
    }
    return _horDashBoard;
}

- (YSVerDashRadiusBoard *)verDashBoard {
    if (!_verDashBoard) {
        _verDashBoard = [[YSVerDashRadiusBoard alloc] initWithFrame:CGRectMake(5, self.playerView.height*0.5-24, 20, 48) percent:0];
        _verDashBoard.autoresizingMask = UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleLeftMargin;
        _verDashBoard.hidden = YES;
    }
    return _verDashBoard;
}

- (EZPlayWaterMarkConfig *)waterMarkConfig {
    if (!_waterMarkConfig) {
        _waterMarkConfig = [[EZPlayWaterMarkConfig alloc] init];
        _waterMarkConfig.fontArray = @[@"水印信息：", @"杭州萤石网络有限公司", @"股票代码：688475"];
        _waterMarkConfig.fontAlignment = EZWaterMarkTextAlignmentCenter;
        _waterMarkConfig.fontRotateAngle = 45.f;
        _waterMarkConfig.fontColorAdapt = YES;
        _waterMarkConfig.windowAdaptMode = EZWaterMarkWindowAdaptModeFontRowColumn;
    }
    return _waterMarkConfig;
}

@end
