快速接入

本文介绍C++ 集成实时音视频Linux ARTC SDK。

一、准备工作

解压Linux SDK压缩包,打开解压后的文件夹里面的Cpp文件夹,交付产物包括如下

├── Release
│   ├── include ##此目录包含需要引入的头文件
│   │   ├── AliRTCEngineCentralInterface.h
│   │   └── AliRTCSdkDefineCentral.h
│   └── lib ##此目录包含需要链接的SDK动态库
│       ├── AliRtcCoreService
│       ├── libAliRtcLinuxEngine.so
│       └── libPluginOpus.so
└── Demo ##简易例程
    ├── CMakeLists.txt
    ├── fake_linux_event_listener.cc ##包含音、视频拉流的回调逻辑
    ├── fake_linux_event_listener.h
    └── simple_main.cc ##demo主体,包含初始化、入会、推流等操作
说明

Release目录包含需要引入的头文件(include)、需要链接的SDK动态库(lib)。API与数据结构均在include目录中的各头文件中声明,要求配置正确的动态库链接地址,如:export LD_LIBRARY_PATH=./lib

Demo目录提供简易例程,其中simple_main.cc是demo主体,包含初始化、入会、推流等操作,fake_linux_event_listener包含音、视频拉流的回调逻辑。

重要

执行Demo请确保开发环境g++版本在4.8以上。

二、核心API

1.初始化SDK

  1. 实现EventHandler类,该类型用于拉流回调,收到数据将触发其中的回调函数

  2. 创建EventHandler实例

  3. 调用AliRTCEngineInterface.h中的CreateAliRTCEngine函数创建AliRTCEngine实例,过程中会将EventHandler实例注册为engine的回调对象。后面只需要调用AliRTCEngine实例的各方法完成推拉流设置

  4. 如果AliRTCEngine实例创建失败,需要销毁EventHandler实例

说明

每创建一个AliRTCEngine,将会启动一个进程对应一个虚拟用户。

示例如下:

//实现EventHandle类
...

//初始化SDK
AliRTCSdk::Central::FakeLinuxEngineEventHandler *engineEventHandler = new AliRTCSdk::Central::FakeLinuxEngineEventHandler();
std::string extra = "";
AliRTCSdk::Central::AliRTCEngineInterface *linuxEngine = AliRTCSdk::Central::CreateAliRTCEngine(engineEventHandler, 42000, 45000, logPath.c_str(), nullptr, false, extra.c_str());

if (linuxEngine == nullptr) {
    delete engineEventHandler;
    engineEventHandler = nullptr;
    return;
}

CreateAliRTCEngine函数的参数如下:

  • EngineEventHandlerInterface * eventHandler:回调对象,负责处理回调逻辑

  • int lowPort:端口号下限,lowPort~highPort之间的端口将被随机选择用于进程间通信

  • int highPort:端口号上限

  • const char * logPath:SDK运行过程中,日志文件的保存路径

  • const char * coreServicePath:AliRtcCoreService的实际路径

  • bool h5mode:h5兼容模式,一般false即可

  • const char * extra:对SDK进行额外配置时传入的JSON格式字符串

2.入会

请先了解鉴权功能,详见Token鉴权

说明

入会存在两个版本的接口,其中单参数入会是在多参数入会的基础上做的语法糖,剥离了业务方传入nonce、timestamp以入会的必要性,在直接获取token的场景下更易于使用。

2.1 多参数入会接口

  1. 获取authInfo,以使用token完成鉴权

  2. 配置JoinChannelConfig,目前先使用默认配置

  3. 调用JoinChannel

// 获取authInfo
AliRTCSdk::Central::AuthInfo authInfo;
authInfo.appid = "";
authInfo.channel = "";
authInfo.userid = "";
authInfo.username = "";
authInfo.nonce = "";
authInfo.token = "";
authInfo.timestamp = 1583582330; //sample

int gslbCount = /* */;
authInfo.gslb_count = gslbCount;
const char *gslbArray[gslbCount];
if (gslbCount > 0)
{
    for(int i = 0; i < gslbCount; i++)
    {
        gslbArray[i] = "";
    }
    authInfo.gslb = gslbArray;
}

