本文介绍智能摄像头场景的构建流程。智能摄像头场景的典型功能如下:
下文以功能维度介绍智能摄像头场景的构建流程。
在呼叫建立过程中,客户端可以通过绑定的账号呼叫设备端。设备端将采集的音视频信号传输给客户端。客户端也可以向设备端传输音视频信号。但是在智能摄像头场景中,一般只涉及客户端向设备端传输音频信号。
你可以通过以下步骤在自己的业务后台实现:
你需要在业务后台自行实现:
客户端和设备端收到业务后台下发的参数后,分别使用声网 RTC 视频 SDK 和 RSTA Lite SDK,以及下发参数加入 RTC 频道。加入成功之后,客户端和设备端就可以互相进行音视频通信。
你可以通过以下步骤实现。
设备端调用 RTSA Lite SDK 中的 agora_rtc_join_channel
加入 RTC 频道。收到 on_join_channel_success
回调后,就可以开始发送音视频数据。下面的示例代码展示了设备端 RTSA Lite SDK 的调用逻辑。
示例代码仅供参考,具体用法参考 RSTA Lite SDK 文档。
// C
// 示例代码基于 1.8.0 版本的 RTSA Lite SDK
// 加入 RTC 频道,需要使用业务后台下发的参数
rval = agora_rtc_join_channel(g_conn_id, CHANNEL_NAME, CALLEE_UID, TOKEN, &channel_options);
if (rval < 0) {
printf("Failed to join channel \"%s\", reason: %s\n", CHANNEL_NAME, agora_rtc_err_2_str(rval));
return -1;
}
// 收到 on_join_channel_success 回调后,启动音视频发送线程
static bool g_connected_flag = false;
static void __on_join_channel_success(connection_id_t conn_id, uint32_t uid, int elapsed)
{
g_connected_flag = true;
agora_rtc_get_connection_info(conn_id, &g_conn_info);
printf("[conn-%u] Join the channel %s successfully, uid %u elapsed %d ms\n", conn_id, g_conn_info.channel_name, uid,
elapsed);
}
while (!g_connected_flag) {
usleep(100 * 1000);
}
pthread_t video_thread_id;
pthread_t audio_thread_id;
rval = pthread_create(&video_thread_id, NULL, video_send_thread, 0);
if (rval < 0) {
printf("Unable to create video push thread\n");
return -1;
}
rval = pthread_create(&audio_thread_id, NULL, audio_send_thread, 0);
if (rval < 0) {
printf("Unable to create audio push thread\n");
return -1;
}
pthread_join(video_thread_id, NULL);
pthread_join(audio_thread_id, NULL);
发送关键帧
设备端发送的音视频数据可以为编码后的码流文件(比如 h.264 或 Opus)或 PCM 文件。如果在接收端出现解码错误,或者频道里加入新终端时,SDK 内部会请求发送关键帧。你需要在设备端发端通过 on_key_frame_gen_req
回调通知应用层,应用层需要根据回调立即指示编码器编码 I 帧并发送给接收端,否则可能出现长时间的卡顿,黑屏等问题。详见发送关键帧。
根据带宽调节目标码率
摄像头一般会支持多个清晰度档位,如标清、高清、超高清、4K 等,常见帧率包括15fps 和 25fps。
你可以通过 agora_rtc_set_bwe_param
设置 bandwidth estimation(BWE) 参数。
BWE 参数表示网络带宽探测的边界,你可以根据场景要求,预测编码的码率可能的最大最小波动范围来设置。
档位 | 分辨率 | 帧率 | 码率范围(kbps) |
---|---|---|---|
标清 | L1: 640*360 | 15 | 400 ~ 800 |
高清 | L2: 1280*720 | 15 | 1130 ~ 2260 |
超高清 | L3: 1920*1080 | 15 | 1130 ~ 4160 |
如上表中配置情况,可设置最小值 400 kbps,最大值 4160 kbps。初始值是带宽探测的初始回调值,实际设置介于最小值和最大值中间,一般设置为正常情况编码的码率。如果初始编码档位为上表的标清档,初始值可设为 500 kbps。
发端会持续探测可用带宽,通过 on_target_bitrate_changed
回调告知应用当前可用带宽,设备端需要做码率跟随,避免超码率发送,引起卡顿等问题。示例处理方法如下:
// C
// 配置 BWE
uint32_t min_bps = 400000;
uint32_t max_bps = 4160000;
uint32_t start_bps = 500000;
agora_rtc_set_bwe_param(min_bps, max_bps, start_bps);
// 通知设备端做码率跟随
static void __on_target_bitrate_changed(const char *channel, uint32_t target_bps)
{
// 当前码率按照 100 K 分档,向下取整
curTargetBitrate = target_bps/100000 * 100;
diffTargetBitrate = abs(curTargetBitRate - lastTargetBitrate);
// 如果探测带宽与设置的编码码率相差 100 K 以上,则重写调整编码参数
if ( (diffTargetBitrate >= 100 ) && (lowBitrateL1 < curTargetBitrate < highBitrateL3) )
{
if (lowBitrateL1 < curTargetBitrate < highBitrateL1)
{
curResolutionLevel = L1;
}
else if (lowBitrateL2 < curTargetBitrate < highBitrateL2)
{
curResolutionLevel = L3;
}
else
{}
// 调整编码分辨率
if (curResolutionLevel != lastResolutionLevel)
{
setEncodeResolution(curResolutionLevel);
}
// 设置编码码率
setEncodeBitrate(curTargetBitrate);
lastResolutionLevel = curResolutionLevel;
lastTargetBitrate = curTargetBitrate;
}
}
客户端调用 RTC SDK 中的 joinChannel
加入 RTC 频道。加入成功后,SDK 会自动发送音视频信号。下面的示例代码展示了客户端 RTC SDK 的调用逻辑。
示例代码仅供参考,具体用法参考 RTC 视频 SDK 文档。
// Java
// 加入 RTC 频道,需要使用业务后台下发的参数
// 示例代码基于 4.0.0 版本的 RTC SDK
mRtcEngine.joinChannel(TOKEN, CHANNEL_NAME, CALLER_UID, options);
在传输指定格式的音视频数据时,RTSA Lite SDK 与 RTC SDK 可以实现互通。详见 与 RTC SDK 互通音视频数据
声网支持云端录制和客户端录制两种方式。你也可以自行实现设备端录制和存储功能,例如存储到本地的 SD 卡中。
在设备端加入 RTC 频道。你的业务后台通过云端录制 RESTful API 发起录制,自动将频道中的流推至第三方云存储中。在用户需要的时候可以随时拉流回看。下面的示例代码展示了使用云端录制 RESTful API 录制 RTC 频道内媒体流并存储到第三方云存储平台。
示例代码仅供参考,具体用法参考云端录制 RESTful API文档。
# 将 <appid> 替换为你的声网项目的 App ID
# 将 <resourceid> 替换为通过 acquire 方法获取的 resource ID
# 将 <mode> 替换为 individual(单流录制)或 composite(合流录制)
curl --location --request POST 'https://api.agora.io/v1/apps/<appid>/cloud_recording/resourceid/<resourceid>/mode/<mode>/start' \
# 将 <Authorization> 替换为 Basic HTTP 认证生成的 Base64 编码凭证
--header 'Authorization: Basic <Authorization>' \
--header 'Content-Type: application/json' \
--data-raw '{
# 将 <YourChannelName> 替换为你需要录制的频道名称
"cname": "<YourChannelName>",
# 将 <YourRecordingUID> 替换为你标识该录制服务的 UID
"uid": "<YourRecordingUID>",
"clientRequest": {
# 将 <YourToken> 替换成你在控制台获取的临时 Token
"token": "<YourToken>",
# 设置云存储相关参数
"storageConfig": {
"secretKey": "<YourSecretKey>",
"vendor": 0,
"region": 0,
"bucket": "<YourBucketName>",
"accessKey": "<YourAccessKey>"
},
# 设置录制相关参数
"recordingConfig": {
# 确保与声网 RTC SDK 的设置一致
"channelType": 0
}
}
}'
实时预览的过程中,用户控制客户端录制视频片段,直接存储于客户端本地。一般用于记录瞬间和社交分享。你可以使用声网 RTC SDK 的音视频录制功能实现客户端录制。
示例代码仅供参考,具体用法参考 RTC 视频 SDK 文档。
// Java
// 客户端录制
AgoraMediaRecorder mediaRecorder = mRtcEngine.getMediaRecorder();
mediaRecorder.startRecording(connection, config);
mediaRecorder.stopRecording(connection);
设备端检测到预定义的异常事件后,会将事件通知推送到客户端。如果客户端处于后台运行或关闭状态,你还需要集成离线推送服务,确保通知可以及时到达。你可以参考第三方推送集成集成环信的离线推送服务。
客户端需要通过信令系统进行设备端的摄像头旋转、分辨率调节等实时操作。你可以在客户端集成声网云信令 SDK 实现设备控制。RTSA Lite SDK 具备云信令接口,可以直接与云信令 SDK 进行通信。
使用信令接口需要通过云信令 RTM Token 鉴权。你需要自行在业务后台生成 RTM Token。业务后台会分配此次通话的 主叫用户 ID 和被叫用户 ID、及对应 RTM Token,分别下发至客户端和设备端。这些参数都是后续客户端使用的声网云信令(RTM) SDK 和设备端使用的 RTSA Lite SDK 登录云信令系统所需要的。
设备端通过 RTSA Lite SDK 的 agora_rtc_login_rtm
方法登录云信令系统。
示例代码仅供参考,具体用法参考 RSTA Lite SDK 文档。
// C
// 设备端通过 RTSA Lite SDK 登录云信令系统
rval = agora_rtc_login_rtm(p_config->p_rtm_uid, p_config->p_token, &rtm_handler);
if (rval < 0) {
printf("login rtm failed\n");
goto EXIT;
}
客户端通过云信令 SDK 的 login
方法登录云信令系统。
示例代码仅供参考,具体用法参考云信令 SDK 文档。
// Java
// 客户端通过云信令 SDK 登录 RTM 系统
mRtmClient.login(token, uid, new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
}
@Override
public void onFailure(ErrorInfo errorInfo) {
CharSequence text = "User: " + uid + " failed to log in to the RTM system!" + errorInfo.toString();
int duration = Toast.LENGTH_SHORT;
runOnUiThread(new Runnable() {
public void run() {
Toast toast = Toast.makeText(getApplicationContext(), text, duration);
toast.show();
}
});
}
});
设备端通过 RTSA Lite SDK 的 agora_rtc_send_rtm_data
方法和 _on_rtm_data
回调发送和接收信令。
示例代码仅供参考,具体用法参考 RSTA Lite SDK 文档。
// c
// 接收你登录的 RTM 系统中对端发送信令
static void __on_rtm_data(const char *user_id, const void *data, size_t data_len)
{
app_t *p_app = app_get_instance();
app_config_t *p_config = &p_app->config;
if (p_config->rtm_role == 3) {
// printf("Receive data[%s] from user[%s] length[%lu]\n", (char *)data, user_id, data_len);
printf("Receive data[%s] length[%d]\n", (char *)data, (int)data_len);
} else {
printf("data_callback %s data[], length[%lu]\n", user_id, data_len);
}
if (p_app->config.rtm_recv_dump_flag && p_app->rtm_recv_file_fd != INVALID_FD) {
if (write(p_app->rtm_recv_file_fd, data, data_len) != data_len) {
printf("write error\n");
return;
}
}
}
// 监听本地用户状态
static void __on_rtm_event(const char *user_id, uint32_t event_id, uint32_t event_code)
{
LOGD("%s event id[%u], event code[%u]", user_id, event_id, event_code);
if (event_id == 0 && event_code == 0) {
app_t *p_app = app_get_instance();
p_app->b_rtm_login_success_flag = 1;
}
}
// 监听本地信令发送结果
static void __on_rtm_send_data_res(uint32_t msg_id, uint32_t error_code)
{
LOGD("msg id [%u], error_code[%u]", msg_id, error_code);
}
// 向指定 RTM 用户发送信令
rval = agora_rtc_send_rtm_data(p_app->config.p_peer_uid, ++message_id, buffer, rval);
if (rval < 0) {
LOGE("%s send data failed, rval=%d", TAG_API, rval);
goto EXIT;
}
客户端通过云信令 SDK 的 sendMessageToPeer
方法和 onMessageReceived
回调发送和接收信令。
示例代码仅供参考,具体用法参考云信令 SDK 文档。
// Java
// 发送信令
mRtmClient.sendMessageToPeer(peer_id, message, option, new ResultCallback<Void>() {
@Override
public void onSuccess(Void aVoid) {
String text = "Message sent from " + uid + " To " + peer_id + " : " + message.getText() + "\n";
writeToMessageHistory(text);
}
@Override
public void onFailure(ErrorInfo errorInfo) {
String text = "Message fails to send from " + uid + " To " + peer_id + " Error : " + errorInfo + "\n";
writeToMessageHistory(text);
}
});
// 接收信令
@Override
public void onMessageReceived(RtmMessage rtmMessage, String peerId) {
String text = "Message received from " + peerId + " Message: " + rtmMessage.getText() + "\n";
writeToMessageHistory(text);
}
RTC SDK、云信令 SDK、RTSA Lite SDK、云端录制 RESTful API 和 Token 生成器必须使用相同的声网 App ID,否则无法互通。
集成 RTC SDK 时,请确保频道场景设为直播,用户角色设为主播。
// Java
ChannelMediaOptions options = new ChannelMediaOptions();
// 将用户角色设置为 BROADCASTER 。
options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
// 设置频道场景为 BROADCASTING。
options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
// 使用 RTC Token 加入频道。
// 你需要自行指定用户 ID,并确保其在频道内的唯一性。
mRtcEngine.joinChannel(token, channelName, 0, options);
RTC SDK 和 RTSA Lite SDK 的音视频接口使用 RTC Token 进行鉴权,云信令 SDK 和 RTSA Lite SDK 的云信令接口使用 RTM Token 进行鉴权。两者生成方式不同且不可混用。
对于 RTSA Lite SDK,两人互通需采用 on_audio_data
回调接收数据;3 人以上互通需要用 on_mixed_audio_data
回调接收数据。
与 RTC SDK 互通时,RTSA Lite SDK 的内置音频编解码器仅支持发送和接收单声道数据。声网建议你使用 16 KHz 采样率采集数据,以获得更好的播放效果。如果你采集的是双声道音频,需要先转化为单声道再进行编码。对于不同的音频编码格式,RTSA Lite SDK 支持的采样率详见API 参考。