实现语音通话
更新时间: 2024/05/20 11:37:24
网易云信 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
-
请调用
nertc::createNERtcEngine
方法创建 SDK 实例。 -
请调用
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)。
|
channelName | 房间名称,长度为 1 ~ 64 字节。目前支持以下 89 个字符:a-z, A-Z, 0-9, space, !#$%&()+-:;≤.,>? @[]^_{|}~"。 设置相同房间名称的用户会进入同一个通话房间。 |
uid | 用户的唯一标识 id,为数字串,房间内每个用户的 uid 必须是唯一的。 |
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;