int agentCount = /* */;
authInfo.agent_count = agentCount;
const char *agentArray[agentCount];
if (agentCount > 0)
{
    for (int i = 0; i < agentCount; i++)
    {
        agentArray[i] = "";
    }
    authInfo.agent = agentArray;
}

//配置JoinChannelConfig
AliRTCSdk::Central::JoinChannelConfig config;

//打开自动推流功能(如果业务需要也可以采用手动推流模式,先入会,再开启推流)
config.publishMode = AliRTCSdk::Central::PublishAutomatically;

//关闭自动录制功能(如无需录制)
config.subscribeMode = AliRTCSdk::Central::SubscribeManually;

//入会自动订阅远端视频,详见AliRTCSdk::Central::VideoFormat
config.subscribeVideoFormat = 1;

//入会自动订阅远端音频,详见AliRTCSdk::Central::AudioFormat
config.subscribeAudioFormat = 2;
//若订阅所有远端用户的混合音频流,设置 config.subAudioMode = 1

//设置channelprofile,通常需要手动设定为互动模式
config.channelProfile = AliRTCSdk::Central::ChannelProfileInteractiveLive;

//设置是否推camera流,置为false则不推camera视频流
linuxEngine->PublishLocalVideoStream(true);

//设置是否推音频流,置为false则不推音频流
linuxEngine->PublishLocalAudioSrtream(true);

//设置视频相机流的编码配置
AliRTCSdk::Central::AliEngineVideoEncoderConfiguration videoConfig;
linuxEngine->SetVideoEncoderConfiguration(videoConfig);
  
//开启Yuv输入,使用camera流进行推送
linuxEngine->SetExternalVideoSource(true, AliRTCSdk::Central::VideoSourceCamera, AliRTCSdk::Central::RenderModeAuto);

//开启Pcm输入
//第二个参数为pcm的采样率,请根据实际情况设置
//第三个参数为pcm的channel数,请根据实际情况设置
linuxEngine->SetExternalAudioSource(true, 16000, 2);

//设置用户模式:主播或观众
// 观众AliEngineClientRoleLive:只拉流不推流,且上线时远端用户无感知
// 主播AliEngineClientRoleInteractive:具有推流的能力,无论是否推流,远端用户都会有感知
linuxEngine->SetClientRole(AliRTCSdk::Central::AliEngineClientRoleInteractive);

//入会
linuxEngine->JoinChannel(authInfo, config);

2.2 单参数入会接口

  1. 获取token,和用户名、房间号等基本信息

  2. 配置JoinChannelConfig

  3. 调用重载的JoinChannel

// 获取入会所需基本信息
const char* token = "";    // 业务方传入
const char* channel = "";  // 房间号
const char* userid = "";   // 用户ID
const char* username = ""; // 用户名
//配置JoinChannelConfig
AliRTCSdk::Central::JoinChannelConfig config;
// ..... 配置选项同上
linuxEngine->JoinChannel(token, channel, userid, username, config);
    说明

    请勿重复调用JoinChannel

    Demo中演示了用GenerateToken模拟appserver生成入会参数的过程,该接口是专为简化开发、测试而提供的。

    生产环境中,请通过与app server的交互获取token,避免appkey泄漏。

3.手动推流

以下API设置是否推送音视频流。

   /**
    * @brief 是否推送本地视频(摄像头)流
    * @param enabled 是否开启/关闭本地视频流推送
    - true: 开启视频流推送
    - false: 关闭视频流推送
    * @return
    - 0: 设置成功
    - <0: 设置失败,返回错误码
    * @note SDK默认设置推送视频流,在加入频道前也可以调用此接口修改默认值,并在加入频道成功时生效
    */
    virtual int PublishLocalVideoStream(bool enabled) = 0;

   /**
    * @brief 是否推送本地音频流
    * @param enabled 是否开启/关闭本地音频流推送
    - true: 开启音频流推送
    - false: 关闭音频流推送
    * @return
    - 0: 设置成功
    - <0: 设置失败,返回错误码
    * @note SDK默认设置推送音频流,在加入频道前也可以调用此接口修改默认值,并在加入频道成功时生效
    */
    virtual int PublishLocalAudioStream(bool enabled) = 0;

