移动直播集成(IOS版)

2020-07-07 10:21:53 浏览数 (1)

最近在腾讯云集成了一下移动直播的SDK,从0到1的整个过程,中途遇到的问题也详细地给大家列举出来,希望对将要用到腾讯云移动直播产品的开发者们,起到一点点作用。若有不足的地方欢迎指出并赐教,谢谢!

此前,移动直播开发文档有的,我再次就不再复述,大家可以按照文档的步骤,一步一步来。地址:https://cloud.tencent.com/document/product/454/7876。首先,介绍一下,我主要使用这个SDK做了哪些功能:

截屏2020-07-06 下午10.44.39.png截屏2020-07-06 下午10.44.39.png

其次,下载官网的SDK(https://github.com/tencentyun/MLVBSDK),根据官网配置好需要的 License和APPID等等。那么开始集成SDK,将Demo中的Common和LiveRoom文件拖到你的工程中

截屏2020-07-06 下午10.53.27.png截屏2020-07-06 下午10.53.27.png

command b 运行,注意(这里的 Bundle ID必须和你工程的 Bundle ID一直,否则报License url不匹配)。

那么问题来了,首先将TCBeautyPanel这个工程拖进来,会提示一下错误。(在Common->TCBeautyPanel目录下)

viewfile.pngviewfile.png

产生错误:

Multiple commands produce '/Users/chenrongke/Library/Developer/Xcode/DerivedData/BigWork-ewykfxpdwrmuqjayrhykmhghoeiq/Build/Products/Debug-iphoneos/BigWork.app/Info.plist':

1) Target 'BigWork' (project 'BigWork') has copy command from '/Users/chenrongke/Desktop/BigWork/BigWork/Common/TCBeautyPanel/Resources/Info.plist' to '/Users/chenrongke/Library/Developer/Xcode/DerivedData/BigWork-ewykfxpdwrmuqjayrhykmhghoeiq/Build/Products/Debug-iphoneos/BigWork.app/Info.plist'

解决办法:xcode的file->Workspace Settings->New Build System 修改为legcay Build System

第二个错误:(导致出现异常的原因是因为工程中添加了一些.c文件(第三方开源解压缩库))

viewfile-1.pngviewfile-1.png

解决办法:将Build Setting ->Compile Sources As 改为 Objective-C

由于修改所有文件的编译类型,所有可能会导致其他包括c、c 代码的提示错误,不过都是些的提示异常,按提示修改即可。

注意,这里我们使用MLVBLiveRoom组件,需要考虑AFNetworking版本,最新版本的请求方法有变化,组件使用的是旧版本的,我这里指定 pod 'AFNetworking','~> 3.2.1’还可以正常使用。(当然你也可以使用最新AFN但需要修改MLVBLiveRoom组件里被AF废弃了的方法),这样,我们继承SDK运行就没有其他报错了,可以进行功能的开发了。

登录&直播间:

1、通过GET方法在@“https://room.zijiebao.com/weapp/utils/get_login_info_debug”的链接请求到

sdkAppID、userSig、userID

然后通过获取到的数据初始化MLVBLoginInfo,为初始化LiveRoom做准备

