[TOC]

日期: 2019-11-19

创盛视联数码科技(北京)有限公司

基础版说明文档

1.概述

CC基础版SDK 是一个适用于iOS平台的云课堂SDK,使用此SDK可以与CC视频云服务进行对接,在iOS端使用CC视频的云课堂功能。

1.1 功能特性

功能 描述 备注
推流 支持推流到服务器
拉流 支持从服务器订阅流
获取流状态 支持获取流的状态(发报数、收报数、丢包数、延时)
前后摄像头切换 支持手机前后摄像头切换
后台播放 支持直播退到后台只播放音频
支持https协议 支持接口https请求

1.2 阅读对象

本文档为技术文档,需要阅读者:

  1. 具备基本的iOS开发能力

  2. 准备接入CC视频的云课堂SDK相关功能

1.3 SDK架构

  1. 支持的CPU架构有armv7,arm64

  2. 支持的最低系统版本iOS9

  3. 模拟器支持:ipad air及以上版本,iphone 5s及以上版本模拟器;

2.开发准备

开发所需相关账号请咨询CC视频客户人员提供;

2.1 开发环境

  1. Xcode : iOS 开发IDE

2.2 Xcode配置

1. Build Settings   ->    Build Options  ->   Enable Bitcode   ->        NO
2. Build Settings -> Always Enbed Swift Standar Libraries ->YES
3. 工程加入云课堂SDK相关动态库
4. 添加需要的系统库:VideoToolbox.framework、libstdc++.tbd、libicucore.tbd
5. General->Embedded Binaries中添加云课堂SDK相关动态库
6. Build Settings   ->   Other Linker Flags 添加-ObjC
7. Capabilities -> Background Modes -> Audio,AirPlay,And Picture in Picture
8. Info.plist 增加Privacy - Microphone Usage Description、Privacy - Photo Library Usage Description、Privacy - Camera Usage Description

3.快速集成

注:快速集成主要提供的是推流和拉流(核心功能)。 基本的直播流程可参考Demo的相关功能函数;

首先,下载最新版本的组件化基础版SDK: 云课堂iOS组件化SDK下载

下载WebRTC库 参考发布包内SDK文件夹内提供的下载地址集成

3.1 导入framework

名称 描述
CCClassRoomBasic.framework 云课堂业务SDK
CCBarleyLibrary.framework 云课堂业务SDK
CCChatLibrary.framework 云课堂业务SDK
CCDocLibrary.framework 云课堂业务SDK
CCFuncTool.framework 云课堂业务SDK
WebRTC.framework 云课堂业务SDK
ZegoLiveRoom.framework 云课堂业务SDK
DocUI.bundle 云课堂资源库

3.2 framework添加Embedded Binaries

由于framework是动态库需要将 CCClassRoomBasic.framework、 CCBarleyLibrary.framework、 CCChatLibrary.framework、 CCDocLibrary.framework 、 CCFuncTool.framework、 WebRTC.framework 、 ZegoLiveRoom.framework 添加到Embedded Binaries

3.3 配置依赖系统库

工程需要下列系统库:libz.thd、libstdc++.thd、libicucore.thd、VideoToolBox.framework

3.4 流事件监听

3.4.1 工具类创建

在需要使用SDK的文件引入头文件

import <CCClassRoomBasic/CCClassRoomBasic.h>

创建SDK实例:

- (void)createBasic
{
      CCEncodeConfig *config = [[CCEncodeConfig alloc] init];
    config.reslution = CCResolution_LOW;

    self.streamerBasic = [CCStreamerBasic sharedStreamer];
    [self.streamerBasic addObserver:self];
}
#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;
#pragma mark - 流
- (void)onServerDisconnected
{
    WS(ws);
    [self.streamerBasic leave:^(BOOL result, NSError *error, id info){
       dispatch_async(dispatch_get_main_queue(), ^{
                 //退出当前控制器
                 [ws.navigationController popViewControllerAnimated:NO];
         });
    }];
}