4.推YUV视频流

/**
 * @brief 启用外部视频输入源
 * @param enable true 开启, false 关闭
 * @param useTexture 是否使用texture 模式,目前只支持false
 * @param type 流类型
 * @note 启用后使用PushExternalVideoFrame接口输入视频数据
 */
virtual int SetExternalVideoSource(bool enable, bool useTexture, AliRTCSdk::Central::VideoSource sourceType) = 0;

/**
 * @brief 输入外部输视频
 * @param frame 帧数据
 * @param type 流类型
 * @param 支持的输入视频类型请见VideoDataFormat
 */
virtual int PushExternalVideoFrame(AliRTCSdk::Central::VideoDataSample *frame, AliRTCSdk::Central::VideoSource sourceType) = 0;

5.推PCM音频流

/**
 * @brief 设置是否启用外部音频输入推流
 * @param enable true 开启,false 关闭
 * @param sampleRate 采样率 16k 48k...
 * @param channelsPerFrame 采样率 16k 48k...
 * @return >=0表示成功, <0表示失败
 * @note 可通过SetExternalAudioPublishVolume设置输入音频推流音量
 */
virtual int SetExternalAudioSource(bool enable, unsigned int sampleRate,  unsigned int channelsPerFrame) = 0;

/**
 * @brief 输入外部音频数据推流
 * @param audioSamples 音频数据
 * @param sampleLength 音频数据长度
 * @param timestamp 时间戳
 * @return <0表示失败,返回值为ERR_AUDIO_BUFFER_FULL时,需要在间隔投递数据时间长度后再次重试投递
 */
virtual int PushExternalAudioFrameRawData(const void* audioSamples, unsigned int sampleLength, long long timestamp) = 0;

/**
 * @brief 设置外部输入音频推流混音音量
 * @param vol 音量 0-100
 */
virtual int SetExternalAudioPublishVolume(int volume) = 0;

/**
 * @brief 获取外部输入音频推流混音音量
 * @return vol 音量
 */
virtual int GetExternalAudioPublishVolume() = 0;

6.手动订阅

说明

在自动订阅的模式下,无需调用这部分接口拉流

/**
 * @brief 停止/恢复订阅特定远端用户的音频流, 用于会中调用, 会前调用无效
 * @param uid 用户ID,从App server分配的唯一标示符
 * @param sub 是否订阅远端用户的音频流
 * - true:订阅指定用户的音频流
 * - false:停止订阅指定用户的音频流
 * @return
 * - 0: 成功
 * - 非0: 失败
 */
virtual int SubscribeRemoteAudioStream(const char* uid, bool sub) = 0;

/**
* @brief 停止/恢复订阅远端用户的视频流, 用于会中调用, 会前调用无效
* @param uid 用户ID,从App server分配的唯一标示符
* @param track 视频流类型
* - AliEngineVideoTrackNo: 无效参数,设置不会有任何效果
* - AliEngineVideoTrackCamera: 相机流
* - AliEngineVideoTrackScreen: 屏幕共享流
* - AliEngineVideoTrackBoth: 相机流和屏幕共享流
* @param sub 是否订阅远端用户的视频流
* - true:订阅指定用户的视频流
* - false:停止订阅指定用户的视频流
* @return
* - 0:设置成功
* - <0:设置失败
* @note
*/
virtual int SubscribeRemoteVideoStream(const char* uid, AliRTCSdk::Central::VideoTrack videoTrack, bool sub) = 0;

7.数据回调

7.1 音频

audioFormat选择AudioFormatPcmBeforMixing模式后,收到音频帧将触发EventHandler实例的回调函数OnSubscribeAudioFrame

  • uid: 表示此时收到的音频帧来自哪个远端用户,借此区分订阅的各路音频流

  • frame: 收到的音频帧,pcm格式

