CNLivePlayer.m 31.2 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
//
//  CNLivePlayer.m
//  CNLivePlayer_Example
//
//  Created by CNLive-zxw on 2019/7/26.
//  Copyright © 2019 153993236@qq.com. All rights reserved.
//

#import "CNLivePlayer.h"
//数据请求
#import "CNLivePlayerNetworkManager.h"
//工具
#import "CNLivePlayerTools.h"
//IM
#import <CNLiveMsgTools/CNLiveMsgManager.h>
//url
#import "CNLivePlayerUrl.h"
//缓存视频
#import <KSYHTTPCache/HTTPCacheDefines.h>
#import "CNLiveHTTPProxyService.h"
//缓存的key
#define PlayerStringToKey(a, b) [NSString stringWithFormat:@"%@:%@",a,b]

@interface CNLivePlayer () {
    NSString *_idName;
    NSString *_contentType;
    NSString *_contentId;
    
    //直播
    NSString *_activityId;
    
    // 1-流畅,2-标清,3-高清,4-超清,默认为2
    NSString *_rate;

    //音频标识
    BOOL _isAudio;
    NSString *_roomId;
    NSInteger _retryTime;
    
    //探针
    NSTimer *_statTimer;
    BOOL _preparedToPlay;
    NSInteger _playTime;
    
}
@property (nonatomic, strong, readwrite) UIView *view;
@property (nonatomic, copy) NSString *videoUrl;

@end

@implementation CNLivePlayer
//收到消息通知
NSString * const CNLivePlayerMsgReceiveNotification = @"CNLiveMsgToolsPlayerReceiveNotification";
//获取主播状态通知
NSString * const CNLiveHostStatusChangedNotification = @"CNLiveAnchorStatusChangedNotification";

#pragma mark - 初始化方法
/*
 * 初始化播放器并设置播放地址
 *
 * url     视频url
 *
 */
- (instancetype)initWithContentURL:(NSString *)url {
    self = [super init];
    if (self) {
        [UIApplication sharedApplication].idleTimerDisabled = YES;

        _contentType = @"";
        _contentId = @"";
        _idName = @"";
        _rate = @"2";
    
        [self addMovieNotificationObservers];
        NSString *proxyUrl = [[CNLiveHTTPProxyService manager] getProxyUrl:url];
        self.player = [[KSYMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:PLAYER_OpenCache?proxyUrl:url]];
        [self.view addSubview:self.player.view];
        _player.view.frame = self.view.bounds;
        _player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
        _player.shouldEnableKSYStatModule = NO;
     
    }
    
    return self;
}

/* 初始化点播播放器  初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay)
 * 码率默认2-标清
 *
 * videoId           点播视频Id
 * authSuccess       初始化成功block
 * authFailure        初始化失败block 回调一个errorInfo的字典
 *
 */
- (instancetype)initVodPlayerWithVideoId:(NSString *)videoId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock {
    if (self = [super init]) {
        if (![videoId isKindOfClass:[NSString class]] || !videoId) {
            videoId = videoId ? [NSString stringWithFormat:@"%@",videoId] : @"";
 
        }
        
        _type = CNLivePlayerVod;

        _idName = @"vid";
        _contentType = @"vod";
        _contentId = videoId;
        _isAudio = NO;

        [self createPlayerWithContentId:videoId authSuccess:authSuccessBlock authFailure:authFailureBlock];

    }
    return self;
    
}

/* 初始化直播播放器(* 以channelId播放)  初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay)
 *
 * channelId         直播视频Id (注* 若agree为YES时,必传,用于获取主播状态; 若agree为NO,可不传)
 * activityId        活动Id
 * agree             是否允许获取主播状态 YES:获取 NO:不获取
 * authSuccess       初始化成功block
 * authFailure       初始化失败block 回调一个errorInfo的字典
 *
 * result            获取主播状态 YES:获取状态成功
 *                               NO:获取失败,不影响视频播放,但是获取不到主播离开、回来、直播结束等通知。
 * 可通过- (void)getHostStatus:(void(^)(BOOL result))result;方法重新获取
 */