3.4.2 场景一 流事件监听(不使用排麦组件)

这里需要实现 CCStreamerBasicDelegate 相关协议

1、主要协议函数如下:

/** @brief Triggers when a stream is added. */
- (void)onStreamAdded:(CCStream*)stream;

/** @brief Triggers when a stream is removed. */
- (void)onStreamRemoved:(CCStream*)stream;

2、示例如下:

//监听流事件--有流加入房间
- (void)onStreamAdded:(CCStream*)stream
{
    //todo ,订阅相关流
}

//监听流事件--有流离开房间
- (void)onStreamRemoved:(CCStream*)stream
{
    //todo ,取消订阅相关流
}

//监听流事件--流有异常
- (void)onStreamError:(NSError *)error forStream:(CCStream *)stream
{
    CCLog(@"__%@__%@", error, stream.streamID);
}

3.4.3 场景二 流事件监听(使用排麦组件)

1、这里需要实现 CCStreamerBasicDelegate 相关协议; 2、添加流事件监听;

1、协议函数如下:

//解码完成(在该函数内部进行视频渲染)
- (void)onStreamFrameDecoded:(CCStream *)stream;

2、添加流事件监听

//一、添加流事件监听
-(void)addObserver
{
//1、有流需要订阅监听
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SDKNeedsubStream:) name:CCNotiNeedSubscriStream object:nil];
//2、有流需要取消订阅监听
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SDKNeedUnsubStream:) name:CCNotiNeedUnSubcriStream object:nil];
}

//二、移除流事件监听
-(void)removeObserver
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:CCNotiNeedSubscriStream object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:CCNotiNeedUnSubcriStream object:nil];
}

3、示例如下:


#pragma mark 拉流代理
//有流需要订阅
- (void)SDKNeedsubStream:(NSNotification *)notify
{
    NSDictionary *dicInfo = notify.userInfo;
    CCStream *stream = dicInfo[@"stream"];
    //todo ,订阅相关流
}
//有流需要取消订阅
- (void)SDKNeedUnsubStream:(NSNotification *)notify
{
    NSDictionary *dicInfo = notify.userInfo;
    CCStream *stream = dicInfo[@"stream"];
    //todo ,取消订阅相关流
}

//订阅流成功后,渲染视频流(异步回调)
- (void)onStreamFrameDecoded:(CCStream *)stream
{
    //主线程更新
    dispatch_async(dispatch_get_main_queue(), ^{
         //将要展示的视频流
        CCStreamView *view = [[CCStreamView alloc] initWithStream:stream];
        //放到需要展示的地方
    });
}

3.5 加入直播间和直播间开始结束的接口

加入直播间的接口

    CCEncodeConfig *config = [[CCEncodeConfig alloc] init];
    config.reslution = CCResolution_LOW;
    //具体参见demo
    NSString *authSessionID = self.info[@"data"][@"sessionid"];
    NSString *user_id = self.info[@"data"][@"userid"];
    [self.streamer joinWithAccountID:self.viewerId sessionID:authSessionID config:config areaCode:nil events:nil completion:^(BOOL result, NSError *error, id info) {


    }];

开始直播

 [weakSelf.streamer startLive:^(BOOL result, NSError *error, id info) {
                        if (result)
                        {
                            CCLog(@"%s__%d", __func__, __LINE__);
                        }
                        else
                        {
                            [weakSelf showError:error];
                        }
                    }];

结束直播

- (void)stopLive
{
    __weak typeof(self) weakSelf = self;
    [self.streamer stopLive:^(BOOL result, NSError *error, id info) {
        if (result)
        {
            CCLog(@"%s__%d", __func__, __LINE__);
        }
        else
        {
            [weakSelf showError:error];
        }
    }];
}

3.6 推流相关调用

3.6.1 创建本地流

#pragma mark -- 创建本地流
/*!
 @method
 @abstract 创建本地流
 @param createVideo 流是否创建视频
 @param front 设备相机
 */
- (void)createLocalStream:(BOOL)createVideo cameraFront:(BOOL)front;

