手把手教你視頻直播開發


#pragma mark viewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.liveRoomVC = LiveRoomVC_LiveRoomViewController;
    self.isTouchHomeEd = NO;
    NSLog(@"!!!%ld",(long)self.staruserid);
    
    [AppDelegate shareAppDelegate].isNeedReturnLiveRoom = YES;
    
    // Do any additional setup after loading the view.
    
    [self initGeneralView];  //初始化scrollView以及loadingView

    //    主播信息
    UserInfo *userInfo = [UserInfoManager shareUserInfoManager].currentUserInfo;
    if (self.staruserid > 0)//主播id
    {
        if (userInfo == nil)//游客的情況
        {
            [self initUserView];//頂部正在直播和退出
            [self initToolBar];//初始化聊天界面
        }
        else if (userInfo.userId == self.staruserid && [AppDelegate shareAppDelegate].isSelfWillLive)//主播id和用戶id一樣
        {
            [self initStarView];//自己主播
        }
        else
        {
            [self initUserView];//看別人直播
            [self initToolBar];//初始化聊天界面
        }
    }
    
    [self initRoomSetting];//初始化房間定時等信息1.后台播放音頻設置,2,進入之前先退出房間,關閉定時器,停止播放,斷開TCP。加載loadView,初始化主播信息
    
    [[SeekuSingle shareSeekuSingle] lib_audioSession_initialize];//跟lib_audioSession_uninitialize是成對出現
    
}

//initGeneralView方法初始化scrollView以及loadingView(也就是主播界面元素)