- (instancetype)initLivePlayerWithChannelId:(NSString *)channelId activityId:(NSString *)activityId hostStatus:(BOOL)agree authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock getHostStatusResult:(void(^)(BOOL result))result{
    if (self = [super init]) {
        if (![channelId isKindOfClass:[NSString class]] || !channelId) {
            channelId = channelId ? [NSString stringWithFormat:@"%@",channelId] : @"";
        }
        if (![activityId isKindOfClass:[NSString class]] || !activityId) {
            activityId = activityId ? [NSString stringWithFormat:@"%@",activityId] : @"";
        }
        
        _type = CNLivePlayerLive;
        
        _idName = @"cid";
        _contentType = @"live";
        _contentId = channelId;
        _activityId = [NSString stringWithFormat:@"%@",activityId];
        _isAudio = NO;
        
        if (agree) {
            //初始化IM
            [[CNLiveMsgManager sharedCNLiveMsgManager] initCNLiveMsgToolsWithAppKey:PLAYER_AppKey appId:PLAYER_AppID];

            //注册通知
            [self addHostStatusNotificationObservers];

            //加入聊天室,获取主播状态
            [self getHostStatus:result];
        }

        [self createPlayerWithContentId:channelId authSuccess:authSuccessBlock authFailure:authFailureBlock];
        
    }
    return self;
    
}

/* 初始化音频播放器  初始化成功后才能对播放器进行设置(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay)
 *
 * audioId         音频Id
 * authSuccess     初始化成功block
 * authFailure     初始化失败block 回调一个errorInfo的字典
 *
 */
- (instancetype)initAudioPlayerWithAudioId:(NSString *)audioId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock{
    if (self = [super init]) {
        if (![audioId isKindOfClass:[NSString class]] || !audioId) {
            audioId = audioId ? [NSString stringWithFormat:@"%@",audioId] : @"";
            
        }
        
        _type = CNLivePlayerAudio;

        _idName = @"vid";
        _contentType = @"vod";
        _contentId = audioId;
        _isAudio = YES;

        [self createPlayerWithContentId:audioId authSuccess:authSuccessBlock authFailure:authFailureBlock];
        
    }
    return self;
    
}

- (void)createPlayerWithContentId:(NSString *)contentId authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock{
    [UIApplication sharedApplication].idleTimerDisabled = YES;
    
    _rate = @"2";
    _retryTime = 0;
    [self addMovieNotificationObservers];
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        __strong typeof(self) strongSelf = weakSelf;
        if(strongSelf){
            [strongSelf getUrlSuccess:authSuccessBlock failure:authFailureBlock];

        }
        
    });
    
}

/* 给播放器设置内容id 设置成功后才能播放(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay)
 * 切换视频
 * contentId         内容Id(直播channelId+activityId)
 * type              内容类型
 */
- (void)setPlayerWithContentId:(NSString *)contentId type:(CNLivePlayerType)type authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock{
    if([_contentId isEqualToString:contentId]){
        return;
    }
    //暂停当前视频
    [self pause];
    _retryTime = 0;
    _type = type;
    switch (type) {
        case CNLivePlayerVod:
        {
            _idName = @"vid";
            _contentType = @"vod";
            _contentId = contentId;
            _isAudio = NO;
        }
            break;
        case CNLivePlayerLive:
        {
            _idName = @"cid";
            _contentType = @"live";
            NSArray *array = [contentId componentsSeparatedByString:@"+"];
            if(array && array.count > 1){
                _contentId = array[0];
                _activityId = array[1];
            }else {
                _contentId = contentId;
                _activityId = @"";
            }
            _isAudio = NO;
        }
            break;
        case CNLivePlayerAudio:
        {
            _idName = @"vid";
            _contentType = @"vod";
            _contentId = contentId;
            _isAudio = YES;
        }
            break;
            
    }

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        __strong typeof(self) strongSelf = weakSelf;
        if(strongSelf){
            [strongSelf getUrlSuccess:authSuccessBlock failure:authFailureBlock];

        }
        
    });
    
}

/* 给点播播放器切换码率设置内容id 设置成功后才能播放(如:自动播放、是否开启硬件解码等)和调用准备播放视频方法(prepareToPlay)
 * 切换视频清晰度
 * contentId         内容Id(如果不切换视频,只切换清晰度,可以将contentId=nil)
 * rate              码率 1-流畅,2-标清,3-高清,4-超清,默认为2
 */