void OnSubscribeAudioFrame(const std::string& uid, const AliRTCSdk::Central::AudioFrame * frame)

选择AudioFormatMixedPcm模式,收到音频帧将触发EventHandler实例的回调函数OnSubscribeMixAudioFrame

void OnSubscribeMixAudioFrame(const AliRTCSdk::Central::AudioFrame * frame)

7.2 视频

订阅视频帧不存在混合流,videoFormat置为VideoFormatH264后,收到视频帧将触发EventHandler实例的回调函数OnRemoteVideoSample

  • uid: 表示此时收到的视频帧来自哪个远端用户,借此区分订阅的各路视频流

  • frame: 收到的视频帧,yuv格式

void OnRemoteVideoSample(const char * uid, const AliRTCSdk::Central::VideoFrame * frame)

8.离会

/* 结束推流 */
linuxEngine->PublishLocalVideoStream(false);
linuxEngine->PublishLocalAudioStream(false);

若要在停止推流后,仍在会中,请手动调用此接口;直接离会也具有停止推流的效果

linuxEngine->LeaveChannel();

9.销毁SDK

linuxEngine->Release();
linuxEngine = nullptr;

delete linuxEventHandler;
linuxEventHandler = nullptr;

三、Demo使用方式

在Demo目录执行下述命令

mkdir build
cd build
cmake ..
make

将在build目录得到可执行文件linux_sdk_simple_demo,直接执行./linux_sdk_simple_demo

若终端有以下输出,表示入会、推拉流成功

# 入会成功
[JoinChannelStateChanged] state: success

# 推流成功
[AudioPublishStateChanged] oldState:0, newState:2, elapseSinceLastState:0, channelId:9090
[VideoPublishStateChanged] oldState:0, newState:2, elapseSinceLastState:0, channelId:9090
[DualStreamPublishStateChanged] oldState:0, newState:2, elapseSinceLastState:0, channelId:9090
[AudioPublishStateChanged] oldState:2, newState:3, elapseSinceLastState:292, channelId:9090
[VideoPublishStateChanged] oldState:2, newState:3, elapseSinceLastState:293, channelId:9090
[DualStreamPublishStateChanged] oldState:2, newState:3, elapseSinceLastState:293, channelId:9090

# 远端用户abcd已上线
OnRemoteUserOnLineNotify userid: abcd

# 收到来自远端用户abcd的音视频流
[AudioSubscribeStateChanged] uid:abcd, oldState:3, newState:1, elapseSinceLastState:38865, channelId:9090
[VideoSubscribeStateChanged] uid:abcd, oldState:3, newState:1, elapseSinceLastState:38865, channelId:9090

音频订阅选择AudioFormatMixedPcm模式时,无论是否有远端用户在会上,入会时就会有音频回调。选择AudioFormatPcmBeforMixing时,只有会上有其他用户,且其他用户在推音频流时,才会触发音频回调。

想要退出离会时,在终端输入exit,等待其自行退出即可。

四、消息通讯

除音视频互动外,RTC SDK还支持实时消息收发,以用于有消息互动需求的场景。您可以通过Data Channel和SEI(Supplemental Enhancement Information)两种通道进行消息收发,前者独立于音视频传输通道,后者依赖视频数据收发,一般场景下建议使用Data Channel。

1. Data Channel

通过SendDataChannelMessage发送消息:

/**
 * @note 发送消息前需通过linuxEngine->SetParameter("{\"data\":{\"enablePubDataChannel\":true,\"enableSubDataChannel\":true}}");,打开DataChannel
 */
std::string message = "This is data channel message";
AliRTCSdk::Central::AliEngineDataChannelMsg dataChannelMsg;
dataChannelMsg.data = (void*)message.c_str();
dataChannelMsg.dataLen = message.length();
dataChannelMsg.networkTime = t0; // 网络时间字段,也可以自定义取值,不影响消息收发
dataChannelMsg.progress = 0; // 保留字段
dataChannelMsg.type = AliRTCSdk::Central::AliEngineDataChannelProgress;

