本文以发送 PCM 格式的音频流和 YUV 格式的视频流为例,介绍从声网的 SD-RTN™ 发送媒体流的基本流程以及集成过程中的注意事项。
下图分别展示了发送本地音频和视频数据的数据处理和流转过程:
其中:
IVideoSinkBase
或 IVideoRenderer
实现本地预览。IVideoRenderer
为 IVideoSinkBase
的派生类,提供默认渲染功能。如需自定义渲染,请使用 IVideoSinkBase
。首先,你需要在你的应用程序里调用 createAgoraService
和 initialize
进行全局初始化,创建并初始化 AgoraService
对象。
这个操作只需要进行一次,AgoraService
对象的生命期和应用程序的生命期保持一致,只要应用程序没有退出,AgoraService
可以一直存在。
createAndInitAgoraService
为 wrapper 函数,实现代码可参考 SDK 包 agora_sdk_demo/samples/common 路径下的 sample_common.cpp
文件。auto service = createAndInitAgoraService(false, true, true);
初始化后,根据以下步骤让本地 AgoraService
对象与声网服务器建立连接。
createRtcConnection
创建 IRtcConnection
对象,用于与声网服务器建立连接。IRtcConnectionObserver
对象(即示例代码中的 SampleConnectionObserver
对象),用于监测连接事件。然后通过 registerObserver
方法将 IRtcConnectionObserver
对象和 IRtcConnection
对象关联起来。connect
与声网服务器建立连接。// Create声网connection
agora::rtc::RtcConnectionConfiguration ccfg;
ccfg.autoSubscribeAudio = false;
ccfg.autoSubscribeVideo = false;
ccfg.clientRoleType = agora::rtc::CLIENT_ROLE_BROADCASTER;
agora::agora_refptr<agora::rtc::IRtcConnection> connection = service->createRtcConnection(ccfg);
if (!connection) {
AG_LOG(ERROR, "Failed to creating声网connection!");
return -1;
}
// Register connection observer to monitor connection event
auto connObserver = std::make_shared<SampleConnectionObserver>();
connection->registerObserver(connObserver.get());
// Connect to声网channel
if (connection->connect(options.appId.c_str(), options.channelId.c_str(),
options.userId.c_str())) {
AG_LOG(ERROR, "Failed to connect to声网channel!");
return -1;
}
IMediaNodeFactory
对象。IAudioPcmDataSender
对象和 IVideoFrameSender
对象,分别用于发送 PCM 格式的音频流和 YUV 格式的视频流。ILocalAudioTrack
对象和 ILocalVideoTrack
对象,分别对应即将发布至频道内的本地音频轨道和本地视频轨道。ILocalUser
对象的 publish
相关方法发布上一步创建的本地音频轨道和视频轨道。 // Create media node factory
agora::agora_refptr<agora::rtc::IMediaNodeFactory> factory = service->createMediaNodeFactory();
if (!factory) {
AG_LOG(ERROR, "Failed to create media node factory!");
}
// Create audio data sender
agora::agora_refptr<agora::rtc::IAudioPcmDataSender> audioPcmDataSender =
factory->createAudioPcmDataSender();
if (!audioPcmDataSender) {
AG_LOG(ERROR, "Failed to create audio data sender!");
return -1;
}
// Create audio track
agora::agora_refptr<agora::rtc::ILocalAudioTrack> customAudioTrack =
service->createCustomAudioTrack(audioPcmDataSender);
if (!customAudioTrack) {
AG_LOG(ERROR, "Failed to create audio track!");
return -1;
}
// Create video frame sender
agora::agora_refptr<agora::rtc::IVideoFrameSender> videoFrameSender =
factory->createVideoFrameSender();
if (!videoFrameSender) {
AG_LOG(ERROR, "Failed to create video frame sender!");
return -1;
}
// Create video track
agora::agora_refptr<agora::rtc::ILocalVideoTrack> customVideoTrack =
service->createCustomVideoTrack(videoFrameSender);
if (!customVideoTrack) {
AG_LOG(ERROR, "Failed to create video track!");
return -1;
}
// Configure video encoder
agora::rtc::VideoEncoderConfiguration encoderConfig(
options.video.width, options.video.height, options.video.frameRate,
options.video.targetBitrate, agora::rtc::ORIENTATION_MODE_ADAPTIVE);
customVideoTrack->setVideoEncoderConfiguration(encoderConfig);
// Publish audio & video track
customAudioTrack->setEnabled(true);
connection->getLocalUser()->publishAudio(customAudioTrack);
customVideoTrack->setEnabled(true);
connection->getLocalUser()->publishVideo(customVideoTrack);
一般情况下,当音频编码器和视频编码器完成一帧数据的编码后,会以回调的方式通知应用程序。你需要在提示编码完成的回调函数中添加发送媒体流的逻辑。每次收到提示编码完成的回调,都需要触发 sendAudioPcmData
和 sendEncodedVideoImage
方法的调用,分别用于发送音频帧和视频帧。换言之,你不需要单独创建一个线程用于发流,直接在编码器的输出回调里进行发流即可。
对于音频帧,调用 sendAudioPcmData
方法时,你需要传入 samplesPer10ms
(每 10 毫秒的 sample 个数)、sampleSize
(每个 sample 的大小)、numOfChannels
和 sampleRate
这四个参数。目前 Linux SDK 每次只能发送 10 毫秒的 PCM 数据。
// Send one PCM frame
if (audioPcmDataSender->sendAudioPcmData(frameBuf, 0, samplesPer10ms, sampleSize,
options.audio.numOfChannels,
options.audio.sampleRate) < 0) {
AG_LOG(ERROR, "Failed to send audio frame!");
}
// Send one YUV frame
if (videoFrameSender->sendVideoFrame(videoFrame) < 0) {
AG_LOG(ERROR, "Failed to send video frame!");
}
媒体流传输完成后,你可以通过以下步骤退出频道并断开与声网服务器的连接:
unpublish
相关方法取消发布音频流和视频流。disconnect
断开与声网服务器的连接。IRtcConnection
对象相关的资源。// Unpublish audio & video track
connection->getLocalUser()->unpublishAudio(customAudioTrack);
connection->getLocalUser()->unpublishVideo(customVideoTrack);
// Unregister connection observer
connection->unregisterObserver(connObserver.get());
// Disconnect from声网channel
if (connection->disconnect()) {
AG_LOG(ERROR, "Failed to disconnect from声网channel!");
return -1;
}
AG_LOG(INFO, "Disconnected from声网channel successfully");
// Destroy声网connection and related resources
connObserver.reset();
audioPcmDataSender = nullptr;
videoFrameSender = nullptr;
customAudioTrack = nullptr;
customVideoTrack = nullptr;
factory = nullptr;
connection = nullptr;
当你的应用程序退出时,可以调用如下逻辑,注销整个 AgoraService
。
// Destroy声网 Service
service->release();
service = nullptr;
Linux SDK 支持使用 String 型用户 ID 加入频道。使用时,你需要注意以下限制:
createAndInitAgoraService
时,需要将 enableuseStringUid
设为 true
。connect
方法时,你必须给 userId
赋值。设置为 null
会收到报错。具体代码可参考 sample_stringuid_send.cpp
示例项目。
你可以使用 encryptionMode
参数设置加密算法以及加密密钥。具体代码可参考 sample_send_encrypted_h264.cpp
示例项目。
// Set the encryption mode
if (options.encryptionMode == true) {
agora::rtc::EncryptionConfig Config;
Config.encryptionMode = agora::rtc::SM4_128_ECB;
Config.encryptionKey = options.encryptionKey.c_str();
if (connection->enableEncryption(options.encryptionMode, Config) < 0) {
AG_LOG(ERROR, "Failed to enable encryption!");
}
AG_LOG(INFO, "Enable encryption successfully");
}
LIVE_BROADCASTING
。我们在 SDK 中提供了一系列发送媒体流的示例项目,供你参考:
示例项目 | 实现功能 |
---|---|
sample_send_aac.cpp |
发送 AAC 帧。 |
sample_send_encrypted_h264.cpp |
发送加密后的 H.264 帧。 |
sample_send_h264_opus.cpp |
发送 H.264 帧和 OPUS 帧。 |
sample_send_h264_pcm.cpp |
发送 H.264 帧和 PCM 帧。 |
sample_send_yuv_dual_stream.cpp |
发送 YUV 大小流。 |
sample_send_h264_dual_stream.cpp |
发送 H.264 大小流。 |
sample_send_yuv_pcm.cpp |
发送 YUV 帧和 PCM 帧。 |
sample_stringuid_send.cpp |
使用 String 型用户 ID 加入频道并发流。 |