《网络摄像机实战④:设备端集成声网Agora 打造远程控制设备或者异常报警推送功能》

69 阅读4分钟

背景

  • 摄像机设备需要支持远程控制(如分辨率、音量、网络配置等),并能够将设备状态(网络信息、运行状态、事件告警)实时上报给用户。这类能力本质上依赖于消息的发布/订阅机制。
  • 传统方案通常使用 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 对象实例。

基于IRtmEventHandler创建AgoraRtmService类,完成声网 RTM 的回调处理,以及核心处理类IRtmClient的实例


class AgoraRtmService : public IRtmEventHandler{
public:
    ~AgoraRtmService() override;
    
    ......
private:
    RtmConfig m_cfg;
    IRtmClient *p_rtm_client;

}

初始化 RTM,使用createAgoraRtmClient完成

int AgoraRtmService::startAgoraRtmService()  {

    m_cfg.appId = DEFAULT_APP_ID;
    // m_cfg.userId = DEFAULT_USER_ACCOUNT;
    // m_cfg.userId = nullptr;
    m_cfg.userId = Device::getInstance().getDeviceGeneralInformation().getDeviceUuid();

    m_cfg.eventHandler = this;
    m_cfg.presenceTimeout = 30;
    m_cfg.logConfig.level = RTM_LOG_LEVEL_INFO;
    m_cfg.logConfig.filePath = DEFAULT_RTM_LOG_PATH;
    m_cfg.useStringUserId = true;

    int errorCode = -1;
    int ret  = 0;
    uint64_t request_id = 0;
    while(errorCode !=0)
    {
        p_rtm_client  = createAgoraRtmClient(m_cfg, errorCode);
        if (!p_rtm_client || errorCode != 0) {
            cout  << "client initialized error ret : " << ret << endl;
        }
    }
    if(p_rtm_client)
    {
        p_rtm_client->login(RTC_USE_STRING_UID_ENABLE?DEFAULT_TOKEN:NULL,request_id);
    }

    return 0;
}

登录回调函数处理

注意: 如果登录成功才开始订阅主题 如果登录不成功会延时 1 秒再登录

void onLoginResult(const uint64_t requestId,
                           RTM_ERROR_CODE errorCode) override {
            LOG(INFO) << " login result error code : " << errorCode << endl;
            if(errorCode == RTM_ERROR_OK)
            {
                uint64_t request = 0;
                if(p_rtm_client)
                    p_rtm_client->subscribe(Device::getInstance().getDeviceGeneralInformation().getDeviceUuid(), SubscribeOptions(), request);
            }
            else
            {
                uint64_t request_id = 0;
                if(p_rtm_client)
                {
                    p_rtm_client->login(RTC_USE_STRING_UID_ENABLE?DEFAULT_TOKEN:NULL, request_id);
                }
                sleep(1);
            }
        }

消息处理

int  handleRobotMessage(const char  *message,size_t message_size) {
       LOG(INFO) << "robot control message : " << message << "length: " << message_size;
       cJSON *messageJson = NULL;
       cJSON *methodJson = NULL;
       cJSON *responseJson = cJSON_CreateObject();;

       messageJson = cJSON_Parse((char *) message);
       methodJson = cJSON_GetObjectItem(messageJson, "method");
       //假如methodJson为 NULL ,客户端发送了错误的method
       if (!methodJson) {
           cJSON *paramsJson = cJSON_CreateObject();;
           cJSON_AddStringToObject(responseJson, "method", "unsupportedMethod");
           cJSON_AddNumberToObject(responseJson, "status", MQTT_UNSUPPORTED_METHOND_STATUS);
           cJSON_AddItemToObject(responseJson, "params", paramsJson);
           cJSON_AddStringToObject(paramsJson, "unsupportedMethodParams", (char *) message);
           char *json_data = cJSON_Print(responseJson);
           // if ((rv = nng_send(g_rep_sock, json_data, strlen(json_data) + 1, 0)) != 0) {
           //     LOG(INFO) << "nng_send" << nng_strerror(rv);
           // }
           cJSON_Delete(responseJson);
           cJSON_Delete(messageJson);
           cJSON_free(json_data);
           json_data = NULL;
           return -1;

       } else {
           static MqttCommandHandle mqttCommandHandle;

           cJSON *paramsJson = cJSON_GetObjectItem(messageJson, "params");
           mqttCommandHandle.MqttCommandHandleFunction(methodJson->valuestring, paramsJson, responseJson);
           char *json_data = cJSON_Print(responseJson);
           //            LOG(INFO) << "after MqttCommandHandleFunction json_data  : " << json_data;
           //            LOG(INFO) << "after MqttCommandHandleFunction strlen(json_data)  : " << strlen(json_data);
           // if ((rv = nng_send(g_rep_sock, json_data, strlen(json_data) + 1, 0)) != 0) {
           //     LOG(INFO) << "nng_send" << nng_strerror(rv);
           // }
           cJSON_Delete(responseJson);
           cJSON_Delete(messageJson);
           cJSON_free(json_data);
           json_data = NULL;
           return 0;

       }
   }

通过onTokenPrivilegeWillExpire函数完成刷新 token

  void onTokenPrivilegeWillExpire(const char* channelName) override{
            cout << " rtm token privilege will expire: "  <<channelName << endl;
            if (DeviceConfig::device_config()->refresh_agora_token() == true) {
                cout << " rtm refresh token success  " << endl;
                uint64_t request_id = 0;
                p_rtm_client->renewToken((const char *)DeviceConfig::device_config()->agora_config()->token().c_str(),request_id);
            } else {
                cout << " rtm refresh token error  " << endl;
            }
        }

参数设置截图

在这里插入图片描述


友情提示🔔

🙏 感谢你的阅读! 如果这篇文章对你有所启发,欢迎关注我 ⭐,欢迎点击 “打赏支持作者” 支持一下我,你的支持是我持续创作的最大动力! 我会持续分享更多关于 智能摄像头 📷、机器人项目、 🤖音视频 RTC 🎧、App 开发 📱、嵌入式开发 🔧 等方向的实战经验,让你更快落地、更少踩坑。 欢迎浏览我其他文章 📚,或许能解决你当前的难题。 如果你正好在做相关项目产品,也欢迎随时私信我,一起技术交流、一起搞事情! 🤝💬📞 联系微信/电话:13826173658