代码语言:txt复制
// 输入代码内容
- (void)login{
   __block NSString *userID = [[NSUserDefaults standardUserDefaults] objectForKey:@"userID"];
    NSString *strCgiUrl = kHttpServerAddr_GetLoginInfo;
    if (userID != nil && userID.length > 0) {
        strCgiUrl = [NSString stringWithFormat:@"%@?userID=%@",kHttpServerAddr_GetLoginInfo,userID];
    }
    // 从后台获取随机产生的userID,以及IMSDK所需要的appid、account_type, sig等信息
    AFHTTPSessionManager *httpSession = [AFHTTPSessionManager manager];
    [httpSession setRequestSerializer:[AFJSONRequestSerializer serializer]];
    [httpSession setResponseSerializer:[AFJSONResponseSerializer serializer]];
    httpSession.requestSerializer.timeoutInterval = 5.0;
    httpSession.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/xml", @"text/plain", nil];
    __weak __typeof(self) weakSelf = self;
    __weak AFHTTPSessionManager *weakManager = httpSession;
    [httpSession GET:strCgiUrl parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [weakManager invalidateSessionCancelingTasks:NO];
        int errCode = [responseObject[@"code"] intValue];
        NSString *errMsg = responseObject[@"message"];
        NSNumber * sdkAppID = responseObject[@"sdkAppID"];
        NSString * userSig = responseObject[@"userSig"];
        if (errCode != 0) {
            NSLog(@"request login info failed: errCode[%d] errMsg[%@]", errCode, errMsg);
            [weakSelf.view crk_MakeToast:errMsg duration:2 position:CSToastPositionCenter];
            [self onLoginFailed];
            return;
        }
        
        if (userID == nil || userID.length == 0) {
            userID = responseObject[@"userID"];
            if (userID == nil || userID.length == 0) {
                NSLog(@"request login info failed: invalid userID");
                [weakSelf.view crk_MakeToast:@"用户账号非法" duration:2 position:CSToastPositionCenter];
                [self onLoginFailed];
                return;
            }else{
                [[NSUserDefaults standardUserDefaults] setObject:userID forKey:@"userID"];
            }
        }
        
        _userID = userID;

        MLVBLoginInfo *loginInfo = [MLVBLoginInfo new];
        loginInfo.sdkAppID = [sdkAppID intValue];
        loginInfo.userID = userID;
        loginInfo.userName = _userName;
        loginInfo.userAvatar = @"headpic.png";
        loginInfo.userSig = userSig;
        
        // 初始化LiveRoom
        [weakSelf.liveRoom loginWithInfo:loginInfo completion:^(int errCode, NSString *errMsg) {
            __strong __typeof(weakSelf) self = weakSelf; if (nil == self) return;
            dispatch_async(dispatch_get_main_queue(), ^{
//                self->_createBtn.enabled = YES;
            });
            NSLog(@"init LiveRoom errCode[%d] errMsg[%@]", errCode, errMsg);
            if (errCode == 0) {
                //登录成功,获取直播间列表
                [self getRoomList];
//                weakSelf.initSucc = YES;
            } else {
                [self onLoginFailed];
                [weakSelf.view crk_MakeToast:@"LiveRoom init失败" duration:2 position:CSToastPositionCenter];
            }
        }];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"request login info failed: err[%@]", [error description]);
        [weakSelf.view crk_MakeToast:@"网络请求超时,请检查网络设置" duration:2 position:CSToastPositionCenter];
        [self onLoginFailed];
        [weakManager invalidateSessionCancelingTasks:NO];
    }];
}

2、调用liveRoom loginWithInfo:loginInfo completion:^(int errCode, NSString *errMsg)的方法

进行 Room登录和IM初始化及登录,登录失败再次手动登录

3、登录成功,调用_liveRoom getRoomList:0 count:100 completion:^(int errCode, NSString errMsg, NSArray<MLVBRoomInfo > *roomInfoArray)方法获取房间列表

代码语言:txt复制
//获取直播间列表
- (void)getRoomList{
    [_liveRoom getRoomList:0 count:100 completion:^(int errCode, NSString *errMsg, NSArray<MLVBRoomInfo *> *roomInfoArray) {
        NSLog(@"getRoomList errCode[%d] errMsg[%@]", errCode, errMsg);
        self.roomList = roomInfoArray;
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tbvMicro reloadData];
        });
    }];
}

创建直播间:

1、使用[_liveRoom createRoom:self.userID roomInfo:_roomName completion:^(int errCode, NSString *errMsg)

创建一个直播间

2、开始推流和本地预览

_liveRoom startLocalPreview:YES view:_pusherView;(使用demo控件MLVBLiveRoom)

实际上是通过TXLivePushConfig初始化TXLivePush的推流,_livePusher startPreview:view使用这个方法进行推流

3、直播间相关功能相机转换、美颜、聊天、PK、静音

代码语言:txt复制
相机转换: [_liveRoom switchCamera];( [_livePusher switchCamera] )
代码语言:txt复制
静音: [self.liveRoom muteLocalAudio:YES];([_livePusher setMute:mute])
代码语言:txt复制
聊天:(文字聊天)使用MLVBRoom中的model LiveRoomMsgModel存储消息的详情(消息类型,用户名,昵称,时间,高度)
代码语言:txt复制
消息发送接口[_liveRoom sendRoomTextMsg:textMsg completion:nil];(自定义的消息sendRoomCustomMsg 接口)

接收消息回调:onRecvRoomTextMsg:(NSString *)roomID userID:(NSString *)userID userName:(NSString *)userName userAvatar:(NSString *)userAvatar message:(NSString *)message接收方接收到消息,刷新UI( onRecvRoomCustomMsg 自定义消息回调)

直播美颜滤镜:

美颜:美颜光滑、美颜自然、p图、美白、红润……等

我这里没有用Demo里面的TCBeautyPanel的库,文档描述使用: TXLivePush 中的setBeautyStyle接口,

但这个接口已经被废弃了,推荐使用TXBeautyManager这个类来设置。(getBeautyManager这个方法可以

获取到,liveRoom和TXLivePush 都有这个同名方法)

通过setBeautyStyle方法给出一个磨皮的算法,然后TXBeautyManager里面开发了很多美颜的方法,这里就不

一一展示了(在TXBeautyManager.h文件中,你可以清楚的看到美颜的各种方法以及他的功能)

代码语言:txt复制
// 输入代码内容
/**
 * 美颜(磨皮)算法
 * SDK 内置了多种不同的磨皮算法,您可以选择最适合您产品定位的方案。
 */
typedef NS_ENUM(NSInteger, TXBeautyStyle) {
    TXBeautyStyleSmooth    = 0,  ///< 光滑,适用于美女秀场,效果比较明显。
    TXBeautyStyleNature    = 1,  ///< 自然,磨皮算法更多地保留了面部细节,主观感受上会更加自然。
    TXBeautyStylePitu      = 2   ///< 企业版美颜算法,仅在 [企业版 SDK](https://cloud.tencent.com/document/product/647/32689#Enterprise) 中生效
};
/**
 * 设置美颜(磨皮)算法
 *
 * SDK 内部集成了两套风格不同的磨皮算法,一套我们取名叫“光滑”,适用于美女秀场,效果比较明显。
 * 另一套我们取名“自然”,磨皮算法更多地保留了面部细节,主观感受上会更加自然。
 *
 * @param beautyStyle 美颜风格,光滑或者自然,光滑风格磨皮更加明显,适合娱乐场景。
 */
- (void)setBeautyStyle:(TXBeautyStyle)beautyStyle;
/**
 * 设置美颜级别
 * @param level 美颜级别,取值范围0 - 9; 0表示关闭,1 - 9值越大,效果越明显。
 */
- (void)setBeautyLevel:(float)level;

滤镜:使用TXBeautyManager 的setFilter方法,要求png图片进行滤镜效果,这里遇到了一个问题,我是直接

用了App里面展示的图片进行渲染,导致效果发生严重的偏差。后来看了文档,是我获取到的照片出错,

应该拿到demo中FilterResource.bundle同名的图片。

截屏2020-07-06 下午11.32.26.png截屏2020-07-06 下午11.32.26.png
代码语言:txt复制
// 错误的示范
}else if ([stytle isEqualToString:@"滤镜"]){
        
        [self.beautyMng setFilter:[UIImage imageNamed:@"bailan.png"]];
    }else if ([stytle isEqualToString:@"动效"]){

}

