#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私聊