背景
- 摄像机设备需要支持远程控制(如分辨率、音量、网络配置等),并能够将设备状态(网络信息、运行状态、事件告警)实时上报给用户。这类能力本质上依赖于消息的发布/订阅机制。
- 传统方案通常使用 MQTT,但需要自建 MQTT 服务器,并在全球范围部署节点,运维成本和接入复杂度较高。相比之下,声网 RTM 具备与 MQTT 类似的实时消息能力,同时提供全球节点覆盖和完善的 SDK,使用户在使用时更加轻量、简洁、无感化运维。
《网络摄像机实战》系列文章,请读者前往阅读 👀
《网络摄像机实战①:Flutter 集成声网 Agora 打造实时音视频对讲》
《网络摄像机实战②:Flutter集成声网Agora图片抓拍与录像功能》
《网络摄像机实战③:设备端集成声网Agora打造实时音视频对讲功能》
《网络摄像机实战④:设备端集成声网Agora 打造远程控制设备或者异常报警推送功能》
《网络摄像机实战⑤:手机端 flutter集成声网Agora RTM打造远程控制设备功能》
《网络摄像机实战⑥:手机端 flutter集成亚马逊 AWS Kinesis Video Streams 音视频界面和控制》
《网络摄像机实战⑦:嵌入式端AWS KVS IoT SDK 库编译与测试全流程-君正/瑞芯微/全志(超详细指南)》
《网络摄像机实战⑧:嵌入式端接入 AWS KVS Producer 的设计要点与注意事项》
声网RTM介绍
实时消息(Real-Time Messaging,RTM)为开发者提供一整套低延时、高并发、可扩展、高可靠的实时消息及状态同步解决方案。RTM 负责管理应用程序实时通信层所需的基础设施。为方便用户开发与创新,RTM 在保障 99.95% 的 SLA 正常运行时间的同时,提供丰富的 App 及开放的第三方 API 扩展。
RTM的核心概念:
用户: 是指应用的实际使用者。 设备: 是指实际接入 RTM 网络的终端,包括但不限于手机、电脑、智能手表等 IoT 设备。 客户端: 是指运行在设备上的 RTM 对象实例。
实例化 RtmClient 并且登录 rtm 服务器
late RtmClient rtmClient;
Future<void> agoraRtmInit() async
{
Get.log("agoraRtmInit userID: ${GlobalInformation().getCurrentDeviceUser().user.id}");
// final record = await RTM(appId, channelName.value);
// final record = await RTM(appId, GlobalInformation().getCurrentDeviceUser().user.id);
var userId = randomDigits(9);
Get.log("rtm userId : ${userId}");
final record = await RTM(appId, userId);
loginStatus = record.$1;
Get.log("loginStatus ${loginStatus.reason}-${loginStatus.errorCode}");
if (loginStatus.error == false) {
Get.log("create rtm success");
rtmClient = record.$2;
rtmClient.addListener(
linkState: (event) {
Get.log('[linkState] ${event.toJson()}');
},
// 订阅主题的消息处理。
message: (event) {
Get.log('[message] event: ${event.toJson()}');
Get.log('event.message:${utf8.decode(event.message!)}');
},
presence: (event) {
Get.log('[presence] event: ${event.toJson()}');
},
topic: (event) {
Get.log('[topic] event: ${event.toJson()}');
},
lock: (event) {
Get.log('[lock] event: ${event.toJson()}');
},
storage: (event) {
Get.log('[storage] event: ${event.toJson()}');
},
token: (channelName) {
Get.log('[token] channelName: $channelName');
},
);
await rtmClient.setParameters('{"rtm.log_filter":2063}');
await agoraRtmLogin();
// 后续逻辑
} else {
Get.log("create rtm failed ${loginStatus.reason}");
}
}
登录成功之后,订阅通道的消息
Future<void> agoraRtmSubscribe() async
{
Get.log("agoraRtmSubscribe ${channelName.value}");
try {
final record = await rtmClient.subscribe(
channelName.value,
withMessage: true,
withPresence: true,
withMetadata: true,
withLock: true,
);
Get.log(' errorCode: ${record.$1.errorCode}, reason: ${record.$1.reason}');
} catch (e) {
Get.log('something went wrong: $e');
}
}
发送电机控制指令到设备
Future<int> setMotorControlInformation()
async {
MotorControlInformationEntity motorInformationEntity = MotorControlInformationEntity();
motorInformationEntity.motorControlInformation = motorControlInformation.value;
Map<String, dynamic> requestJson = {};
requestJson[MQTT_COMMAND_HANDLE_METHOD] = SET_MOTORCONTROL_INFORMATION_COMMAND ;
requestJson[MQTT_COMMAND_HANDLE_PARAMS]=motorInformationEntity.toJson();
Get.log(json.encode('$requestJson'));
rtmClient.publish(channelName.value, json.encode(requestJson));
// await MqttService().mqttPublishMessage("v1/devices/" + GlobalInformation().getCurrentDeviceUser().id + "/rpc/request/01", json.encode(requestJson));
DeviceInformation().setMotorControlInformation(motorControlInformation.value);
// 保存到缓存
ApplicationCacheModule().ApplicationCacheHandleFunction(SAVE_CACHE_DEVICE_MOTOR_CONTROL_INFORMATION, motorControlInformation.value.toJson());
motorControlInformation.refresh();
return 0;
}
回调函数处理设备的状态信息或者报警
// 订阅主题的消息处理。
message: (event) {
Get.log('[message] event: ${event.toJson()}');
Get.log('event.message:${utf8.decode(event.message!)}');
handleMessage(message)
},
退出 rtm
await rtmClient.logout();
App截图
友情提示🔔
🙏 感谢你的阅读! 如果这篇文章对你有所启发,欢迎关注我 ⭐,欢迎点击 “打赏支持作者” 支持一下我,你的支持是我持续创作的最大动力! 我会持续分享更多关于 智能摄像头 📷、机器人项目、 🤖音视频 RTC 🎧、App 开发 📱、嵌入式开发 🔧 等方向的实战经验,让你更快落地、更少踩坑。 欢迎浏览我其他文章 📚,或许能解决你当前的难题。 如果你正好在做相关项目产品,也欢迎随时私信我,一起技术交流、一起搞事情! 🤝💬📞 联系微信/电话:13826173658