3.6.2 开启本地流预览

#pragma mark - 开启预览
/*!
 @method (1000)
 @abstract 开始预览
 @discussion 开启摄像头开启预览,在推流开始之前开启
 @param completion 回调
 */
- (void)startPreview:(CCComletionBlock)completion;

3.6.3 关闭本地流预览

#pragma mark - 停止预览
/*!
 @method
 @abstract 停止预览(login out 包含该操作)
 @return 操作结果
 */
- (BOOL)stopPreview:(CCComletionBlock)completion;

3.6.4 推流

- (void)publish
{
    __weak typeof(self) weakSelf = self;
    [self.streamer publish:^(BOOL result, NSError *error, id info) {
        if (result)
        {
            CCLog(@"%s__%d", __func__, __LINE__);
        }
        else
        {
            [weakSelf showError:error];
        }
    }];
}

3.6.5 结束推流

- (void)unpublish
{
    __weak typeof(self) weakSelf = self;
    [self.streamer unPublish:^(BOOL result, NSError *error, id info) {
        if (result)
        {
            CCLog(@"%s__%d", __func__, __LINE__);
        }
        else
        {
            [weakSelf showError:error];
        }
    }];
}

3.7 拉流相关掉用

3.7.1 订阅流

//1、订阅流
- (void)autoSub:(CCStream *)stream
{
    __weak typeof(self) weakSelf = self;
     [self.streamer subcribeWithStream:stream completion:^(BOOL result, NSError *error, id info) {
            if (result)
            {
                NSLog(@"sub success");
            }
            else
            {
                NSLog(@"sub fail");            
            }
        }];
}
//2、订阅流成功后,渲染视频流
//解码完成,加载视图
- (void)onStreamFrameDecoded:(CCStream *)stream
{
    //主线程更新
    dispatch_async(dispatch_get_main_queue(), ^{
         //将要展示的视频流
        CCStreamView *view = [[CCStreamView alloc] initWithStream:stream];
        //放到需要展示的地方
    });
}

3.7.2 取消订阅流

- (void)autoUnSub:(CCStream *)stream
{
    __weak typeof(self) weakSelf = self;
    [self.streamer unsubscribeWithStream:stream completion:^(BOOL result, NSError *error, id info) {
        if (result)
        {
            [weakSelf.streamView removeStreamView:info];
        }
        else
        {
            [weakSelf showError:error];
        }
    }];
}

3.8 获取城市节点列表

/*!
 @method
 @abstract 获取节点列表
 @param accountId 用户账号ID
 @param completion 回调
 @return 操作结果
 */
- (BOOL)getRoomServerWithAccountID:(NSString *)accountId completion:(CCComletionBlock)completion;

4.功能使用

4.1 预览

预览是将初始化相机的流渲染出来:

- (void)startPreview:(CCComletionBlock)completion;

4.2 开始直播

点击开始直播,成功以后,进行推流和拉流操作:

  • 开始直播方法 ```objc
  • (BOOL)startLive:(CCComletionBlock)completion; ```

    4.3 结束直播

    结束当前直播:
  • 结束直播方法 ```objc
  • (BOOL)stopLive:(CCComletionBlock)completion; ```

    4.4 推流/取消推流

推本地相机的流到服务器:

  • 推流的方法 ```objc
  • (BOOL)publish:(CCComletionBlock)completion; ```

取消推相机本地流到服务器:

  • 取消推流的方法 ```objc
  • (BOOL)unPublish:(CCComletionBlock)completion; ```

    4.5 拉流/取消拉流

从服务端拉流:

  • 拉流方法 ```objc
  • (BOOL)subcribeWithStream:(CCStream *)stream completion:(CCComletionBlock)completion; ```

  • 渲染远程流 ```objc //解码完成

  • (void)onStreamFrameDecoded:(CCStream *)stream; ```

取消从服务端拉流:

  • 取消拉流方法 ```objc
  • (BOOL)unsubscribeWithStream:(CCStream *)stream completion:(CCComletionBlock)completion; ```

4.6 开启视频/关闭视频

/*!
 @method
 @abstract 设置视频状态(开始直播之后生效)
 @param opened 视频状态
 @param userID 学生ID(为空表示操作自己的视频)

 @return 操作结果
 */
- (BOOL)setVideoOpened:(BOOL)opened userID:(NSString *)userID;

4.7 开启音频/关闭音频

/*!
 @method
 @abstract 设置音频状态(开始直播之后才生效)
 @param opened 音频状态
 @param userID 学生ID(为空表示操作自己的音频)

 @return 操作结果
 */
- (BOOL)setAudioOpened:(BOOL)opened userID:(NSString *)userID;

4.8 被动监听事件

事件监听,建议在初始化sdk后做监听

4.8.1 用户加入房间、退出房间通知

添加监听事件,具体参考集成示例工程;

- (void)receiveSocketEvent:(NSNotification *)noti
{
    CCSocketEvent event = (CCSocketEvent)[noti.userInfo[@"event"] integerValue];
    id value = noti.userInfo[@"value"];

    if(event == CCSocketEvent_UserJoin)
    {
        NSString *uname = value[@"name"];
        NSString *msg = [NSString stringWithFormat:@"<%@> 加入房间!",uname];
        [self showMessage:msg];
    }
    else if(event == CCSocketEvent_UserExit)
    {
        NSString *uname = value[@"name"];
        NSString *msg = [NSString stringWithFormat:@"<%@> 离开房间!",uname];
        [self showMessage:msg];
    }
}

4.8.2 学员举手通知(举手连麦模式)

添加监听事件,具体参考集成示例工程;

- (void)receiveSocketEvent:(NSNotification *)noti
{
    CCSocketEvent event = (CCSocketEvent)[noti.userInfo[@"event"] integerValue];
    id value = noti.userInfo[@"value"];
    NSLog(@"%s__%@__%@", __func__, noti.name, @(event));

    if(event == CCSocketEvent_UserHandUp)
    {
        NSString *name = value[@"name"];
        NSString *str = [NSString stringWithFormat:@"<%@> 举手了!",name];
        [self showMessage:str];
    }
}

4.8.3 用户自定义消息发送

添加监听事件,具体参考集成示例工程;

- (void)receiveSocketEvent:(NSNotification *)noti
{
    CCSocketEvent event = (CCSocketEvent)[noti.userInfo[@"event"] integerValue];
    id value = noti.userInfo[@"value"];
    NSLog(@"%s__%@__%@", __func__, noti.name, @(event));

    if(event == CCSocketEvent_PublishMessage)
    {
        NSString *val = value[@"value"];
        NSString *smessage = [NSString stringWithFormat:@"收到消息:%@",val];
    }
}

4.9 单条流音视频处理

//pragma mark -- 修改远程流接收状态

#pragma mark - 设置流视频的状态
/*!
 @method
 @abstract 设置流视频的状态
 @param stream  流
 @param video   视频流状态(开启/关闭)
 @param completion 成功闭包
  @return 操作结果
 */
- (BOOL)changeStream:(CCStream *)stream videoState:(BOOL)video completion:(CCComletionBlock)completion;
-
#pragma mark - 设置流音频的状态
/*!
 @method
 @abstract 设置流音频的状态
 @param stream  流
 @param audio   音频流状态(开启/关闭)
 @param completion 回调闭包
 @return 操作结果
 */
- (BOOL)changeStream:(CCStream *)stream audioState:(BOOL)audio completion:(CCComletionBlock)completion;

5.API查询

Document目录打开index.html文件

6.Q&A

6.1 运行崩溃

dyld: Library not loaded: @rpath/CCClassRoomBasic.framework/CCClassRoomBasic
  Referenced from: /var/containers/Bundle/Application/E8CDE526-6F19-415B-9BA4-2380AB0A1FDE/CCClassRoom.app/CCClassRoom
  Reason: image not found

解决办法参考3.2

results matching ""

    No results matching ""