- (void)setPlayerWithContentId:(NSString *)contentId rate:(NSString *)rate authSuccess:(AuthSuccessBlock)authSuccessBlock authFailure:(AuthFailureBlock)authFailureBlock {
    if([_rate isEqualToString:rate]){
        return;
    }
    if(contentId && ![contentId isEqualToString:@""]){
        _contentId = contentId;

    }
    _idName = @"vid";
    _contentType = @"vod";
    _isAudio = NO;
    _rate = rate;
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        __strong typeof(self) strongSelf = weakSelf;
        [strongSelf getUrlSuccess:authSuccessBlock failure:authFailureBlock];

    });
}

/*
 * 销毁播放器
 */
- (void)destroyPlayer {
    if (_statTimer) {
        [_statTimer invalidate];
        _statTimer = nil;
    }
    
    if (_player) {
        [self stop];
        _player = nil;
    }
    
    [self removeNotificationObservers];
    
    if (PLAYER_TestEnvironment) {
        NSLog(@"CNLivePlayer-->dealloc");
    }
    
}

- (void)dealloc {

}

#pragma mark - 查找本地是否有缓存url
- (void)getUrlSuccess:(void (^)(void))authSuccessBlock failure:(void (^)(NSDictionary *errorDic))authFailureBlock {
    id data = [[CNLivePlayerDBTool shared] searchInTable:PlayerStringToKey(_contentId,_rate)];
    CNLivePlayerDBModel *model = (CNLivePlayerDBModel *)data;
    //1.判断是否存在缓存url
    if (![model isEmpty]) {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong typeof(self) strongSelf = weakSelf;
            if(!strongSelf){
                return ;
            }
            [strongSelf createPlayerWithUrl:model.url];
            
            if (PLAYER_TestEnvironment) {
                NSLog(@"CNLivePlayer-->缓存数据,播放器初始化成功,可以对播放器进行设置并准备播放");
            }
            
            authSuccessBlock ? authSuccessBlock() : nil;
            
        });
        
    } else {
        //2.判断是否鉴权
        NSDictionary *privilegeDic = [[NSUserDefaults standardUserDefaults] objectForKey:@"kCNLivePlayerSDKAuthentication"];
        if(privilegeDic&&[privilegeDic[@"resultCode"] isEqualToString:@"0"]){
            //3.存在鉴权成功,直接请求url
            if([_contentType isEqualToString:@"vod"]){
                [self getVodVideoURLSuccess:authSuccessBlock failure:authFailureBlock];

            }else{
                [self getLiveVideoURLSuccess:authSuccessBlock failure:authFailureBlock];

            }

        }else{
            __weak typeof(self) weakSelf = self;
            //4.不存在鉴权成功,先请求鉴权
            [self authenticateSuccess:^{
                //5.鉴权成功,再请求url
                __strong typeof(self) strongSelf = weakSelf;
                if(strongSelf){
                    if([strongSelf->_contentType isEqualToString:@"vod"]){
                        [strongSelf getVodVideoURLSuccess:authSuccessBlock failure:authFailureBlock];
                        
                    }else{
                        [strongSelf getLiveVideoURLSuccess:authSuccessBlock failure:authFailureBlock];
                        
                    }
                }
               
                
            } failure:authFailureBlock];
        }
        
    }
    
}

//根据url创建播放器
- (void)createPlayerWithUrl:(NSString *)url {
    self.videoUrl = url;
    if(!self.player){
        NSString *proxyUrl = [[CNLiveHTTPProxyService manager] getProxyUrl:self.videoUrl];
        self.player = [[KSYMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:PLAYER_OpenCache?proxyUrl:self.videoUrl]];
        [self.view addSubview:self.player.view];
        self.player.view.frame = self.view.bounds;
        self.player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
        self.player.shouldEnableKSYStatModule = NO;
        
    }else{
        [self.player reset:YES];
        [self.player setUrl:[NSURL URLWithString:self.videoUrl]];
        
    }
    
}