#pragma mark - 加載顯示視頻的imageView 以及背景 ,loadingView
- (void)initGeneralView
{
    //音視頻畫布
    if ([self phoneLiving]) {
        
        _liveRoomIamgeView = [[LiveRoomIamgeView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
        _liveRoomIamgeView.userInteractionEnabled = YES;
        [self.view addSubview:_liveRoomIamgeView];
        self.liveBg = [[UIImageView alloc] initWithFrame:_liveRoomIamgeView.bounds];
        self.liveBg.image = [UIImage imageNamed:@"liveBg"];
        [_liveRoomIamgeView addSubview:self.liveBg];
        
    }else{
        _videoImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
        _videoImage.backgroundColor =  [UIColor colorWithPatternImage:[UIImage imageNamed:@"livrRoomPram"]];
        //    _videoImage.contentMode = UIViewContentModeScaleAspectFill;
        _videoImage.clipsToBounds = YES;
        [self.view addSubview:_videoImage];
        self.liveBg = [[UIImageView alloc] initWithFrame:_videoImage.bounds];
        self.liveBg.image = [UIImage imageNamed:@"liveBg"];
        [_videoImage addSubview:self.liveBg];
    }
    
    
    
    
    //加載loadingView
    _liveLoadingView  = [[LiveLoadingView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    [self.view addSubview:_liveLoadingView];
    
    
    //公聊區
    _middleView = [[LiveRoomMiddleView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) showInView:self.view];
    _middleView.rootViewController = self;
    _middleView.delegate = self;
    [self.view addSubview:_middleView];
    
    
    [LiveRoomHelper shareLiveRoomHelper].rootLiveRoomViewController = self;
    

    
    
    //頭像列表
    _liveAudienceHeaderView = [[AudienceHeaderView alloc]initWithFrame:CGRectMake(SCREEN_WIDTH/2-10, 0+2, SCREEN_WIDTH -(SCREEN_WIDTH/2-10), 14+35) showInView:self.view];
    __weak typeof(self) weakself = self;
    _liveAudienceHeaderView.rootLiveRoomViewController = self;
    _liveAudienceHeaderView.audienceHeaderViewTouch = ^(UserInfo* userInfo){
        _userIdcount = userInfo.userId;
        [weakself userInfor:userInfo];
    };

    [self.view addSubview:_liveAudienceHeaderView];
    
    
    
    //底部欄
    _liveBottomView = [[LiveBottomView alloc]initView:CGRectMake(0, SCREEN_HEIGHT-35-10, SCREEN_WIDTH, 35+10) withType:[self phoneLiving]?BottomType_ZhuBo:BottomType_GuanZHong];
    _liveBottomView.userInteractionEnabled = YES;
    
    _liveBottomView.backgroundColor = [UIColor clearColor];
    
    __weak typeof(self) weakself1 = self;
    _liveBottomView.LiveBottomViewTouch = ^(NSInteger tag){
        
        if (tag==0) {//公聊
            
            [weakself1.chatToolBar.messageCotent becomeFirstResponder];
            weakself1.chatToolBar.typeKey = TypeKey_GONGLIAO;
            
        }else if (tag==1){//私聊
           
            
            if ([weakself1 showLoginDialog]){
                return ;
            }
                
            weakself1.chatToolBar.isPrivateChat = YES;
            [weakself1.priVateChatView changeFieldText];
            [weakself1.priVateChatView  hidPrivateChatViewWithisHid:NO];
            weakself1.chatToolBar.frame =  CGRectMake (0, SCREEN_HEIGHT-33 , SCREEN_WIDTH, 33);
            
        }else if (tag==2){//分享
            
            
            [weakself1.liveShareView hidSelfWithisHid:NO];
            
            
        }else if (tag==3){//禮物
            if (_liveBottomView.type == BottomType_ZhuBo) {//關閉
                [weakself1 liveEndDealog];
            }else {
                [weakself1 giftAction:nil];
            }
            
            
        }else if (tag==4){//關閉
            NSLog(@"右下角關閉按鈕");
            [weakself1 autoExitRoom];
            
        }
        
    };
    [self.view addSubview:_liveBottomView];

_liveShareView
= [[LiveShareView alloc]initWithFrame:[self phoneLiving]?CGRectMake(SCREEN_WIDTH-10-(4-2)*(35+7)+7+35/2-103/2, SCREEN_HEIGHT-_liveBottomView.frameHeight-9-6-150/2, 103, 150/2+6):CGRectMake(SCREEN_WIDTH-10-(5-2)*(35+7)+7+35/2-103/2, SCREEN_HEIGHT-_liveBottomView.frameHeight-9-6-354/2, 103, 354/2+6) showInView:self.view]; _liveShareView.rootLiveRoomViewController = self; [_liveShareView hidSelfWithisHid:YES]; _liveShareView.liveShareViewTouche = ^(NSInteger tag){ #pragma mark 分享&相機設置 if (tag==0) {//微信 if (weakself.liveBottomView.type == BottomType_ZhuBo) {//閃光燈 if (weakself.liveRoomIamgeView.isAVCaptureTorchModeOn) { [weakself showNoticeInWindow:@"閃光燈已關閉" duration:1]; [weakself.liveRoomIamgeView setFlashMode:AVCaptureTorchModeOff]; }else{ if (weakself.liveRoomIamgeView.isAVCaptureDevicePositionFront) { [weakself showNoticeInWindow:@"當前攝像頭不支持閃光燈" duration:1]; return ; } [weakself showNoticeInWindow:@"閃光燈已打開" duration:1]; [weakself.liveRoomIamgeView setFlashMode:AVCaptureTorchModeOn]; } }else{ [[LiveRoomHelper shareLiveRoomHelper] helperShareWithNumber:0 withShareTyep:@[UMShareToWechatSession] withParms:weakself1.headImageView.image]; } }else if (tag==1){//朋友圈 if (weakself.liveBottomView.type == BottomType_ZhuBo) {//翻轉 [weakself.liveRoomIamgeView rotateCamera]; }else{ [[LiveRoomHelper shareLiveRoomHelper] helperShareWithNumber:0 withShareTyep:@[UMShareToWechatTimeline] withParms:weakself1.headImageView.image]; } }else if (tag==2){//QQ [[LiveRoomHelper shareLiveRoomHelper] helperShareWithNumber:0 withShareTyep:@[UMShareToQQ] withParms:weakself1.headImageView.image]; }else if (tag==3){//QQ空間 [[LiveRoomHelper shareLiveRoomHelper] helperShareWithNumber:0 withShareTyep:@[UMShareToQzone] withParms:weakself1.headImageView.image]; }else if (tag==4){//微博 [[LiveRoomHelper shareLiveRoomHelper] helperShareWithNumber:0 withShareTyep:@[UMShareToSina] withParms:weakself1.headImageView.image]; } }; [self.view addSubview:_liveShareView]; _priVateChatView = [[PrivateChatView alloc]initWithFrame:CGRectMake(0, SCREEN_HEIGHT-570/2, SCREEN_WIDTH, 570/2) showInView:self.view]; [_priVateChatView hidPrivateChatViewWithisHid:YES]; _priVateChatView.rootLiveRoomViewController = self; [self.view addSubview:_priVateChatView]; [self.view bringSubviewToFront:self.chatToolBar]; [self initStatusView]; //頂部贊個觀眾 _liveEndView = [[LiveEndView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]; _liveEndView.rootLiveRoomController = self; _liveEndView.hidden = YES; [self.view addSubview:_liveEndView]; [self hidLiveRoomBeforLiveViewHid:YES]; if (![AppManager valueForKey:@"isLiveGuideFirst"]&&![self phoneLivingSelf]) {//引導層 UIImageView* imageViewGuide = [[ UIImageView alloc]initWithFrame:CGRectMake(0, 0, 270/2, 78/2)]; imageViewGuide.center = CGPointMake(SCREEN_WIDTH-10-(5-4)*(35+7)+7-270/2/2, SCREEN_HEIGHT-35-10-78/2/2-5); [self.view addSubview:imageViewGuide]; imageViewGuide.image =[UIImage imageNamed:@"LRLiveShare.png"]; __weak UIImageView* safeGuide = imageViewGuide; EWPButton*buttonGuide= [[EWPButton alloc]initWithFrame:CGRectMake(0, 0 , SCREEN_WIDTH, SCREEN_HEIGHT)]; __weak EWPButton* button =buttonGuide; buttonGuide.buttonBlock = ^(EWPButton* sender){ [AppManager setUserBoolValue:YES key:@"isLiveGuideFirst"]; [safeGuide removeFromSuperview]; [button removeFromSuperview]; }; [self.view addSubview:buttonGuide]; } }

//開始直播的代碼

 if (model.code == 1)
                     {
                         [stongSelf startLiving];
                         
                         if (stongSelf.isFrontCamera) {//當為前置攝像頭時候關閉閃光燈
                             [stongSelf.openFlashLight setImage:[UIImage imageNamed:@"Star_LiveRoom_lightning_no.png"] forState:UIControlStateNormal];
                             _isFlashOn  = NO;
                         }
                     }
#pragma mark- <<<<<<<<開播成功 >>開始錄制上傳視頻流
- (void)startLiving
{

    if (self.strUploadUrl.length==0) {
//        [self showNoticeInWindow:@"url為nil" duration:2];
//        NSLog(@"url為nil,退出");
//        [self autoExitRoom];
    __weak typeof(self) weakself = self;
        [self showAlertView:@"獲取視頻服務器信息失敗" message:@"是否重試" confirm:^(id sender) {
            weakself.liveRoomOtherThings = LiveRoomOtherThing_SelfLiveIpError;
            [weakself getLivingInfo];
        } cancel:^(id sender) {
             NSLog(@"獲取自己直播時,視頻服務器信息失敗,退出");
            [weakself stopPlayingautoExitRoom];
        }];
        return;
    }
    
    const char* a= [self.strUploadUrl UTF8String];
 
    int result = -1 ;
  NSString * str =[UserInfoManager shareUserInfoManager].currentStarInfo.roomfmt;
       const char * pargs =[str cStringUsingEncoding:NSASCIIStringEncoding];
    if ([[SeekuSingle shareSeekuSingle] streamInitialized])
    {
        result = [[SeekuSingle shareSeekuSingle] lib_seeku_stream_start:a args:pargs];
    }
   
    if (result<0) {
        self.beforeLiveView.buttonLiving.userInteractionEnabled = YES;
        [self showNoticeInWindow:@"獲取視頻失敗,請稍后重試" duration:2];

    }else{
        
        [_liveRoomIamgeView setCanUpLoad ];
        
        [self upLodSeeku];
    }
    
    
}
作者:鄭岐
鏈接:http://www.zhihu.com/question/36076688/answer/104440589
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

1、從推流到拉流的通道,這當中包括數據采集→數據編碼→數據傳輸(流媒體服務器) →解碼數據→播放顯示整個流程;
2、內容復制分發,也就是cdn這塊,服務器收集到主播視頻后再通過在全國各地的節點將視頻內容分發到終端。cdn是直播中最貴的,技術難度較高,一般都是采用第三方的;如果自己做的話,也需要和cdn廠商對接有經驗的技術;
3、美顏:美顏涉及到復雜的算法和圖像處理技術,美顏起初是用於圖片上,目前圖片上的美顏技術已經較為成熟,然而在視頻上的美顏還需要很長的路要走;
4、聊天室:我們在看直播的時候,還可以在聊天室中聊天,這是應用了im及時通訊中的聊天室功能,聊天室和群聊的區別是,只有用戶進入聊天室才能發言,看到好友,退出聊天室后就類似於退群,就收不到消息,看不到用戶,看不到聊天記錄了;
5、服務器:對於直播產品來說,流量變化是非常大的,一天中直播的流量高峰期基本在晚上,有時候搞個活動,或周傑倫跑來直播了,那這個時候流量可能是平時的幾十倍。流量忽高忽低對服務器自然提出了很高的要求;

二、我們肉眼上所看到的只是直播的UI層設計,一個直播產品要將聊天室和視頻呈現給觀眾,手機屏幕就這么大,可發揮的空間很有限,因此我們從UI層來看,各個直播app似乎大同小異。

三、然而直播最復雜的技術邏輯是在后端的處理上。直播的技術實現一般兩種方法,自研or使用第三方SDK,從長遠看,等到直播平台發展到像斗魚這樣的體量,自研可以節省成本。對於一個初創團隊來講,自研直播不管在技術門檻、CDN、帶寬上都是有很大的門檻的。所以,目前體量較大的直播產品也有使用第三方直播雲服務的。

四、利益相關
我們團隊是做直播技術的,底層架構都是做好的,開放給開發者sdk和api接口,開發者接入后就可以實現直播的功能。感興趣的同學qq2479775187私聊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM