实现语音通话

更新时间: 2024/03/15 16:58:02

网易云信 RTC 提供高质量的实时语音通话。本文介绍语音通话的基本实现流程和示例代码。

前提条件

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

完整示例代码

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

实现语音通话的完整示例代码

在您工程的类的头文件中,添加以下代码

#include "INertcEngineEx.h"
#include "INertcEngineEventHandler.h"
#include "INertcAudioDeviceManager.h"


class YourProjectClass :public nertc::IRtcEngineEventHandler, public nertc::IRtcMediaStatsObserver
{
public: 
  void InitNertcEngine(FString yourAppkey, FString yourLogPath);
  //重写onJoinChannel虚函数,调用joinChannel后,SDK会自动调用该函数,通知结果
  void onJoinChannel(channel_id_t cid, nertc::uid_t uid, NERtcErrorCode result, uint64_t elapsed) override;
  //重写onConnectionStateChange虚函数,链接状态发生变更时,SDK会自动调用该函数
  void onConnectionStateChange(NERtcConnectionStateType state, NERtcReasonConnectionChangedType reason)override;
  //重写onLeaveChannel虚函数,离开房间后,SDK会自动调用该函数
  void onLeaveChannel(NERtcErrorCode result, uint64_t channel_id) override;
  //调用setStatsObserver之后,SDK会定期回调onRtcStats函数,将SDK内部数据,例如CPU使用率等,通知给您
  void onRtcStats(const NERtcStats& stats) override;

  。。。。//您还可以重写其他任意您感兴趣的虚函数

  private:
  //用来保存SDK引擎对象的指针
  nertc::IRtcEngineEx* nertc_engine_;
  //用来保存音频设备管理器的指针
  nertc::IAudioDeviceManager* audio_device_manager_;
}

在您工程的cpp文件中,添加以下代码

void YourProjectClass::InitNertcEngine(FString yourAappkey, FString yourLogPath) {
  nertc_engine_ = nertc::createNERtcEngine();

  if (!nertc_engine_)
    return;

  std::string appKey = TCHAR_TO_ANSI(*yourAappkey);
  std::string logPath = TCHAR_TO_ANSI(*yourLogPath);

  nertc::NERtcEngineContext context{};
  context.app_key = appKey.c_str();
  //设置引擎事件回调,确保YourProjectClass继承了IRtcEngineEventHandler
  context.event_handler = this;
  context.log_dir_path = logPath.c_str();
  context.log_level = kNERtcLogLevelInfo;

  auto ret = nertc_engine_->initialize(context);
  if (ret != 0) {
    return;
  }
  //设置游戏模式,获得更好的音频体验 (可选)
  nertc_engine_->setParameters("{\"sdk.enable.plugin.game.mode\":true}");

  //获取音频manager对象,用于操控音频设备 (可选)
  nertc_engine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)(&audio_device_manager_));
  if (!audio_device_manager_) {
    return;
  }

  //设置媒体状态回调,确保YourProjectClass继承了IRtcMediaStatsObserver (可选)
  nertc_engine_->setStatsObserver(this);
}

void YourProjectClass::onJoinChannel(channel_id_t cid, nertc::uid_t uid, NERtcErrorCode result, uint64_t elapsed) {
  Print("Join chennel event");
  //所有callback函数的代码运行在引擎的worker线程中,不能直接在这些函数中操作UI!
  ......
}

//重写IRtcMediaStatsObserver基类中的虚函数(可选)
void YourProjectClass::onRtcStats(const NERtcStats& stats) {
  ..... 显示SDK状态
}

在您的业务逻辑代码的合适时机处,进行SDK的初始化、入会、离会等操作

//初始化SDK
InitNertcEngine("your appkey", "your log path");
......

//对于安卓平台,需要主动申请下麦克风权限, 该方法会同步等待结果,然后才返回
nertc_engine_->acquireAndroidRecordingPermissions();

//加入房间
NERtcJoinChannelOptions option;
//如果您的appkey开通了非安全模式,则此处的“your_token”可以填空字符串
nertc_engine_->joinChannel("your_token", "your_channel_name", yourUid, option);

//joinChannel之后,SDK会调用 onJoinChannel函数将入会的结果通知给您,假如您想在onJoinChannel函数中更新UI,请注意线程问题:onJoinChannel函数并不运行在主线程,直接更新UI会有线程问题,请自行抛线程处理。

......

//结束通话
nertc_engine_->leaveChannel();
......

//程序即将退出时,释放SDK
nertc_engine_->release();
delete nertc_engine_;
nertc_engine_ = nullptr;

API 时序图

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

sequenceDiagram
    autonumber
    participant App
    participant NERtcSDK
    participant 云信服务端

    App->>NERtcSDK: setParameters 设置为游戏模式
    App->>NERtcSDK: initialize 初始化 NERtcEngine
    App->>NERtcSDK: joinChannel 加入房间
    NERtcSDK->>云信服务端: 请求加入房间
    NERtcSDK-->>App: onJoinChannel
    App->>NERtcSDK: leaveChannel 离开房间
    NERtcSDK->>云信服务端: 请求离开房间
    App->>NERtcSDK: release 销毁实例

实现语音通话

步骤一 导入类

在您的工程中对应实现语音通话的 Activity 文件里添加如下代码先导入以下重要类:

#include "INertcEngineEx.h"
#include "INertcEngineEventHandler.h"
#include "INertcAudioDeviceManager.h"

步骤二 继承回调事件类

SDK通过回调来通知上层相应的事件,您需要在初始化时注册回调指针,请继承 nertc::IRtcEngineEventHandler 类,并按需重写其中的虚函数。初始化 SDK 时即可将YourProjectClass 指针传给 SDK,完成事件的注册。

class YourProjectClass :public nertc::IRtcEngineEventHandler, public nertc::IRtcMediaStatsObserver
{
public: 
  void InitNertcEngine(FString yourAappkey, FString yourLogPath);
  //重写onJoinChannel虚函数,调用joinChannel后,SDK会自动调用该函数,通知结果
  void onJoinChannel(channel_id_t cid, nertc::uid_t uid, NERtcErrorCode result, uint64_t elapsed) override;
  //重写onConnectionStateChange虚函数,链接状态发生变更时,SDK会自动调用该函数
  void onConnectionStateChange(NERtcConnectionStateType state, NERtcReasonConnectionChangedType reason)override;
  //重写onLeaveChannel虚函数,离开房间后,SDK会自动调用该函数
  void onLeaveChannel(NERtcErrorCode result, uint64_t channel_id) override;
  //调用setStatsObserver之后,SDK会定期回调onRtcStats函数,将SDK内部数据,例如CPU使用率等,通知给您
  void onRtcStats(const NERtcStats& stats) override;

  。。。。//您还可以重写其他任意您感兴趣的虚函数

  private:
  //用来保存SDK引擎对象的指针
  nertc::IRtcEngineEx* nertc_engine_;
  //用来保存音频设备管理器的指针
  nertc::IAudioDeviceManager* audio_device_manager_;
}

步骤三 创建并初始化 SDK

  1. 请调用 nertc::createNERtcEngine 方法创建 SDK 实例。

  2. 请调用 initialize 方法完成初始化。

示例代码如下:

void YourProjectClass::InitNertcEngine(FString yourAappkey, FString yourLogPath) {
  nertc_engine_ = nertc::createNERtcEngine();

  if (!nertc_engine_)
    return;

  std::string appKey = TCHAR_TO_ANSI(*yourAappkey);
  std::string logPath = TCHAR_TO_ANSI(*yourLogPath);

  nertc::NERtcEngineContext context{};
  context.app_key = appKey.c_str();
  //设置引擎事件回调,确保YourProjectClass继承了IRtcEngineEventHandler
  context.event_handler = this;
  context.log_dir_path = logPath.c_str();
  context.log_level = kNERtcLogLevelInfo;

  auto ret = nertc_engine_->initialize(context);
  if (ret != 0) {
    return;
  }
  //设置游戏模式,获得更好的音频体验 (可选)
  nertc_engine_->setParameters("{\"sdk.enable.plugin.game.mode\":true}");

  //获取音频manager对象,用于操控音频设备 (可选)
  nertc_engine_->queryInterface(nertc::kNERtcIIDAudioDeviceManager, (void**)(&audio_device_manager_));
  if (!audio_device_manager_) {
    return;
  }

  //设置媒体状态回调,确保YourProjectClass继承了IRtcMediaStatsObserver (可选)
  nertc_engine_->setStatsObserver(this);
}

//在您的业务逻辑代码的合适时机处,进行SDK的初始化操作
InitNertcEngine("your_appkey", "your_log_path");
参数 类型 描述
Aappkey String 请替换为云信控制台上您的应用对应的 AppKey。获取 AppKey 的方法请参见创建应用并获取 AppKey
LogPath String 请设置日志路径,必填
Windows 操作系统推荐使用 %temp% 目录或您的工程目录,只要是具有读写权限的目录即可。

步骤四 加入房间

调用 joinChannel 方法加入房间。

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

示例代码如下:

nertc_engine_->joinChannel("your_token", "your_channel_name", yourUid, option);

参数说明

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

    推荐使用安全模式

channelName 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。
设置相同房间名称的用户会进入同一个通话房间。
您也可以在加入通道前,通过创建房间接口创建房间。加入房间时,若传入的 {channelName} 未事先创建,则云信服务器内部将为其自动创建一个名为 {channelName} 的通话房间。
uid 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。此 uid 为用户在您应用中的 ID,请在您的业务服务器上自行管理并维护。
channelOptions 加入房间时可以设置携带一些特定信息,包括高级权限密钥。若无需设置上述信息,请构造一个默认对象传入即可。具体请参考 NERtcJoinChannelOptions
  • SDK 发起加入房间请求后,服务器会进行响应,您可以通过onJoinChannel 回调监听加入房间的结果,同时该回调会抛出当前通话房间的 channelId 与加入房间总耗时(毫秒);其中 channelId 即语音通话的 ID,建议您在业务层保存该数据,以便于后续问题排查。

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

  • 您也可以调用 onUserJoined 回调监听其它用户加入房间的通知。

步骤五 发布、停止本地音频流(可选)

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

您也可以调用 enableLocalAudio方法开启或关闭音频设备,以控制本地音频流的发布。

nertc_engine_->enableLocalAudio(kNERtcAudioStreamTypeMain, true or false);

步骤六 离开房间

调用 leaveChannel 方法离开房间。

示例代码如下:

nertc_engine_->leaveChannel();
  • 通过 onLeaveChannel 回调可以监听当前用户退出房间的结果。
  • 通过 onUserLeft 回调可以监听远端用户离开房间的通知。

步骤七 销毁实例

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

当此接口调用成功之后,若您想再次使用 SDK,需要重新创建并初始化SDK。

示例代码如下:

//程序即将退出时,释放SDK
nertc_engine_->release();
delete nertc_engine_;
nertc_engine_ = nullptr;
此文档是否对你有帮助?
有帮助
去反馈
  • 前提条件
  • 完整示例代码
  • API 时序图
  • 实现语音通话
  • 步骤一 导入类
  • 步骤二 继承回调事件类
  • 步骤三 创建并初始化 SDK
  • 步骤四 加入房间
  • 步骤五 发布、停止本地音频流(可选)
  • 步骤六 离开房间
  • 步骤七 销毁实例