linuxEngine->SendDataChannelMessage(dataChannelMsg);

当接收到Data Channel消息后,会触发回调:

/**
 * @brief 获得dataChannel远端数据
 * @param uid 发出消息的远端用户id
 * @param msg 远端传来的消息
 */
virtual void OnDataChannelMsg(const char* uid, AliRTCSdk::Central::AliEngineDataChannelMsg& msg) {}

2. SEI

/**
 * @brief 发送 媒体扩展信息(SEI), 最大长度为4*1024字节,用于业务的少量数据传输
 * @param message 扩展信息内容,可以传递4K Bytes数据
 * @param length 扩展信息长度,单位:字节
 * @param repeatCount 重复次数,用于防止网络丢包导致的消息丢失
 * @param delay 延迟多久发出去,单位:毫秒
 * @param isKeyFrame 是否只在关键帧上增加SEI
 * @return <0: 成功,-1: SDK内部错误>
*/
int SendMediaExtensionMsg(const char* message,
                          size_t length,
                          int repeatCount,
                          uint32_t delay,
                          bool isKeyFrame);

通过SendMediaExtensionMsg发送消息:

std::string seiMsg = "This is SEI message";
linuxEngine->SendMediaExtensionMsg(seiMsg.c_str(), seiMsg.length(), 3, 0 ,false);

由于关键帧间隔较长,若需要高频收发SEI消息,建议isKeyFrame设置为false。同时为保证消息有效送达,建议重复次数大于1。

当接收到SEI消息后,会触发回调:

/**
 * @brief 收到媒体扩展信息回调
 * @param uid 发送用户userId
 * @param message 扩展信息内容
 * @param size 扩展信息长度
 * @note 当一端通过 {@link SendMediaExtensionMsg} 发送信息后,其他端通过该回调接收数据
 */
virtual void OnMediaExtensionMsgReceived(const char* userid, const char* message, size_t size) {}

五、其他功能

创建引擎时的extra字段,可用于开闭部分额外功能。extra字段为JSON格式,各键对应不同配置项,修改示例如下。

Json::Value extraJson;
extraJson["key"] = value; // 填充字段示例
std::string extra = extraJson.toStyledString();

1. 关闭Audio Ranking

Audio Ranking是多人语聊房中,仅订阅音量较大几路音频流的功能,使听众听到的声音更为清晰,默认为开启状态。若场景需要订阅全部音频流,需要通过如下字段关闭Audio Ranking功能:

extraJson["user_specified_disable_audio_ranking"] = "true";

2. 开启订阅音频时的转码AAC功能

对于有拉流存档需求的场景,SDK提供将订阅到的远端音频转码至AAC编码的能力,并通过回调函数OnSubscribeAudioAac()返回。您可通过AudioTranscodingCodec中的各项选择以何种格式获取远端音频(PCM、AAC或二者皆要),若开启AAC转码功能,可以选择是否进行重采样以降低CPU和存储占用。当user_specified_audio_observer_resample_rate设置为0表示不进行重采样。

extraJson["user_specified_audio_observer_codec"] = 
    (int)AliRTCSdk::Central::AudioTranscodingCodecBothPcmAndAac;
extraJson["user_specified_audio_observer_codec_format"] = 0; // 目前只支持adts格式
extraJson["user_specified_audio_observer_resample_rate"] = 16000; // 重采样率
extraJson["user_specified_audio_observer_codec_bitrate"] = 64000; // 转码的目标码率

3. 开启订阅视频时直接获取H264码流功能

对于有拉流存档需求的场景,SDK提供直接获取远端视频H264码流的能力。您可通过VideoTranscodingCodec的各项,选择以何种格式获取远端视频(YUV、H264码流或二者皆要)。

extraJson["user_specified_video_observer_codec"] = 
    (int)AliRTCSdk::Central::VideoTranscodingCodecBothYuvAndH264;
extraJson["user_specified_video_observer_codec_format"] = 0; // 目前只支持annexb格式