#pragma mark - 鉴权
- (void)authenticateSuccess:(AuthSuccessBlock)authSuccessBlock failure:(AuthFailureBlock)authFailureBlock {
    
    NSDictionary *parameter = @{@"platform_id": [NSBundle mainBundle].bundleIdentifier,
                                @"timestamp": [CNLivePlayerTools getTimestamp],
                                @"appId": PLAYER_AppID};
    
    NSString *videoUrl = [CNLivePlayerTools encryptionSignWithUrl:AuthUrl parameter:parameter];
    
    __weak typeof(self) weakSelf = self;
    [[CNLivePlayerNetworkManager manager] authenticate:videoUrl success:^(NSURLResponse * _Nullable response, id  _Nullable responseObject) {
        __strong typeof(self) strongSelf = weakSelf;
        if(!responseObject||![responseObject isKindOfClass:[NSDictionary class]]){
            authFailureBlock ? authFailureBlock(responseObject) : nil;
            
        }
        
        NSDictionary *authErrorDic = @{@"code":responseObject[@"errorCode"],
                                       @"resultCode":@"0",
                                       @"errorMessage":responseObject[@"errorMessage"]};
        [[NSUserDefaults standardUserDefaults] setObject:authErrorDic forKey:@"kCNLivePlayerSDKAuthentication"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        if ([responseObject[@"errorCode"] isEqualToString:@"0"]) {
            authSuccessBlock ? authSuccessBlock() : nil;
            
        }
        else {
            if (PLAYER_TestEnvironment) {
                NSLog(@"CNLivePlayer-->播放器初始化失败,失败原因:%@", authErrorDic);
            }
            authFailureBlock ? authFailureBlock(responseObject) : nil;
            if(strongSelf){
                [CNLivePlayerTools errorStat:authErrorDic[@"errorMessage"] contentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName];
            }
            
            
        }
        
    } failure:^(NSURLResponse * _Nullable response, NSError * _Nullable error) {
        __strong typeof(self) strongSelf = weakSelf;
        NSDictionary *authErrorDic = @{@"code":[NSString stringWithFormat:@"%ld", error.code],
                                       @"resultCode":@"1",
                                       @"errorMessage":[NSString stringWithFormat:@"%@", error.localizedDescription]};
        [[NSUserDefaults standardUserDefaults] setObject:authErrorDic forKey:@"kCNLivePlayerSDKAuthentication"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
        if (PLAYER_TestEnvironment) {
            NSLog(@"CNLivePlayer-->播放器初始化失败,失败原因:%@", authErrorDic);
        }
        authFailureBlock ? authFailureBlock(authErrorDic) : nil;
        if(strongSelf){
            [CNLivePlayerTools errorStat:authErrorDic[@"errorMessage"] contentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName];
        }
        
    }];
    
}

#pragma mark - 获取点播视频url
- (void)getVodVideoURLSuccess:(AuthSuccessBlock)authSuccessBlock failure:(AuthFailureBlock)authFailureBlock {
    NSString *stamp = [CNLivePlayerTools getTimestamp];
    NSDictionary *parameter = @{@"appId": PLAYER_AppID,
                         @"vId": _contentId,
                         @"timestamp": stamp,
                         @"isHLS": @"1",
                         @"plat": @"i",
                         @"playType": _isAudio ? @"a": @"v",
                         @"platform_id": [NSBundle mainBundle].bundleIdentifier,
                         @"channelName": PLAYER_ChannelName,
                         @"rate":_rate?_rate:@"2",
                         @"tag": PLAYER_Tag,
                         @"from": @"apple",
                         @"sid": PLAYER_UserId,
                         @"uid": [CNLivePlayerTools getUidForTimestamp:stamp]};
    
    NSString *videoUrl = [CNLivePlayerTools encryptionSignWithUrl:VodUrl parameter:parameter];
    
    __weak typeof(self) weakSelf = self;
    [[CNLivePlayerNetworkManager manager] getVideoURL:videoUrl success:^(NSString * _Nullable videoURL, NSInteger code) {
        __strong typeof(self) strongSelf = weakSelf;
        if(!strongSelf){
            return ;
        }
        CNLivePlayerDBModel *model = [[CNLivePlayerDBModel alloc]initWithContenId:PlayerStringToKey(strongSelf->_contentId, strongSelf->_rate) url:videoURL];
        [[CNLivePlayerDBTool shared] insertTable:model];
        
        __weak typeof(self) weakSelf = strongSelf;
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong typeof(self) strongSelf = weakSelf;
            if(!strongSelf){
                return ;
            }
            [strongSelf createPlayerWithUrl:videoURL];
            
            [CNLivePlayerTools statSDKWithContentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName];
            
            if (PLAYER_TestEnvironment) {
                NSLog(@"CNLivePlayer-->请求数据,点播播放器初始化成功,可以对播放器进行设置并准备播放");
            }
            
            if (authSuccessBlock) {
                authSuccessBlock();
            }
            
        });
    }];
}