//改正后
}else if ([stytle isEqualToString:@"滤镜"]){
        NSString * path = [[NSBundle mainBundle] pathForResource:@"FilterResource" ofType:@"bundle"];
        path = [path stringByAppendingPathComponent:[dict objectForKey:@"img"]];

        UIImage *image = [UIImage imageWithContentsOfFile:path];
        [self.beautyMng setFilter:image];
    }else if ([stytle isEqualToString:@"动效"]){
        
    }
截屏2020-07-06 下午11.39.30.png截屏2020-07-06 下午11.39.30.png

观众互动(连麦):

我这边连麦的功能只有在观众的界面,整个过程就是:观众进入(enterRoom)加入了IM Group,同时也

进行了拉流的操作(TXLivePlayer),然后发起连麦(liveroom requestJoinAnchor)

连麦逻辑:开始连麦是需要判断主播是否正在连麦或者PK中,只有主播处于空闲状态下才提示主播连

麦请求,开始推流(startLocalPreview)。主播收到连麦的请求(onRequestJoinAnchor),通过

responseJoinAnchor处理这个请求。观众收到主播连麦处理回调(joinAnchor),同意:然后双方进行推流

拉流,不同意,观众立即停止推流。停止连麦onKickoutJoinAnchor,观众停止推流,改变UI,主播停止

拉流修改UI

这里遇到一个问题:进行连麦的时候,观众端观看主播卡死,而主播端也不能刷出观众的连麦视频页面

(两边的推流都出现了问题,并过了一段时间,自动停止推流)。查看了房间监控的直播流量监控,发现看到连麦后,推流就为0帧了。(查看工具:http://dev.video.isd.com)

主要是因为网络的问题,网络不稳定,而我们的推流的画面质量太高,数据传输不过来。服务器70秒内无

法收到帧数据,直接断开直播放,日志打印网络超时。后面修改成了最低的画面质量就可以了。

主播PK:

逻辑:首先判断当前主播是否已经在PK或连麦中,如果是就停止PK quitRoomPK,否则获取主播列表

getRoomList(过滤掉当前主播)。选择某个主播进行PK(requestRoomPK),等待对方回应。对方主播收到onRequestRoomPK请求,调用responseRoomPK进行回应。拒接则当前主播一个提示。

同意则进行PK,同时布局UI,liveRoom的startRemoteView接口播放邀约主播的流。

这里还遇到了一个问题,主播PK一端断开了,另一端退出不了,quitRoomPK:返回错误-6(房间不存在)

原因:没有使用onQuitRoomPK的回调方法,没有移除pk的界面,再次推出pk房间已经不存在了

总结&问题:

遇到的问题:

1、在连麦和PK卡死的情况下,通过TXLivePushConfig 修改videoBitrateMin 和videoBitrateMax ,尝试通过修改码率来改变画质,但是没有效果。(通过监控器可以看出,码率确实是不在自定义的范围内)

原因:在自定义码率完成后,我还调用了TXLivePush 的setVideoQuality方法来设置画质(sdk推荐使用),

然后码率就会被重新配置,码率在1200到1800之间(好像是高清的画质),所以,两者只能设置其一,因为

不管哪个在前面都会被后者覆盖

2、 TXCAudioCore.mm, restart, 1969:AudioCenter: retry to start augraph after starting augraph failed, 系统异常,录音失败-10874,直播只有画面没有声音

解决方法:重新pod一下 pod 'TXLiteAVSDK_Professional', :podspec => 'http://pod-1252463788.cosgz.myzijiebao.com/liteavsdkspec/TXLiteAVSDK_Professional.podspec'

就好了,原因暂时不明。百度时查找到相似的问题,说移除AVAudioSession sharedInstance相关方法,或者添加一些关于音频的库。

3、使用MLVBLiveRoom组件,需要考虑AFNetworking版本,最新版本的请求方法有变化,组件使用的是旧版本的,我这里指定 pod 'AFNetworking','~> 3.2.1’还可以正常使用。

往后还会有更多的关于腾讯云音视频集成的相关问题,请大家多多关注。有不对的地方也请开发者们多提意见,谢谢大家!

0 人点赞