写点什么

🏆【声网 Agora】「Linux 系统下实时音视频流速提升实现」

发布于: 2021 年 06 月 14 日
🏆【声网Agora】「Linux系统下实时音视频流速提升实现」

前提概要

声网实时码流加速产品(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 上方便、快捷地发起音频呼叫,联络客服坐席。

产品功能

发送和接收多种格式的音视频数据,视频数据:

  • 支持向 SD-RTN™ 发送以下格式的媒体流或媒体文件:

  • YUV + PCM

  • H.264 + PCM

  • H.264 + OPUS

  • YUV + AAC

  • H.264 + AAC

  • 支持从 SD-RTN™ 接收以下格式的媒体流:

  • YUV + PCM

  • H.264 + PCM

前提条件

下表列出了安装 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 gitsudo apt install libx11-dev libxcomposite-dev libxext-dev libxfixes-dev libxdamage-dev
//cmake 版本不得低于 3.5wget https://cmake.org/files/v3.6/cmake-3.6.2.tar.gztar xvf cmake-3.6.2.tar.gzcd cmake-3.6.2/./bootstrap --prefix=/usr/localmakesudo make install
复制代码

如果你的 Linux 服务器操作系统为 CentOS,则需要安装以下依赖:

sudo yum groupinstall "Development Tools"sudo yum install wgetsudo yum groupinstall X11
//cmake 版本不得低于 3.5wget https://cmake.org/files/v3.6/cmake-3.6.2.tar.gztar xvf cmake-3.6.2.tar.gzcd cmake-3.6.2/./bootstrap --prefix=/usr/localmakesudo make install
复制代码

集成 SDK

  1. 下载并解压最新的 Agora Linux SDK。软件包内容如下:

  2. 为你的项目准备所需库:将 include 文件夹添加到你的项目里。

  3. 如果你的网络环境设置了防火墙限制外网访问,将以下目标域名及对应的端口添加到防火墙白名单:

    .agora.io .edge.agora.io .agoraio.cn .edge.agoraio.cn

  4. 为调试方便,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 对象与声网服务器建立连接。

  1. 调用 createRtcConnection 创建 IRtcConnection 对象,用于与声网服务器建立连接。

  2. (可选)你可以通过 VideoSubscriptionOptions 和 AudioSubscriptionOptions 定义音视频订阅选项。

  3. 创建 IRtcConnectionObserver 对象(即示例代码中的 SampleConnectionObserver 对象),用于监测连接事件。然后通过 registerObserver 方法将 IRtcConnectionObserver 对象和 IRtcConnection 对象关联起来。

  4. 调用 connect 与声网服务器建立连接。


// Configure audio subscription optionsagora::rtc::AudioSubscriptionOptions audioSubOpt;audioSubOpt.bytesPerSample = sizeof(int16_t) * options.audio.numOfChannels;audioSubOpt.numberOfChannels = options.audio.numOfChannels;audioSubOpt.sampleRateHz = options.audio.sampleRate;
// Configure connection optionsagora::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 connectionagora::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 useragora::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 eventauto connObserver = std::make_shared<SampleConnectionObserver>();connection->registerObserver(connObserver.get());
// Connect to Agora channelif (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;}
复制代码


媒体流接收准备

  1. 创建 ILocalUserObserver 对象(即示例代码中的 SampleLocalUserObserver 对象)。

  2. 注册音视频帧观测器。成功注册观测器后,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 framebool 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 frameint 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;};
复制代码

断开连接并释放资源

媒体流传输完成后,你可以通过以下步骤退出频道并断开与声网服务器的连接:

  1. 注销音视频帧观测器。

  2. 调用 disconnect 断开与声网服务器的连接。

  3. 释放 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)

发布于: 2021 年 06 月 14 日阅读数: 39
用户头像

我们始于迷惘,终于更高水平的迷惘。 2020.03.25 加入

🏆 【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 🤝未来我们希望可以共同进步🤝

评论

发布
暂无评论
🏆【声网Agora】「Linux系统下实时音视频流速提升实现」