DingRTC的基本功能包含初始化SDK、加入频道、本地发布、订阅远端和离开频道等。通过阅读本文,您可以了解DingRTC的基本功能。
操作步骤
初始化SDK。
您需要创建RtcEngine实例,并注册回调。
// 设置日志路径 RtcEngine::SetLogDirPath(logDirPath); // 创建引擎实例 RtcEngine * engine = RtcEngine::Create(extras); // 按需实现RtcEngineEventListener,设置事件回调 engine->SetEngineEventListener(listener);
加入频道。
入会需获取鉴权Token,通常由业务方服务端生成,具体方法请参考“使用Token鉴权”
struct RtcEngineAuthInfo { /*! 频道ID。 */ String channelId; /*! 用户ID。 */ String userId; /*! 应用ID。 */ String appId; /*! 令牌。 */ String token; /*! GSLB地址。 */ String gslbServer; }; // 获取token RtcEngineAuthInfo authInfo = getYourAuthInfo(); // 入会 engine->JoinChannel(authInfo, userName);
参数
描述
appId
应用ID,在控制台应用管理页面创建和查看。
channelId
频道ID。1~64位,由大小写字母、数字、下划线(_)、短划线(-)组成。
userId
用户ID。1~64位,由大小写字母、数字、下划线(_)、短划线(-)组成。
说明同一个用户ID在其他端登录,先入会的端会被后入会的端踢出频道。
token
频道鉴权令牌。
gslbServer
服务地址,可为空,默认值为:
"https://gslb.dingrtc.com"
,请您通过业务服务器下发到客户端SDK,不建议您将该地址固化在客户端代码。发布本地音频流。
入会之后,SDK默认不推流,需主动调用API开启推流(可在入会前调用)。
开启音频推流
// 开启音频推流,可在JoinChannel前调用 engine->PublishLocalAudioStream(true);
外部音频推流
如果是Linux服务器或没有麦克风声音采集设备,可使用外部音频源接口来推送音频数据。
// 开启外部音频源,同时设置音频采样率、声道数等参数 engine->SetExternalAudioSource(true, sampleRate, channels);
通常需要一个单独的线程来循环推送音频数据,同时控制数据保持一定的时间间隔均匀输入给SDK。可类比为麦克风设备,按固定周期采集音频数据输入给SDK。
static const int kBytePerSamplePcm16 = 2; // 16bit音频采样点字节数 std::string pcm_file_path = "/path/to/my_audio.pcm"; // pcm文件路径 int audio_sample_rate = 48000; // 采样率 int audio_channels = 1; // 声道数 int read_ms = 40; // 单次读取40ms音频数据 bool push_audio_quit = false; // quit标记位 // 音频推流线程,从PCM文件读取音频数据,保持一定时间间隔输入给SDK推流 // 可以理解为模拟音频采集设备,按固定周期采集音频数据 std::thread push_audio_thread = std::thread([=]{ // 打开文件 FILE *fp = fopen(pcm_file_path.c_str(), "rb"); if (fp) { // 统计音频帧数,并用于计算音频时间戳 int64_t sample_count = 0; // 音频时延 int64_t delay_ms = 0; // 起始的系统时钟 auto start_clock = std::chrono::high_resolution_clock::now(); // 单次读取的音频采样点数 int samples_to_read = audio_sample_rate / (1000 / read_ms); // 单次读取音频字节数 int buffer_size = samples_to_read * audio_channels * kBytePerSamplePcm16; // 分配相应内存buffer以存储读取数据 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); // 进入循环推流阶段 while (true) { // 直至quit标记为true,退出推流 if (push_audio_quit) break; size_t read_bytes = fread(buffer.get(), 1, buffer_size, fp); // 读取到文件末尾,则从头开始读取 if (read_bytes == 0) { fseek(fp, 0, SEEK_SET); continue; } // 填充音频帧字段 RtcEngineAudioFrame frame; frame.type = RtcEngineAudioFramePcm16; frame.bytesPerSample = kBytePerSamplePcm16; frame.samplesPerSec = audio_sample_rate; frame.channels = audio_channels; frame.buffer = buffer.get(); frame.samples = read_bytes / frame.bytesPerSample / frame.channels; frame.timestamp = sample_count * 1000 / audio_sample_rate; // 计算音频时延与系统时钟的差值,sleep来控制输入频率 delay_ms = frame.timestamp; int64_t elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_clock).count(); if (delay_ms - elapsed_ms > 5) { std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - elapsed_ms)); } engine->PushExternalAudioFrame(&frame); sample_count += frame.samples; } // 关闭文件 fclose(fp); } });
停止音频推流
// 关闭外部音频源 engine->SetExternalAudioSource(false, sampleRate, channels); // 关闭音频推流 engine->PublishLocalAudioStream(false);
发布本地视频流。
入会之后,SDK默认不推流,需主动调用API开启推流(可在入会前调用)。
开启视频推流
// 开启视频推流,可在JoinChannel前调用 engine->PublishLocalVideoStream(true);
外部视频推流
如果是Linux服务器或没有摄像头视频采集设备,可使用外部视频源接口来推送视频数据。
// 开启外部视频源,来代替Camera流 engine->SetExternalVideoSource(true, RtcEngineVideoTrackCamera);
通常需要一个单独的线程来循环推送视频数据,同时控制数据保持一定的时间间隔均匀输入给SDK。可类比为摄像头设备,按固定周期采集音频数据输入给SDK。
std::string yuv_file_path = "/path/to/my_yuv.yuv"; // yuv文件路径 RtcEngineVideoPixelFormat video_pixel_format = RtcEngineVideoI420; // 视频像素格式 int video_width = 1280; // 视频宽度 int video_height = 720; // 视频高度 int video_fps = 25; // 视频帧率 bool push_video_quit = false; // quit标记位 // 视频推流线程,从YUV文件读取音频数据,保持一定时间间隔输入给SDK推流 // 可以理解为模拟音频采集设备,按固定周期采集音频数据 std::thread push_video_thread = std::thread([=] { FILE *fp = fopen(yuv_file_path.c_str(), "rb"); if (fp) { // 统计视频帧数,并用于计算视频时间戳 int64_t frame_count = 0; // 视频时延 int64_t delay_ms = 0; // 起始的系统时钟 auto start_clock = std::chrono::high_resolution_clock::now(); // 一帧视频的字节数 int buffer_size = CalcBufferSize(video_pixel_format, video_width, video_height); // 分配相应内存buffer以存储读取数据 std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); // 进入循环推流阶段 while (true) { // 直至quit标记为true,退出推流 if (push_video_quit) break; size_t read_bytes = fread(buffer.get(), 1, buffer_size, fp); // 读取到文件末尾,则从头开始读取 if (read_bytes == 0) { fseek(fp, 0, SEEK_SET); continue; } // 配置外部视频流参数 RtcEngineVideoFrame frame; memset(&frame, 0, sizeof(frame)); frame.frameType = RtcEngineVideoFrameRaw; frame.pixelFormat = video_pixel_format; frame.width = video_width; frame.height = video_height; frame.stride[0] = video_width; frame.stride[1] = frame.stride[2] = video_width >> 1; frame.rotation = config.rotation; frame.data = buffer.get(); frame.timestamp = frame_count * 1000 / video_fps; // 计算视频时延与系统时钟的差值,sleep来控制输入频率 delay_ms = frame.timestamp; int64_t elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_clock).count(); if (delay_ms - elapsed_ms > 5) { std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms - elapsed_ms)); } engine->PushExternalVideoFrame(&frame, RtcEngineVideoTrackCamera); frame_count++; } fclose(fp); } }); size_t CalcBufferSize(RtcEngineVideoPixelFormat pixel_format, int width, int height) { size_t buffer_size = 0; switch (pixel_format) { case RtcEngineVideoI420: case RtcEngineVideoNV12: case RtcEngineVideoNV21: { int half_width = (width + 1) >> 1; int half_height = (height + 1) >> 1; buffer_size = width * height + half_width * half_height * 2; break; } case RtcEngineVideoBGRA: case RtcEngineVideoARGB: case RtcEngineVideoRGBA: case RtcEngineVideoABGR: buffer_size = width * height * 4; break; default: break; } return buffer_size; }
停止视频推流
// 关闭外部视频源 engine->SetExternalVideoSource(false, RtcEngineVideoTrackCamera); // 关闭音频推流 engine->PublishLocalVideoStream(false);
订阅远程音频流。
SDK默认自动订阅远端音视频并拉流,如需关闭拉流请主动调用API关闭拉流(可在入会前调用)。
订阅音频流
SDK当前不支持按指定用户订阅音频流,仅支持订阅音频合流(即所有远端用户的声音合流)。
// 订阅音频流,可在JoinChannel前调用 engine->SubscribeAllRemoteAudioStreams(true); // 取消订阅音频流,可在JoinChannel前调用 engine->SubscribeAllRemoteAudioStreams(false);
监听音频帧数据
// 实现RtcEngineAudioFrameObserver,接收音频数据回调 class MyAudioFrameObserver : public ding::rtc::RtcEngineAudioFrameObserver { public: void OnPlaybackAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} void OnCapturedAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} void OnProcessCapturedAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} void OnPublishAudioFrame(ding::rtc::RtcEngineAudioFrame &frame) override {} }; MyAudioFrameObserver myObserver; // 注册音频数据回调 engine->RegisterAudioFrameObserver(&myObserver); // 开启音频回调,可指定接收哪种类型的音频数据回调 engine->EnableAudioFrameObserver(true, RtcEngineAudioPositionPlayback); // 可指定同时接收多种类型数据回调 engine->EnableAudioFrameObserver(true, RtcEngineAudioPositionCaptured | RtcEngineAudioPositionPub | RtcEngineAudioPositionPlayback); // 关闭音频回调 engine->EnableAudioFrameObserver(false, RtcEngineAudioPositionPlayback); // 取消注册音频数据回调 engine->RegisterAudioFrameObserver(nullptr);
监听发言人音量
/** * @ingroup CPP_DingRtcEngineAudio * @since 3.0 * @brief 设置音量回调频率和平滑系数。 * @param interval 时间间隔,单位毫秒,最小值不得小于100ms, 建议设置300-500ms, <= 0表示不启用音量提示和说话人提示功能。 * @param smooth 平滑系数,数值越大平滑程度越高,反之越低,实时性越好,建议设置3,范围[0, 9]。 * @param reportVad 说话人检测开关。 * - 1:开启。 * - 0:关闭。 * @return * - 0:成功; * - <0:失败。 */ virtual int EnableAudioVolumeIndication(int interval, int smooth, int reportVad) = 0; class MyEventListener : public RtcEngineEventListener { public: void OnAudioVolumeIndication(const ding::rtc::AudioVolumeInfo* speakers, unsigned int speakerNumber) override {} }; // 开启音量回调,OnAudioVolumeIndication回调说话人音量信息 engine->EnableAudioVolumeIndication(300, 3, 1); // 关闭音量回调 engine->EnableAudioVolumeIndication(0, 3, 1);
订阅远程视频流。
订阅视频流
// 订阅所有远端用户的视频流,可在JoinChannel前调用 engine->SubscribeAllRemoteVideoStreams(true); // 取消订阅所有远端用户的视频流,可在JoinChannel前调用 engine->SubscribeAllRemoteVideoStreams(false); // 订阅某个远端用户的视频流 engine->SubscribeRemoteVideoStream(uid, track, true); // 取消订阅某个远端用户的视频流,可在JoinChannel前调用 engine->SubscribeRemoteVideoStream(uid, track, false);
监听视频帧数据
// 实现RtcEngineVideoFrameObserver,接收音频数据回调 class MyVideoFrameObserver : public ding::rtc::RtcEngineVideoFrameObserver { public: ding::rtc::RtcEngineVideoPixelFormat GetVideoFormatPreference() override; bool OnCaptureVideoFrame(ding::rtc::RtcEngineVideoFrame &frame) override; bool OnRemoteVideoFrame(ding::rtc::String uid, ding::rtc::RtcEngineVideoTrack track, ding::rtc::RtcEngineVideoFrame &frame) override; bool OnPreEncodeVideoFrame(ding::rtc::RtcEngineVideoTrack track, ding::rtc::RtcEngineVideoFrame &frame) override; }; MyVideoFrameObserver myObserver; // 注册视频数据回调 engine->RegisterVideoFrameObserver(&myObserver); // 开启视频回调,可指定接收哪种类型的视频数据回调 engine->EnableVideoFrameObserver(true, RtcEnginePositionPreRender); // 可指定同时接收多种类型数据回调 engine->EnableAudioFrameObserver(true, RtcEnginePositionPostCapture | RtcEnginePositionPreRender | RtcEngineAudioPositionPlayback); // 关闭视频回调 engine->EnableVideoFrameObserver(false, RtcEnginePositionPreRender); // 取消注册视频数据回调 engine->RegisterVideoFrameObserver(nullptr);
离开频道。
// 离会 engine->LeaveChannel();
销毁引擎。
// 取消事件回调监听
engine->SetEngineEventListener(nullptr);
// 销毁SDK
RtcEngine::Destroy(engine);