#pragma mark - 获取直播视频url
- (void)getLiveVideoURLSuccess:(AuthSuccessBlock)authSuccessBlock failure:(AuthFailureBlock)authFailureBlock {
    NSString *timestamp = [CNLivePlayerTools getTimestamp];
    NSDictionary *parameter = @{@"appId": PLAYER_AppID,
                                 @"channelId": _contentId,
                                 @"activityId": _activityId ? _activityId : @"",
                                 @"timestamp": timestamp,
                                 @"isHLS": @"0",
                                 @"plat": @"i",
                                 @"playType": _isAudio ? @"a": @"v",
                                 @"platform_id": [NSBundle mainBundle].bundleIdentifier,
                                 @"channelName": PLAYER_ChannelName,
                                 @"tag": PLAYER_Tag,
                                 @"from": @"apple",
                                 @"sid": PLAYER_UserId,
                                 @"uid": [CNLivePlayerTools getUidForTimestamp:timestamp]};
    
    NSString *videoUrl = [CNLivePlayerTools encryptionSignWithUrl:LiveUrl parameter:parameter];
    
    // 拿到302跳转后的播放地址
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:videoUrl]];
    [urlRequest setHTTPMethod:@"GET"];
    [urlRequest setTimeoutInterval:30];
    
    __weak typeof(self) weakSelf = self;
    [NSURLConnection sendAsynchronousRequest:urlRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        __strong typeof(self) strongSelf = weakSelf;
        if(!strongSelf){
            return ;
        }
        NSString *url = nil;
        if (connectionError) {
            NSError *error = connectionError.userInfo[NSUnderlyingErrorKey];
            NSString *videoUrl302 = error.userInfo[NSURLErrorFailingURLStringErrorKey];
            if (!videoUrl302 || ![videoUrl302 isKindOfClass:[NSString class]] || videoUrl302.length <= 0) {
                videoUrl302 = nil;
            }
            url = videoUrl302;
            
        } else if (response) {
            url = response.URL.absoluteString;
            
        } else {
            url = videoUrl;
            
        }
        CNLivePlayerDBModel *model = [[CNLivePlayerDBModel alloc]initWithContenId:PlayerStringToKey(strongSelf->_contentId, strongSelf->_rate) url:url];
        [[CNLivePlayerDBTool shared] insertTable:model];
        
        __weak typeof(self) weakSelf = strongSelf;
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong typeof(self) strongSelf = weakSelf;
            if(!strongSelf){
                return ;
            }
            [strongSelf createPlayerWithUrl:url];
            
             [CNLivePlayerTools statSDKWithContentId:strongSelf->_activityId ? strongSelf->_activityId : strongSelf->_contentId idName:strongSelf->_idName];
            
            if (PLAYER_TestEnvironment) {
                NSLog(@"CNLivePlayer-->请求数据,直播播放器初始化成功,可以对播放器进行设置并准备播放");
            }
            
            if (authSuccessBlock) {
                authSuccessBlock();
            }
            
        });
    }];
}

#pragma mark - IM
//获取主播状态(离开/已结束
- (void)getHostStatus:(void(^)(BOOL result))result {
    //连接IM服务器
    [[CNLiveMsgManager sharedCNLiveMsgManager] justOnLineSuccess:^(NSString *toolsId) {
        //加入聊天室
        [self joinChatRoomResult:result];
        
    } error:^(NSInteger errors) {
        if (result) {
            result(NO);
        }
        
    }];
    
}

