前提概要
声网实时码流加速产品(Real-Time Streaming Acceleration, RTSA)帮助拥有第三方音视频编解码模块或自研编解码的开发者实现实时音视频码流传输互通。RTSA 依托声网底层实时传输网络 Agora SD-RTN (Software Defined Real-time Network) ,运用全球全网节点 、智能动态路由以及端侧弱网对抗算法,提供高联通性、低延时、高稳定性的音视频码流传输云服务,减少延时、丢包等网络问题对音视频传输质量和体验的影响。此外,RTSA 适配多种 IoT 平台以及 x86 架构,助力开发者在任意设备与场景中开启实时互动能力。
Agora Linux SDK
Agora Linux SDK 部署在服务端,可与集成了 Agora RTC SDK 的客户端通过 Agora SD-RTN 进行实时通信,实现向客户端发送音视频流和从客户端接收音视频流的功能。
下图展示了 Linux SDK 的几种应用,包括在频道中播放本地文件,推流到 CDN, 以及与企业呼叫中心互通。Linux SDK 将 SD‑RTN™ 中的媒体流转换为指定格式,输出给其他模块,并将其他模块的媒体流编码,发送到 SD‑RTN™。
Agora RTC SDK 和 Linux SDK 互通的场景中,请确保将 RTC SDK 的频道场景设置为 LIVE_BROADCASTING
。
产品特性
兼容:与 Android、iOS、Windows、macOS、Web 的 Agora RTC SDK(3.0.0 或更高版本)以及小程序 Agora RTC SDK (2.4.2) 互通。
可靠:支持集群部署,动态扩容,服务高可用。
流畅:基于 SD-RTN™ 的音视频抗丢包特性,实现低延迟的服务器到客户端的内容传递。
高并发:使用一台 8 核、16G 内存、2.5GHz 服务器向多个频道发送媒体流,视频分辨率 320 × 240,帧率 15 fps,可同时向 1000 频道发送媒体流。
安全:提供音视频通话、数据传输、数据存储等端到端安全保障机制
适用场景
AI 互动课堂:AI 互动课堂是一种个性化在线教学方式,利用 AI 技术对学生的表现进行智能分析,向学生推送针对性的音视频课件讲解,做到因材施教。使用 Linux SDK,可以向频道中不同的 UID 发送不同的课件视频。
网络测试:在上课之前,Linux SDK 作为机器人进入频道,与老师和学生通话,测试端到端网络情况。
呼叫中心:使用 Linux SDK 为企业传统的呼叫中心系统 (VoIP/PSTN)建立音频连接, 让用户可以在 App 上方便、快捷地发起音频呼叫,联络客服坐席。
产品功能
发送和接收多种格式的音视频数据,视频数据:
前提条件
下表列出了安装 Linux SDK 的基本要求:
服务器:
物理或虚拟
Ubuntu 14.04+ x64
CentOS 6.6+(推荐 7.0)x64
网络
这台 Linux 服务器要接入公网,有公网 IP
带宽
根据需要同时传输的频道数量和频道内情况确定所需带宽。以下数据可供参考:传输一个分辨率为 640 × 480 的画面需要的带宽约为 500 kbps;传输一个有两个人的频道则需 1 Mbps;同时传输 100 个这样的频道,需要带宽为 100 Mbps。
域名解析
服务器允许访问 .agora.io 以及 .agoralab.co。
集成
准备环境
如果你的 Linux 服务器操作系统为 Ubuntu,则需要安装以下依赖:
sudo apt install build-essential git
sudo apt install libx11-dev libxcomposite-dev libxext-dev libxfixes-dev libxdamage-dev
//cmake 版本不得低于 3.5
wget https://cmake.org/files/v3.6/cmake-3.6.2.tar.gz
tar xvf cmake-3.6.2.tar.gz
cd cmake-3.6.2/
./bootstrap --prefix=/usr/local
make
sudo make install
复制代码
如果你的 Linux 服务器操作系统为 CentOS,则需要安装以下依赖:
sudo yum groupinstall "Development Tools"
sudo yum install wget
sudo yum groupinstall X11
//cmake 版本不得低于 3.5
wget https://cmake.org/files/v3.6/cmake-3.6.2.tar.gz
tar xvf cmake-3.6.2.tar.gz
cd cmake-3.6.2/
./bootstrap --prefix=/usr/local
make
sudo make install
复制代码
集成 SDK
下载并解压最新的 Agora Linux SDK。软件包内容如下:
为你的项目准备所需库:将 include 文件夹添加到你的项目里。
如果你的网络环境设置了防火墙限制外网访问,将以下目标域名及对应的端口添加到防火墙白名单:
.agora.io .edge.agora.io .agoraio.cn .edge.agoraio.cn
为调试方便,Agora 建议你打开系统的 core dump 功能以记录可能产生的程序崩溃信息。
发送和接收媒体流
你已经集成了 Linux SDK,可以选择以下任意一种方式进行媒体流的发送和接收:
音频
视频
其中:
箭头指代音视频流流向。蓝色方框为你需要创建的对象,虚线为可选项,黄色方框为 SDK 内置模块。
你可以为接收到的视频流加多个 filter,用于美颜、超分辨率算法、变声等。
除用于接收原始视频数据外,IVideoSinkBase
也可以用于渲染远端视频。
你可以使用 SDK 内置的 Audio Device Module (ADM) 播放远端音频。
实现方法
API 时序图
接收原始音视频数据
接收编码后视频数据和原始音频数据
实现步骤
全局初始化
首先,你需要在你的应用程序里调用 createAgoraService
和 initialize
进行全局初始化,创建并初始化 AgoraService
对象。
这个操作只需要进行一次,AgoraService
对象的生命期和应用程序的生命期保持一致,只要应用程序没有退出,AgoraService
可以一直存在。、
auto service = createAndInitAgoraService(false, true, true);
复制代码
建立连接
初始化后,根据以下步骤让本地 AgoraService
对象与声网服务器建立连接。
调用 createRtcConnection
创建 IRtcConnection
对象,用于与声网服务器建立连接。
(可选)你可以通过 VideoSubscriptionOptions
和 AudioSubscriptionOptions
定义音视频订阅选项。
创建 IRtcConnectionObserver
对象(即示例代码中的 SampleConnectionObserver
对象),用于监测连接事件。然后通过 registerObserver
方法将 IRtcConnectionObserver
对象和 IRtcConnection
对象关联起来。
调用 connect
与声网服务器建立连接。
// Configure audio subscription options
agora::rtc::AudioSubscriptionOptions audioSubOpt;
audioSubOpt.bytesPerSample = sizeof(int16_t) * options.audio.numOfChannels;
audioSubOpt.numberOfChannels = options.audio.numOfChannels;
audioSubOpt.sampleRateHz = options.audio.sampleRate;
// Configure connection options
agora::rtc::RtcConnectionConfiguration ccfg;
ccfg.clientRoleType = agora::rtc::CLIENT_ROLE_AUDIENCE;
ccfg.audioSubscriptionOptions = audioSubOpt;
ccfg.autoSubscribeAudio = false;
ccfg.autoSubscribeVideo = false;
ccfg.enableAudioRecordingOrPlayout =
false; // Subscribe audio but without playback
// Create Agora connection
agora::agora_refptr<agora::rtc::IRtcConnection> connection =
service->createRtcConnection(ccfg);
if (!connection) {
AG_LOG(ERROR, "Failed to creating Agora connection!");
return -1;
}
// Subcribe streams from all remote users or specific remote user
agora::rtc::ILocalUser::VideoSubscriptionOptions subscriptionOptions;
if (options.streamType == STREAM_TYPE_HIGH) {
subscriptionOptions.type = agora::rtc::REMOTE_VIDEO_STREAM_HIGH;
} else if(options.streamType==STREAM_TYPE_LOW){
subscriptionOptions.type = agora::rtc::REMOTE_VIDEO_STREAM_LOW;
} else{
AG_LOG(ERROR, "It is a error stream type");
return -1;
}
if (options.remoteUserId.empty()) {
AG_LOG(INFO, "Subscribe streams from all remote users");
connection->getLocalUser()->subscribeAllAudio();
connection->getLocalUser()->subscribeAllVideo(subscriptionOptions);
} else {
connection->getLocalUser()->subscribeAudio(options.remoteUserId.c_str());
connection->getLocalUser()->subscribeVideo(options.remoteUserId.c_str(),
subscriptionOptions);
}
// Register connection observer to monitor connection event
auto connObserver = std::make_shared<SampleConnectionObserver>();
connection->registerObserver(connObserver.get());
// Connect to Agora channel
if (connection->connect(options.appId.c_str(), options.channelId.c_str(),
options.userId.c_str())) {
AG_LOG(ERROR, "Failed to connect to Agora channel!");
return -1;
}
复制代码
媒体流接收准备
创建 ILocalUserObserver
对象(即示例代码中的 SampleLocalUserObserver
对象)。
注册音视频帧观测器。成功注册观测器后,SDK 会在捕捉到每个音频或视频帧时触发回调。你可以在回调中获得该音频或视频帧。
// Create local user observer
auto localUserObserver =
std::make_shared<SampleLocalUserObserver>(connection->getLocalUser());
// Register audio frame observer to receive audio stream
auto pcmFrameObserver = std::make_shared<PcmFrameObserver>(options.audioFile);
if (connection->getLocalUser()->setPlaybackAudioFrameBeforeMixingParameters(
options.audio.numOfChannels, options.audio.sampleRate)) {
AG_LOG(ERROR, "Failed to set audio frame parameters!");
return -1;
}
localUserObserver->setAudioFrameObserver(pcmFrameObserver.get());
// Register video frame observer to receive video stream
agora::agora_refptr<agora::rtc::IVideoSinkBase> yuvFrameObserver =
new agora::RefCountedObject<YuvFrameObserver>(options.videoFile);
localUserObserver->setVideoFrameObserver(yuvFrameObserver);
复制代码
接收媒体流
注册音频帧观测器和视频帧观测器后,当 SDK 触发 onPlaybackAudioFrameBeforeMixing
或 onFrame
回调时,意味着收到了一个音频帧或视频帧。你需要在这两个回调函数中添加接收媒体流的逻辑,比如将音频帧写入音频文件。
// Callback to receive a PCM frame
bool PcmFrameObserver::onPlaybackAudioFrameBeforeMixing(
unsigned int uid, AudioFrame& audioFrame) {
// Create new file to save received PCM samples
if (!pcmFile_) {
std::string fileName = (++fileCount > 1)
? (outputFilePath_ + to_string(fileCount))
: outputFilePath_;
if (!(pcmFile_ = fopen(fileName.c_str(), "w"))) {
AG_LOG(ERROR, "Failed to create received audio file %s",
fileName.c_str());
return false;
}
AG_LOG(INFO, "Created file %s to save received PCM samples",
fileName.c_str());
}
// Write PCM samples
size_t writeBytes =
audioFrame.samplesPerChannel * audioFrame.channels * sizeof(int16_t);
if (fwrite(audioFrame.buffer, 1, writeBytes, pcmFile_) != writeBytes) {
AG_LOG(ERROR, "Error writing decoded audio data: %s", std::strerror(errno));
return false;
}
fileSize_ += writeBytes;
// Close the file if size limit is reached
if (fileSize_ >= DEFAULT_FILE_LIMIT) {
fclose(pcmFile_);
pcmFile_ = nullptr;
fileSize_ = 0;
}
return true;
}
// Callback to receive a YUV frame
int YuvFrameObserver::onFrame(
const agora::media::base::VideoFrame& videoFrame) {
// Create new file to save received YUV frames
if (!yuvFile_) {
std::string fileName = (++fileCount > 1)
? (outputFilePath_ + to_string(fileCount))
: outputFilePath_;
if (!(yuvFile_ = fopen(fileName.c_str(), "w+"))) {
AG_LOG(ERROR, "Failed to create received video file %s",
fileName.c_str());
return -1;
}
AG_LOG(INFO, "Created file %s to save received YUV frames",
fileName.c_str());
}
// Write Y planar
size_t writeBytes = videoFrame.yStride * videoFrame.height;
if (fwrite(videoFrame.yBuffer, 1, writeBytes, yuvFile_) != writeBytes) {
AG_LOG(ERROR, "Error writing decoded video data: %s", std::strerror(errno));
return -1;
}
fileSize_ += writeBytes;
// Write U planar
writeBytes = videoFrame.uStride * videoFrame.height / 2;
if (fwrite(videoFrame.uBuffer, 1, writeBytes, yuvFile_) != writeBytes) {
AG_LOG(ERROR, "Error writing decoded video data: %s", std::strerror(errno));
return -1;
}
fileSize_ += writeBytes;
// Write V planar
writeBytes = videoFrame.vStride * videoFrame.height / 2;
if (fwrite(videoFrame.vBuffer, 1, writeBytes, yuvFile_) != writeBytes) {
AG_LOG(ERROR, "Error writing decoded video data: %s", std::strerror(errno));
return -1;
}
fileSize_ += writeBytes;
// Close the file if size limit is reached
if (fileSize_ >= DEFAULT_FILE_LIMIT) {
fclose(yuvFile_);
yuvFile_ = nullptr;
fileSize_ = 0;
}
return 0;
};
复制代码
断开连接并释放资源
媒体流传输完成后,你可以通过以下步骤退出频道并断开与声网服务器的连接:
注销音视频帧观测器。
调用 disconnect
断开与声网服务器的连接。
释放 IRtcConnection
对象相关的资源。
// Unregister audio & video frame observers
localUserObserver->unsetAudioFrameObserver();
localUserObserver->unsetVideoFrameObserver();
// Disconnect from Agora channel
if (connection->disconnect()) {
AG_LOG(ERROR, "Failed to disconnect from Agora channel!");
return -1;
}
AG_LOG(INFO, "Disconnected from Agora channel successfully");
// Destroy Agora connection and related resources
localUserObserver.reset();
pcmFrameObserver.reset();
yuvFrameObserver = nullptr;
connection = nullptr;
复制代码
全局注销
当你的应用程序退出时,可以调用如下逻辑,注销整个 AgoraService
。
// Destroy Agora Service
service->release();
service = nullptr;
复制代码
从本地发送媒体流至 SD-RTN™
架构图
下图分别展示了发送本地音频和视频数据的数据处理和流转过程:
音频
视频
其中:
箭头指代音视频流流向。蓝色方框为你需要创建的对象,虚线为可选项,黄色方框为 SDK 内置模块。
你可以加多个 filter,用于美颜、超分辨率算法、变声等。
你可以使用 IVideoSinkBase
或 IVideoRenderer
实现本地预览。IVideoRenderer
为 IVideoSinkBase
的派生类,提供默认渲染功能。如需自定义渲染,请使用 IVideoSinkBase
。
你可以使用 SDK 内置的 Audio Device Module (ADM) 直接播放本地音频。
由于篇幅过长,后续内容由下章介绍,具体内容可参考官方文档:从本地发送媒体流至 SD-RTN™ (agora.io)
评论