音视频通话 2.0
iOS
新手接入指南
产品简介
产品介绍
功能特性
产品优势
应用场景
基本概念
使用限制
性能指标
产品计费
按量计费
资源包
更新日志
体验 Demo
下载 SDK 和 示例代码
快速开始
快速跑通 Sample Code
接入流程
创建应用
开通服务
集成 SDK
实现音视频通话
Token 鉴权
高级 Token 鉴权
基础功能
设置音频属性
设置视频属性
设置视频旋转方向
设置通话音量
屏幕共享
通话前网络质量探测
监测发言者音量
通话中质量监测
进阶功能
音频管理
客户端音频录制
原始音频数据
美声变声与混响
耳返
自定义音频采集与渲染
音效与伴音
设置音频订阅优先级
音频裸流传输
媒体补充增强信息
视频管理
视频截图
水印
云信美颜
相芯美颜
自定义视频采集
虚拟背景
视频图像畸变矫正
视频裸流传输
加入多房间
设备管理
视频设备管理
音频设备管理
媒体流管理
媒体流加密
跨房间媒体流转发
视频流回退
云端录制
使用云代理
AI 融合功能
AI 降噪
AI 超分
场景实践
语聊房
PK连麦
在线教育
最佳实践
音视频参数配置推荐
房间连接状态管理
实现音视频安全检测
轻松构建本土Clubhouse
Audio Session 使用建议
API 参考
iOS API 参考
服务端 API
错误码
控制台指南
常见问题处理
FAQ
错题集
获取音频 Dump 文件
音频常见问题排查
视频常见问题排查
服务协议

实现音视频通话

更新时间: 2023/02/23 10:30:27

网易云信音视频通话产品的基本功能包括高质量的实时音视频通话。当您成功初始化 SDK 之后,您可以简单体验本产品的基本业务流程。本文档为您展示音视频通话提供的基本业务流程。

前提条件

请确认您已完成以下操作:

示例代码

网易云信为您提供完整的实现基础音视频通话的示例代码作为参考,您可以直接拷贝用于运行测试。

实现音视频通话的完整示例代码
//初始化SDK
- (void)setupRTCEngine {
    //默认情况下日志会存储在App沙盒的Documents目录下
    NERtcLogSetting *logSetting = [[NERtcLogSetting alloc] init];
#if DEBUG
    logSetting.logLevel = kNERtcLogLevelInfo;
#else
    logSetting.logLevel = kNERtcLogLevelWarning;
#endif
    
    NERtcEngineContext *context = [[NERtcEngineContext alloc] init];
    context.engineDelegate = self;
    context.appKey = AppKey;
    context.logSetting = logSetting;
    [[NERtcEngine sharedEngine] setupEngineWithContext:context];
}

//释放SDK资源
- (void)destroyRTCEngineWithCompletion:(void(^)(void))completion {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        [NERtcEngine destroyEngine];
        
        if (completion) {
            completion();
        }
    });
}

//加入房间
- (void)joinChannelWithRoomId:(NSString *)roomId
                       userId:(uint64_t)userId {
    NERtcEngine *coreEngine = [NERtcEngine sharedEngine];
    
    //1v1音视频通话场景的视频推荐配置
    //其他场景下请联系云信技术支持获取配置
    NERtcVideoEncodeConfiguration *config = [[NERtcVideoEncodeConfiguration alloc] init];
    config.width = 640;
    config.height = 360;
    config.frameRate = kNERtcVideoFrameRateFps15;
    [coreEngine setLocalVideoConfig:config];
    
    //1v1音视频通话场景的音频推荐配置
    //其他场景下请联系云信技术支持获取配置
    [coreEngine setAudioProfile:kNERtcAudioProfileStandard
                       scenario:kNERtcAudioScenarioSpeech];
    
    [coreEngine enableLocalAudio:YES];
    [coreEngine enableLocalVideo:YES];
    
    __weak typeof(self) weakSelf = self;
    [coreEngine joinChannelWithToken:@""
                         channelName:roomId
                               myUid:userId
                          completion:^(NSError * _Nullable error, uint64_t channelId, uint64_t elapesd) {
        if (error) {
            //加入失败,弹框之后退出当前页面
            NSString *message = [NSString stringWithFormat:@"join channel fail.code:%@", @(error.code)];
            [weakSelf showDismissAlertWithMessage:message actionBlock:^{
                [weakSelf destroyRTCEngineWithCompletion:^{
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [weakSelf.navigationController popViewControllerAnimated:YES];
                    });
                }];
            }];
        } else {
            //加入成功,建立本地canvas渲染本地视图
            NERtcVideoCanvas *canvas = [weakSelf setupLocalCanvas];
            [coreEngine setupLocalVideoCanvas:canvas];
        }
    }];
}

#pragma mark - SDK回调(含义请参考NERtcEngineDelegateEx定义)

- (void)onNERtcEngineDidError:(NERtcError)errCode {
    NSString *message = [NSString stringWithFormat:@"nertc engine did error.code:%@", @(errCode)];
    [self showDismissAlertWithMessage:message actionBlock:^{
        [[NERtcEngine sharedEngine] leaveChannel];
    }];
}

- (void)onNERtcEngineUserDidJoinWithUserID:(uint64_t)userID
                                  userName:(NSString *)userName {
    //如果已经setup了一个远端的canvas,则不需要再建立了
    if (_remoteCanvas != nil) {
        return;
    }
}
    
    //建立远端canvas,用来渲染远端画面
    NERtcVideoCanvas *canvas = [self setupRemoteCanvasWithUid:userID];
    [NERtcEngine.sharedEngine setupRemoteVideoCanvas:canvas
                                           forUserID:userID];
}

- (void)onNERtcEngineUserVideoDidStartWithUserID:(uint64_t)userID
                                    videoProfile:(NERtcVideoProfileType)profile {
    //如果已经订阅过远端视频流,则不需要再订阅了
    if (_remoteCanvas.subscribedVideo) {
        return;
    }
}
    //订阅远端视频流
    _remoteCanvas.subscribedVideo = YES;
    [NERtcEngine.sharedEngine subscribeRemoteVideo:YES
                                         forUserID:userID
                                        streamType:kNERtcRemoteVideoStreamTypeHigh];
}

- (void)onNERtcEngineUserVideoDidStop:(uint64_t)userID {
    if (userID == _remoteCanvas.uid) {
        _remoteStatLab.hidden = YES;
    }
}

- (void)onNERtcEngineUserDidLeaveWithUserID:(uint64_t)userID
                                     reason:(NERtcSessionLeaveReason)reason {
    //如果远端的人离开了,重置远端模型和UI
    if (userID == _remoteCanvas.uid) {
        _remoteStatLab.hidden = NO;
        [_remoteCanvas resetCanvas];
        _remoteCanvas = nil;
    }
}

- (void)onNERtcEngineDidDisconnectWithReason:(NERtcError)reason {
    //网络连接中断时会触发该回调,触发之后的操作则由开发者按需实现
    //此时已与房间断开连接,如果需要重新加入房间,必须再次调用join接口
}

- (void)onNERtcEngineDidLeaveChannelWithResult:(NERtcError)result {
    //调用leaveChannel之后,若需要释放SDK资源,建议在收到该回调之后,再调用destroyEngine
    [self destroyRTCEngineWithCompletion:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.navigationController popViewControllerAnimated:YES];
        });
    }];
}

实现流程

实现音频通话的 API 调用时序如下图所示。

uml diagram

实现视频通话的 API 调用时序如下图所示。

uml diagram

实现方法

步骤一 (可选)创建音视频通话界面

您可以参考此步骤根据业务场景创建相应的音视频通话界面,若您已实现相应界面,请忽略该步骤。

实现基础的音视频通话,建议您参考 SampleCode 中的 Main.storyboard 文件在界面上添加以下控件。

  • 本端视频窗口
  • 远端视频窗口
  • 麦克风按钮
  • 摄像头按钮
  • 结束通话按钮

效果图如下图所示。

ios效果图.png

步骤二 导入类

在项目中导入 NERtcSDK 类:

#import <NERtcSDK/NERtcSDK.h>

步骤三 初始化

调用 setupEngineWithContext 方法完成初始化。

您需要将 AppKey 替换为您的应用对应的 App Key。

示例代码如下:

@interface Myapp ()<NERtcEngineDelegateEx>
...
NERtcEngine *coreEngine = [NERtcEngine sharedEngine];
NERtcEngineContext *context = [[NERtcEngineContext alloc] init];
// 设置通话相关信息的回调
context.engineDelegate = self;
// 设置当前应用的 App Key
context.appKey = AppKey;//请替换为您自己的 App Key
[coreEngine setupEngineWithContext:context];

为了实现标准音视频通话业务,您还需要在初始化时注册相关必要回调,建议您请在初始化方法中传入原型为 NERtcEngineDelegate / NERtcEngineDelegateEx 的以下回调:

//本端用户连接中断通知,收到回调后,表示已从房间内断开。
- (void)onNERtcEngineDidDisconnectWithReason:(NERtcError)reason {
}
//远端用户加入房间通知回调,建议在收到此回调后再进行设置远端视图等的操作
- (void)onNERtcEngineUserDidJoinWithUserID:(uint64_t)userID
                                  userName:(NSString *)userName {
                              {
    }
}
//远端用户退出房间通知回调
- (void)onNERtcEngineUserDidLeaveWithUserID:(uint64_t)userID
                                     reason:(NERtcSessionLeaveReason)reason {
                                     {
    }
}
//远端用户推流/停止推流通知回调,建议在收到此回调后再进行订阅或取消订阅音视频流的操作
- (void)onNERtcEngineUserVideoDidStartWithUserID:(uint64_t)userID
                                    videoProfile:(NERtcVideoProfileType)profile {
                                       {
    }
}
- (void)onNERtcEngineUserVideoDidStop:(uint64_t)userID {
                                     {
    }
}

步骤四 设置本地视图

初始化成功后,可以设置本地视图,来预览本地图像。您可以根据业务需要实现加入房间之前预览或加入房间后预览。

  • 若您想调整摄像头的相关参数,请参考视频设备管理进行设置。
  • 在加入房间前,默认预览分辨率为 640*480,您可以通过 setLocalVideoConfig 接口的 width height 参数调整采集分辨率。
  • 实现加入房间前预览。

    1. 调用 setupLocalVideoCanvas 方法设置本地视图,可以通过 renderModemirrorMode 参数设置视频的渲染模式和镜像模式;再调用 startPreview(streamType) 方法预览本地图像。

      示例代码如下:

      //设置本地视频画布,以适应区域模式、开启镜像为例
      UIView *localUserView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
      NERtcVideoCanvas *canvas = [[NERtcVideoCanvas alloc] init];
      canvas.container = localUserView;
      canvas.renderMode = kNERtcVideoRenderScaleCropFill;
      canvas.mirrorMode = kNERtcVideoMirrorModeEnabled;
      [[NERtcEngine sharedEngine] setupLocalVideoCanvas:canvas];
      
      //以开启本地视频主流预览为例
      [[NERtcEngine sharedEngine] startPreview:kNERtcStreamChannelTypeMainStream];
      
    2. 若要结束预览,或者准备加入房间时,调用 stopPreview(streamType) 方法停止预览。

      stopPreview(streamType)streamType 参数请与 startPreview(streamType) 的保持一致,即同为主流或辅流的开启和停止预览。

  • 实现加入房间后预览。

    在成功加入房间后,调用 setupLocalVideoCanvas 方法设置本地视图,可以通过 renderModemirrorMode 参数设置视频的渲染模式和镜像模式;再调用 enableLocalVideo 方法进行视频的采集发送与预览。成功加入房间后,即可预览本地图像。

    示例代码如下:

    //设置本地视频画布,以适应区域模式、开启镜像为例
    UIView *localUserView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    NERtcVideoCanvas *canvas = [[NERtcVideoCanvas alloc] init];
    canvas.container = localUserView;
    canvas.renderMode = kNERtcVideoRenderScaleCropFill;
    canvas.mirrorMode = kNERtcVideoMirrorModeEnabled;
    [[NERtcEngine sharedEngine] setupLocalVideoCanvas:canvas];
    //以开启本地视频主流采集并发送为例
    [NERtcEngine.sharedEngine enableLocalVideo:YES,kNERtcStreamChannelTypeMainStream];
    

步骤五 加入房间

加入房间前,请确保已完成初始化相关事项。若您的业务中涉及呼叫邀请等机制,建议通过 信令 实现,总体实现流程请参见一对一会话操作流程,具体呼叫邀请机制的实现请参见邀请机制

调用 joinChannelWithToken:channelName:myUid:channelOptions:completion: 方法加入房间。

[NERtcEngine.sharedEngine joinChannelWithToken:@"Your Token"
                                    channelName: Your roomId
                                          myUid:Your userId
                                channelOptions:NERtcJoinChannelOptions
                                    completion:^(NSError * _Nullable error, uint64_t channelId, uint64_t elapesd) {
                                                        if (error) {
                                                            //加入失败
                                                        } else {
                                                            //加入成功
                                                        }
                                      }];

参数说明

参数 说明
Token 安全认证签名(NERTC Token)。
  • 调试模式下:可设置为 null。产品默认为安全模式,您可以在网易云信控制台将鉴权模式修改为调试模式,具体请参见Token 鉴权
    调试模式的安全性不高,请在产品正式上线前修改为安全模式。
  • 产品正式上线后:请设置为已获取的NERTC Token。安全模式下必须设置为获取到的 Token 。若未传入正确的 Token 将无法进入房间。

    推荐使用安全模式

channelName 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。
设置相同房间名称的用户会进入同一个通话房间。
您也可以在加入通道前,通过创建房间接口创建房间。加入房间时,若传入的 {channelName} 未事先创建,则云信服务器内部将为其自动创建一个名为 {channelName} 的通话房间。
myUid 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。此 uid 为用户在您应用中的 ID,请在您的业务服务器上自行管理并维护。
completion 操作完成的 block 回调,返回 channelId 和 uid(未指定 uid 时),其中 channelId 即音视频通话的 ID,建议您在业务层保存该数据,以便于后续问题排查。
channelOptions 加入房间时可以设置携带一些特定信息。默认值为 nil,具体请参考 `NERtcJoinChannelOptions`

成功加入房间之后,您可以通过监听 onNERtcEngineConnectionStateChangeWithState:reason: 回调实时监控自己在本房间内的连接状态。

步骤六 设置远端视图并发起订阅

音视频通话过程中,除了要显示本地的视频画面,通常也要显示参与互动的其他连麦者/主播的远端视频画面。

  1. 监听远端用户进出频道。

NERtcEngineDelegate 通过以下回调获取相关信息:

  1. 设置远端视频画布。

在监听到远端用户加入房间或发布视频流后,本端可以调用 setupRemoteVideoCanvas:forUserID: 方法设置远端用户视频画布,用于显示其视频画面。

示例代码如下:

  - (void)onNERtcEngineUserDidJoinWithUserID:(uint64_t)userID userName:(NSString *)userName {


  //如果已经setup了一个远端的canvas,则不需要再建立了
  ...
  if (_remoteCanvas != nil) {
      return;
  }


  //建立远端canvas,用来渲染远端画面
  NERtcVideoCanvas *canvas = [[NERtcVideoCanvas alloc] init];
  canvas.container = _remoteCanvas;
  [NERtcEngine.sharedEngine setupRemoteVideoCanvas:canvas forUserID:userID];


  - (void)onNERtcEngineUserDidLeaveWithUserID:(uint64_t)userID reason:(NERtcSessionLeaveReason)reason {


  //如果远端的人离开了,重置远端模型和UI
  ...
  [NERtcEngine.sharedEngine setupRemoteVideoCanvas:nil forUserID:userID];
  ...
  }
  1. 监听远端视频流发布。

当房间中的其他用户发布视频流时,本端会触发 onNERtcEngineUserVideoDidStartWithUserID: 回调:

@protocol NERtcEngineDelegate <NSObject>
/**
 其他用户打开视频的回调


 @param userID 用户id
 @param profile 用户发送视频的最大分辨率类型
 */
- (void)onNERtcEngineUserVideoDidStartWithUserID:(uint64_t)userID videoProfile:(NERtcVideoProfileType)profile;
  1. 订阅远端视频流。

在设置完远端视频画布后,且监听到远端用户有视频发布时,本端可以调用 subscribeRemoteVideo:forUserID:streamType: 方法订阅远端用户的视频流。

示例代码如下:

// 监听到远端用户有视频流发布
- (void)onNERtcEngineUserVideoDidStartWithUserID:(uint64_t)userID
                                    videoProfile:(NERtcVideoProfileType)profile {
    //如果已经订阅过远端视频流,则不需要再订阅了
    ...
    if (_remoteCanvas.subscribedVideo) {
        return;
    }


//订阅远端视频流
_remoteCanvas.subscribedVideo = YES;
[NERtcEngine.sharedEngine subscribeRemoteVideo:YES
                              forUserID:userID
                              streamType:kNERtcRemoteVideoStreamTypeHigh];
...}
  1. 监听远端用户离开房间或停止发布视频。

示例代码如下:

// 监听到远端用户停止视频流发布
- (void)onNERtcEngineUserVideoDidStop:(uint64_t)userID {
    if (userID == _remoteCanvas.uid) {
        // 收到此回调后,SDK 内部会取消对应的视频流订阅,无需开发者主动取消订阅。
        ...
        _remoteStatLab.hidden = YES;
        ...
    }
}

步骤七 音频流

本地音频的采集发布和远端音频订阅播放是默认启动的,正常情况下无需开发者主动干预。

步骤八 退出通话房间

调用 leaveChannel 方法退出通话房间。

示例代码如下:

 //UI 挂断按钮事件- (IBAction)onHungupAction:(UIButton *)sender {
    [NERtcEngine.sharedEngine leaveChannel];
    [self dismiss];
}

执行完 leaveChannel 方法后,SDK 会触发离开房间回调 onNERtcEngineDidLeaveChannelWithResult,通知当前用户退出房间的结果。

示例代码如下:

- (void)onNERtcEngineDidLeaveChannelWithResult:(NERtcError)result{
    // 进行业务数据清理
}

步骤九 销毁音视频实例

当确定 App 短期内不再使用音视频通话实例时,可以调用 destroyEngine 方法释放对应的对象资源。

释放资源需要在子线程中进行。

示例代码如下:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
      //开始销毁
     [NERtcEngine destroyEngine];
     //到这里表示销毁完成
  });
此文档是否对你有帮助?
有帮助
我要吐槽
  • 前提条件
  • 示例代码
  • 实现流程
  • 实现方法
  • 步骤一 (可选)创建音视频通话界面
  • 步骤二 导入类
  • 步骤三 初始化
  • 步骤四 设置本地视图
  • 步骤五 加入房间
  • 步骤六 设置远端视图并发起订阅
  • 步骤七 音频流
  • 步骤八 退出通话房间
  • 步骤九 销毁音视频实例