//加入聊天室
- (void)joinChatRoomResult:(void(^)(BOOL result))result {
    NSString *contentId = _activityId ? _activityId : _contentId;
    if (![_contentId containsString:@"_"]) {
        NSString *spId = [[PLAYER_AppID componentsSeparatedByString:@"_"] firstObject];
        contentId = [NSString stringWithFormat:@"%@_%@", spId, _contentId];
    }
    
    _roomId = [NSString stringWithFormat:@"CNLiveAnchorStatus_%@",contentId];
    
    //加入聊天室
    [[CNLiveMsgManager sharedCNLiveMsgManager] gatherIfNullCreate:_roomId messageCount:1 success:^(NSString *targetId) {
        if (result) {
            result(YES);
        }
        
    } error:^(NSInteger errors, NSString *targetId) {
        if (result) {
            result(NO);
        }
        
    }];
    
}

#pragma mark - 注册通知
- (void)addMovieNotificationObservers {
    // 视频加载状态
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(loadStateDidChange:)
                                                 name:MPMoviePlayerLoadStateDidChangeNotification
                                               object:nil];
    // 视频播放结束
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(moviePlayBackDidFinish:)
                                                 name:MPMoviePlayerPlaybackDidFinishNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(mediaIsPreparedToPlayDidChange:)
                                                 name:MPMediaPlaybackIsPreparedToPlayDidChangeNotification
                                               object:nil];
    if(PLAYER_OpenCache){
        // 缓存状态
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(mediaCacheDidChanged:)
                                                     name:CacheStatusNotification
                                                   object:nil];
        // 缓存出错
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(mediaCacheErrorDidChanged:)
                                                     name:CacheErrorNotification
                                                   object:nil];
    }
    
}

- (void)addHostStatusNotificationObservers {
    //获取主播状态
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveMessage:) name:CNLivePlayerMsgReceiveNotification object:nil];
    
}

- (void)removeNotificationObservers {
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    if (![CNLivePlayerTools isEmptyString:_roomId]) {
        [[CNLiveMsgManager sharedCNLiveMsgManager] disperse:_roomId success:^(NSString *targetId) {
            
        } error:^(NSInteger errors, NSString *targetId) {
            
        }];
    }
    
}

#pragma mark - 通知响应方法
- (void)loadStateDidChange:(NSNotification *)notification {
    MPMovieLoadState loadState = _player.loadState;
    if (PLAYER_TestEnvironment) {
        NSLog(@"CNLivePlayer-->播放器加载状态改变MPMovieLoadState:%ld", (unsigned long)loadState);
    }
    if ((loadState & MPMovieLoadStatePlaythroughOK) != 0) {
        if (!_preparedToPlay && !_statTimer) {
            [self mediaIsPreparedToPlayDidChange:nil];
        }
        
    }
    
}

- (void)moviePlayBackDidFinish:(NSNotification *)notification {
    [UIApplication sharedApplication].idleTimerDisabled = NO;
    NSNumber *reason = [notification.userInfo valueForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];
    if (reason != nil){
        NSInteger reasonAsInteger = [reason integerValue];
        if (PLAYER_TestEnvironment) {
            NSLog(@"CNLivePlayer-->播放器播放结束原因MPMovieFinishReason:%d", reasonAsInteger);
        }
        switch (reasonAsInteger) {
            case MPMovieFinishReasonPlaybackEnded:
            {
                [_statTimer invalidate];
                _statTimer = nil;
                _playTime = 0;
            }
                break;
                
            case MPMovieFinishReasonUserExited:
            {
                [_statTimer invalidate];
                _statTimer = nil;
                _playTime = 0;
            }
                break;
                
            case MPMovieFinishReasonPlaybackError://视频异常探针
            {
                [CNLivePlayerTools errorStat:notification.userInfo.description contentId:_activityId ? _activityId : _contentId idName:_idName];
            }
                break;
                
            default:
                
                break;
        }

    }
    
    
}

- (void)mediaIsPreparedToPlayDidChange:(NSNotification *)notification {
    if (PLAYER_TestEnvironment) {
        NSLog(@"CNLivePlayer-->播放器缓冲至可播放状态");
    }
    _preparedToPlay = YES;
    if (!_contentType || _contentType.length == 0) {
        return;
    }
    if(!_statTimer){
        _statTimer = [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(statTimerAction) userInfo:nil repeats:YES];

    }
    
}

- (void)mediaCacheDidChanged:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    float progress = [userInfo[CacheProgressKey] floatValue];
    if ((NSInteger)(progress * 10000) > 10000) {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong typeof(self) strongSelf = weakSelf;
            if(strongSelf){
                [[CNLivePlayerDBTool shared] updateInTable:PlayerStringToKey(strongSelf->_contentId, strongSelf->_rate)];
            }
           
        });
    }
}


- (void)mediaCacheErrorDidChanged:(NSNotification *)notification {
    
}

- (void)receiveMessage:(NSNotification *)noti {
    
    if (!noti.userInfo[@"message"]) return;
    
    NSInteger newValue = [noti.userInfo[@"message"] intValue];
    
    if (_hostStatus == newValue) return;
    
    if (newValue == 1) {
        _hostStatus = CNLiveHostStatusLiving;
        [_player play];
        
    } else if (newValue == 2) {
        _hostStatus = CNLiveHostStatusEnd;
        [_player stop];
        
    } else {
        _hostStatus = CNLiveHostStatusLeaving;
        [_player pause];
        
    }
    
    if (PLAYER_TestEnvironment) {
        NSLog(@"CNLiveMoviePlayerController-->主播直播状态%lu",(unsigned long)_hostStatus);
    }
    
    //发送通知
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:CNLiveHostStatusChangedNotification
                                                            object:nil
                                                          userInfo:nil];
    });
    
}

#pragma mark - 播放时长计算
- (void)statTimerAction{
    if (!_contentType || _contentType.length == 0) {
        return;
    }
    _playTime++;
    
    NSString *timestamp = [CNLivePlayerTools getTimestamp];
    NSString *url = [NSString stringWithFormat:@"%@?sver=%@&uid=%@&tm=%@&from=apple&spid=&appid=%@&version=1001_%@&eventid=1001&uri=%@&sid=&plat=i_%@&objid=%@_%@", StatUrl, StatVersion, [CNLivePlayerTools getUidForTimestamp:timestamp], timestamp, PLAYER_AppID, PlayerSDKVersion, [NSString stringWithFormat:@"%ld_30", (long)_playTime], StatAppVersion,_contentId, _idName];
    [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:nil];
    
}

#pragma mark - 属性
- (NSURL *)contentURL {
    return _player.contentURL;
    
}

- (UIView *)view {
    if (!_view) {
        _view = [[UIView alloc] init];
        _view.backgroundColor = [UIColor blackColor];
    }
    return _view;
}

- (void)setShouldMute:(BOOL)shouldMute {
    _player.shouldMute = shouldMute;
}

- (void)setShouldLoop:(BOOL)shouldLoop {
    _player.shouldLoop = shouldLoop;
}

- (void)setRotateDegress:(int)rotateDegress {
    _player.rotateDegress = rotateDegress;
}

- (void)setBInterruptOtherAudio:(BOOL)bInterruptOtherAudio {
    _player.bInterruptOtherAudio = bInterruptOtherAudio;
}

- (BOOL)isPreparedToPlay {
    return _player.isPreparedToPlay;
}

- (NSTimeInterval)currentPlaybackTime{
    return _player.currentPlaybackTime;
    
}

- (void)setCurrentPlaybackTime:(NSTimeInterval)currentPlaybackTime{
    _player.currentPlaybackTime = currentPlaybackTime;
}

- (NSTimeInterval)duration{
    return _player.duration;
}

- (CGSize)naturalSize{
    return _player.naturalSize;
}

- (NSInteger)naturalRotate{
    return _player.naturalRotate;
}

- (MPMoviePlaybackState)playbackState{
    return _player.playbackState;
}

- (MPMovieLoadState)loadState{
    return _player.loadState;
}


#pragma mark - 方法
- (void)prepareToPlay {
    [_player prepareToPlay];
}

- (void)play{
    [_player play];
}

- (BOOL)isPlaying{
    return _player.isPlaying;
}

- (void)pause{
    [_player reset:YES];
    [_player pause];
}

- (void)stop{
    [_player reset:NO];
    [_player stop];
}

- (UIImage *)thumbnailImageAtCurrentTime {
    return _player.thumbnailImageAtCurrentTime;
}

- (void)reload:(NSURL *)aUrl {
    [_player reload:aUrl];
}

@end