设备端SDK(Linux)文档
更新时间:2019-08-30 10:26:32
概述
LinkVisual设备端SDK提供了网络摄像机(IPC)接入阿里云视频边缘智能服务的基础API,主要包括实时视频直播相关的接口、设备本地文件点播相关的接口、报警事件上报和图片上传的接口以及其他IPC控制相关的接口等,为物联网设备提供的视频直播、视频点播、图片上传的功能。
资源占用情况如下:
- 内存占用:1MB的码流的情况下,预估占用500K左右的内存
- ROM占用:2MB
平台支持:目前支持Linux平台,且需要C++11的支持(支持C++11的编译工具链版本在4.8.1之上)。
依赖:LinkVisual SDK依赖了飞燕SDK。飞燕SDK提供了物联网通道能力,LinkVisual响应飞燕SDK的控制消息来进行流媒体业务处理。开发者需要同时获取LinkVisual SDK和飞燕SDK来完成设备的接入。
- 飞燕 SDK主要是提供物联网控制通道能力,包括长连接、消息通知、事件上报等。
- LinkVisual SDK主要是提供音视频流通道能力;
SDK Demo
准备开发环境
我们提供Ubuntu16.04系统上编译的x86 64位的LinkVisual SDK和Demo供快速体验, 在其它Linux版本上尚未验证过, 推荐安装与阿里一致的发行版以避免碰到兼容性方面的问题。使用下面命令安装一些必备的软件:
$ sudo apt-get install -y build-essential make git gcc g++ cmake
下载
- 提供Ubuntu16.04下编译的设备端SDK和Demo供参考使用。单击下载http://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/108188/cn_zh/1551844317227/link_visual_ipc_v1.0.0.tar.gz。下载本LinkVisual SDK Demo将默认您同意本软件许可协议。
编译运行
- 内容确认
请将下载的后文件解压得到link_visual_ipc_v1.0.0文件夹,确认文件夹下的文件目录结构如下:
---- CMakeLists.txt
|
---- samples
|
---- sdk
切换到link_visual_ipc_v1.0.0文件夹下,执行如下命令。
$ mkdir -p build;建立一个build文件夹,用于归类编译产物
$ cd build
$ cmake ..;使用上层目录的CmakeLists.txt进行cmake
$ make;编译
$ make install;安装运行相关文件
- 创建产品和设备
请根据物联网平台接入指引来创建产品,并获取设备的三元组。
- 运行
在link_visual_ipc_v1.0.0文件夹下的build目录下,生成了对应的可执行程序。进入可执行程序目录下,运行如下的命令,其中product_name
、device_name
、device_secret
需要替换为自己的设备三元组。
./link_visual_demo -p product_name -n device_name -s device_secret
SDK获取
LinkVisual SDK获取
LinkVisual设备端SDK以静态库的形式提供,我们可以编译SDK支持不同芯片平台的接入,请发送邮件到ray.wl@antfin.com进行SDK申请,并确保在邮件内容中,请包含如下信息:
1.公司名称;
2.联系人及联系方式;
3.公司地址;
4.应用场景描述。
我们的商务人员在收到邮件后,会主动与您联系获取芯片平台的交叉编译工具链。提供交叉编译工具链时,需要详细说明编译链的使用说明。根据使用说明,我们将编译好对应平台的SDK提供给您。
前置依赖项
请确认已经参照“LinkVisual对接技术说明”完成了飞燕平台的视频【产品创建】和【服务开通】环节,并且在该产品添加了所需的【标准功能】;
API列表
SDK生命周期
说明 | 接口 |
---|---|
SDK初始化 | lv_init |
SDK停止 | lv_destroy |
Link Kit消息注入
说明 | 接口 |
---|---|
Linkkit的MQTT消息注入 | lv_linkkit_adapter |
直播及点播服务
说明 | 接口 |
---|---|
通知直播、点播服务已开启 | lv_start_push_streaming_cb |
通知直播、点播服务已结束 | lv_stop_push_streaming_cb |
推送视音频配置参数 | lv_stream_send_config |
推送视频数据 | lv_stream_send_video |
推送音频数据 | lv_stream_send_audio |
推流过程中命令控制(暂停等) | lv_on_push_streaming_cmd_cb |
点播的文件列表查询 | lv_query_storage_record_cb |
强制关闭一路链接 | lv_stream_force_stop |
录像文件播放结束 | lv_post_file_close |
图片服务
说明 | 接口 |
---|---|
图片上传 | lv_post_alarm_image |
通知上传图片 | lv_trigger_pic_capture_cb |
语音对讲服务
说明 | 接口 |
---|---|
通知开启服务 | lv_start_voice_intercom_cb |
通知结束服务 | lv_stop_voice_intercom_cb |
开启服务 | lv_voice_intercom_start_service |
停止服务 | lv_voice_intercom_stop_service |
发送音频 | lv_voice_intercom_send_audio |
接收音频 | lv_voice_intercom_receive_data_cb |
接收音频参数配置 | lv_voice_intercom_receive_metadata_cb |
对接指南
重要说明
请开发者根据编译说明中的步骤,完成demo的编译和运行后,在进行实际API的对接。
功能说明
获取到LinkVisual SDK后,开始进入对接阶段。以下列出了当前SDK能够支持的所有功能,开发者根据自身产品定义自行检查。
[ ] 配网功能(一键配网、零配等)
[ ] OTA功能
[ ] Linkkit的MQTT消息收发
[ ] Linkkit自定义物模型
[ ] 直播功能
[ ] 卡录像点播功能
[ ] PTZ功能
[ ] 图片上报功能(抓图功能和移动侦测报警功能)
[ ] 语音对讲功能
SDK内容
将获取到的SDK解压后,可以看到以下内容:
1. version.txt:版本说明
2. CMakeLists.txt:基于cmake的编译的基础示例
3. LinkVisual:内含sdk和samples,分别包含库和头文件、示例代码
4. third_party:LinkVisual依赖的一些库信息
LinkVisual依赖库
- cJSON,为LinkVisual提供JSON解析的支持,使用版本为1.7.7
- linkkit-sdk-c,为阿里云提供的物联网接入sdk,LinkVisual使用版本v2.3.0。更多信息请访问官方网址及官方文档。
编译说明
环境配置
编译依赖cmake等一些相关软件,这里以ubuntu16.04下安装为例:
$ sudo apt-get install -y build-essential make git gcc g++ cmake tree
cJSON编译
在third_party目录里解压cJSON-1.7.7,并进入解压后的文件夹
$ cd third_party
$ tar -vxf cJSON-1.7.7.tar.gz;解压代码压缩包
$ cd cJSON-1.7.7
交叉编译准备:修改Makefile,在文件最开始加入工具链的声明(请替换成对应的交叉编译工具链)
CC = arm-linux-gcc
LD = arm-linux-ld
AR = arm-linux-ar
编译,确认是否生成了libcjson.a以及头文件cJSON.h是否存在
$ make
$ ls lib*.a;确认libcjson.a已生成
$ ls *.h
linkkit-sdk-c编译
在third_party目录里解压linkkit-sdk-c,并进入解压后的文件夹
$ cd third_party
$ tar -vxf linkkit-sdk-c.tar.gz;解压代码压缩包
$ cd linkkit-sdk-c
交叉编译准备:修改src/board/config.ubuntu.x86,在最后加上CROSS_PREFIX :=交叉编译工具链路径前缀(请替换成对应的交叉编译工具链)
CROSS_PREFIX :=arm-linux-
选择ubuntu编译和确认生成库libiot_tls.a/libiot_sdk.a/libiot_hal.a
$ make reconfig;这里选择ubuntu对应的数字
$ make;
$ ls output/lib/*.a;这里只需要有libiot_tls.a/libiot_sdk.a/libiot_hal.a
$ tree include;这里可以看到很多头文件,如iot_import.h等,全部都需要
整体编译
重新回到SDK解压后的文件夹根目录,准备开始整体编译。
修改CMakeLists.txt里的TOOLCHAINS_PREFIX设置,第二个参数设置为交叉编译工具链前缀(请替换成对应的交叉编译工具链)
set(TOOLCHAINS_PREFIX "arm-linux-" CACHE STRING "set the toolchain")
开始编译:
$ mkdir -p build;建立一个build文件夹,用于归类编译产物
$ cd build
$ cmake ..;使用上层目录的CmakeLists.txt进行cmake
$ make;编译
$ make install;安装运行相关文件
自行编译时,请参考以下链接顺序:link_visual_ipc iot_sdk cjson iot_hal iot_tls pthread rt。编译的CFLAGS,请添加-std=c++11。
运行设备demo
将build文件夹拷贝至板子上,进入build后执行以下命令(其中三元组需替换):
$ ./link_visual_demo -p productKey -n productName -s productSecret
飞燕SDK对接
SDK功能繁多,详细的SDK内容请参考文档:点击这里。
当前LinkVisual主要依赖于Linkkit的MQTT功能,针对此功能,LinkVisual提供了该功能的使用要求。这些使用要求在sample/linkkit_adapter.cpp里有使用范例。其他如配网、OTA等功能,需要开发者根据文档开发。
配网功能
Linkkit提供配网功能和设备绑定功能,配网方式包括一键配网等,开发者根据自身需求选择对接。配网功能查看:
点击这里。
OTA功能
Linkkit提供OTA功能。OTA功能对于设备后续版本的迭代更新有着至关重要的功能,要求开发者必须完成OTA功能更的对接。当前LinkVisual要求使用高级版接口的OTA功能。OTA功能:点击这里。
Linkkit高级版接口
LinkVisual使用了Linkkit高级版接口,此处列出LinkVisual使用到的一些接口。
Linkkit生命周期
IOT_Linkkit_Open;打开一个linkkit句柄
IOT_Linkkit_Connect;连接服务器
IOT_Linkkit_Yield;用于维持心跳,建议其参数值为200ms,不建议自行修改,会导致MQTT消息往外抛出变慢。
IOT_Linkkit_Close;关闭linkkit连接和句柄
Linkkit消息订阅
IOT_RegisterCallback;用于订阅linkkit消息
LinkVisual要求必须要订阅的有:
ITE_SERVICE_REQUEST:物模型的服务类消息,消息回调中部分消息需要使用lv_linkkit_adapter交由LinkVisual处理。
ITE_LINK_VISUAL:LinkVisual自定义消息,消息回调中所有消息需要使用lv_linkkit_adapter交由LinkVisual处理。
具体参考LinkVisual SDK中的linkkit_adapter.cpp
Linkkit其他配置
IOT_SetLogLevel;用于设置Linkkit的打印级别,初始对接阶段建议设置为DEBUG。
CONFIG_MQTT_TX_MAXLEN;定义在iot_import_config.h,影响MQTT发送的最大值。
LinkVisual定义的卡录像列表查询功能中,由于需要查询的数量不同,导致最终查询的字符串长度可能超过默认MQTT的长度设置,
需要根据实际长度定义最大值。
Linkkit功能验收
对接LinkVisual功能前,要求Linkkit以下功能已经是可用状态:
- IOT_Linkkit_Connect连接成功
- IOT_RegisterCallback注册了ITE_SERVICE_REQUEST/ITE_LINK_VISUAL回调
以下Linkkit功能可在对接LinkVisual过程中完成:
- 配网功能
- OTA功能
LinkVisual SDK对接
SDK中的LinkVisual/sdk/link_visual_ipc.h和LinkVisual/sdk/link_visual_def.h是SDK的头文件。
SDK中的LinkVisual/samples/demo.cpp主要说明了LinkVisual SDK的对接示例,文档中的示例代码主要来自于demo.cpp和linkkit_adapter.cpp。
问题排查
LinkVisual以非源码形式提供功能,在对接过程中出现问题时,LinkVisual相当于黑盒子,不利于问题的排查。因此我们对问题排查提出了一定的要求,以提高问题排查的效率。
- 日志:所有问题都需要提供日志。日志中要求包含lv_init函数打印的SDK版本信息,要求lv_init中的log_level为DEBUG级别,要求问题发生的前后日志尽可能的详细。
- core:发生崩溃性问题时,除日志外,要求提供core文件的堆栈信息。
- tcpdump:部分问题可能要求提供tcpdump进行网络数据抓包。
SDK生命周期
lv_init
接口详情:int lv_init(const lv_config_s *config)
SDK初始化
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
config | lv_config_s* | 配置参数结构体 |
- 示例代码
//新建一个配置结构体,并置空
lv_config_s config;
memset(&config, 0, sizeof(lv_config_s));
//以下是设置具体的配置属性
//设备三元组
memcpy(config.product_key, product_key.c_str(), product_key.length());
memcpy(config.device_name, device_name.c_str(), device_name.length());
memcpy(config.device_secret, device_secret.c_str(), device_secret.length());
//SDK的日志配置
config.log_level = LV_LOG_DEBUG;
config.log_dest = LV_LOG_DESTINATION_STDOUT;
//config.log_dest_file;
//音视频推流服务
config.start_push_streaming_cb = startPushStreamingCallback;
config.stop_push_streaming_cb = stopPushStreamingCallback;
config.on_push_streaming_cmd_cb = onPushStreamingCmdCb;
//语音对讲服务
config.start_voice_intercom_cb = startVoiceIntercomCallback;
config.stop_voice_intercom_cb = stopVoiceIntercomCallback;
config.voice_intercom_receive_metadata_cb = voice_intercom_receive_metadata_cb;
config.voice_intercom_receive_data_cb = VoiceIntercomReceiveDataCallback;
//获取存储录像录像列表
config.query_storage_record_cb = queryStorageRecordCallback;
//触发设备抓图
config.trigger_pic_capture_cb = triggerPicCaptureCallback;
//触发设备录像
config.trigger_record_cb = triggerRecordCallback;
//初始化SDK,失败则退出
int ret = lv_init(&config);
if (ret < 0) {
return -1;
}
lv_destroy
接口详情:int lvdestroy(void);
SDK销毁
- 参数说明
无。
- 示例代码
//运行结束后
lv_destroy();
其他说明
- lv_init会打印相关版本号信息,在寻找LinkVisual团队支持时,请开发者务必注明此版本信息,方便追溯问题。
- lv_init设备初始化不会因无网络而失败,失败的原因一般是入参不正确或者资源分配失败(一般不会)。
- lv_init注册了大量的回调函数,这些回调函数共用一个消息队列线程,意味着一个回调消息的阻塞将导致后续消息的延迟,一个回调的永久阻塞将导致所有回调无法抛出。因此建议开发者:不要在回调中做过于耗时的操作;认真检查回调函数中的代码是否会长时间或者永久阻塞。
- lv_init定义了日志类型log_level,建议对接初期将日志类型设置为LV_LOG_DEBUG。
- 推荐lv_init在IOT_Linkkit_Connect成功后调用。
- 推荐lv_destroy在IOT_Linkkit_Close调用之前调用。
Linkkit消息注入
LinkVisual需要接受linkkit下发的部分消息并代为处理,处理用接口为lv_linkkit_adapter,已被LinkVisual处理过的消息不再需要开发者处理。
lv_linkkit_adapter
接口详情:int lvlinkkit_adapter(lv_linkkit_adapter_type_s type, const lv_linkkit_adapter_property_s *in)
_SDK初始化。
_
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
type | lv_linkkit_adapter_type_s | linkkit的消息适配类型,目前有linkkit物模型服务类消息和LinkVisual自定义消息。 |
in | const lv_linkkit_adapter_property_s* | linkkit的消息结构体 |
- 示例代码
IOT_RegisterCallback(ITE_LINK_VISUAL, user_link_visual_handler);//订阅了LinkVisual自定义消息回调
IOT_RegisterCallback(ITE_SERVICE_REQUST, user_service_request_event_handler);//订阅了Linkkit物模型服务类消息回调
ITE_LINK_VISUAL
ITE_LINK_VISUAL是LinkVisual自定义的消息。由于Linkkit v2.3.0接口暂不支持自定义消息,因此LinkVisual修改了Linkkit部分代码,非LinkVisual专用的Linkkit版本将无法使用该功能。
自定义消息全部由LinkVisual处理业务逻辑:
//user_link_visual_handler为IOT_RegisterCallback使用ITE_LINK_VISUAL订阅的回调,
//在回调中,所有消息都使用lv_linkkit_adapter注入
static int user_link_visual_handler(const int devid, const char *payload,
const int payload_len)
{
//Linkvisual自定义的消息,直接全交由LinkVisual来处理
if (payload == nullptr || payload_len == 0) {
return 0;
}
lv_linkkit_adapter_property_s in = {0};
in.request = payload;
in.request_len = payload_len;
int ret = lv_linkkit_adapter(LV_LINKKIT_ADAPTER_TYPE_LINK_VISUAL, &in);
if (ret < 0) {
EXAMPLE_TRACE("LinkVisual process service request failed, ret = %d", ret);
return -1;
}
return 0;
}
ITE_SERVICE_REQUST
ITE_SERVICE_REQUST是Linkkit定义的物模型服务类消息,部分物模型消息需要由LinkVisual处理业务逻辑,此类消息需要使用lv_linkkit_adapter来处理。
//LinkVisual代为处理的服务类消息
static std::vector<std::string> link_visual_service = {
"TriggerPicCapture",//触发设备抓图
"StartVoiceIntercom",//开始语音对讲
"StopVoiceIntercom",//停止语音对讲
"StartVod",//开始录像观看
"StartVodByTime",//开始录像按时间观看
"StopVod",//停止录像观看
"QueryRecordList",//查询录像列表
"StartP2PStreaming",//开始P2P直播
"StartPushStreaming",//开始直播
"StopPushStreaming",//停止直播
};
//user_service_request_event_handler为IOT_RegisterCallback使用ITE_SERVICE_REQUST订阅的回调,
//在回调中,判断当前消息是否需要LinkVisual处理,如需要,则使用lv_linkkit_adapter注入
static int user_service_request_event_handler(const int devid, const char *id, const int id_len,
const char *serviceid, const int serviceid_len,
const char *request, const int request_len,
char **response, int *response_len)
{
/* 打印方式1 */
printf("Service Request Received, Devid: %d, ID %.*s, Service ID: %.*s, Payload: %s \n",
devid, id_len, id, serviceid_len, serviceid, request);
/* 打印方式2 */
printf("Service Request Received, Devid: %d, ID %s, Service ID: %s, Payload: %s \n",
devid, id, serviceid, request);
bool link_visual_process = false;
//判断当前serviceid是否是LinkVisual需要处理的消息
for (int i = 0; i < link_visual_service.size(); i++) {
//这里需要根据字符串的长度来判断
if (!strncmp(serviceid, link_visual_service[i].c_str(), link_visual_service[i].length())) {
link_visual_process = true;
break;
}
}
//根据前文的判断,将需要LinkVisual处理的消息使用lv_linkkit_adapter注入
if (link_visual_process) {
//ISV将某些服务类消息交由LinkVisual来处理,不需要处理response
lv_linkkit_adapter_property_s in = {0};
in.dev_id = devid;
in.id = id;
in.id_len = id_len;
in.service_id = serviceid;
in.service_id_len = serviceid_len;
in.request = request;
in.request_len = request_len;
int ret = lv_linkkit_adapter(LV_LINKKIT_ADAPTER_TYPE_TSL_SERVICE, &in);
if (ret < 0) {
EXAMPLE_TRACE("LinkVisual process service request failed, ret = %d", ret);
return -1;
}
} else {
//这里给一个非LinkVisual处理的消息示例
if (!strncmp(serviceid, "PTZActionControl", (serviceid_len > 0)?serviceid_len:0)) {
cJSON *root = cJSON_Parse(request);
if (root == NULL) {
EXAMPLE_TRACE("JSON Parse Error");
return -1;
}
int action_type = 0;
cJSON *child = cJSON_GetObjectItem(root, "ActionType");
if (!child) {
EXAMPLE_TRACE("JSON Parse Error");
cJSON_Delete(root);
return -1;
}
action_type = child->valueint;
int step = 0;
child = cJSON_GetObjectItem(root, "Step");
if (!child) {
EXAMPLE_TRACE("JSON Parse Error");
cJSON_Delete(root);
return -1;
}
step = child->valueint;
cJSON_Delete(root);
EXAMPLE_TRACE("PTZActionControl %d %d", action_type, step);
}
}
return 0;
}
PTZ功能
LinkVisual定义了PTZ属性,但是这些PTZ消息,需要开发者自行处理。
ITE_SERVICE_REQUST的示例代码中已经给出了PTZ的解析示例代码,此处补充PTZ的定义。
PTZActionControl | 物模型服务类标识符 | |
---|---|---|
ActionType | 动作类型 | 0 - 左;1 - 右;2 - 上;3 - 下;4 - 上左;5 - 上右;6 - 下左;7 - 下右;8 - 放大;9 - 缩小; |
Step | 步进量 | 取值范围:0 ~ 10;步长:1 |
其他说明
任何一个自定义消息或者物模型消息缺失,都将影响SDK的功能。若开发者不使用demo中的示例代码,请一定要仔细检查自写代码中消息是否成功注入了lv_linkkit_adapter。
通过云产品的后台控制台的设备调试功能,云端可下发测试数据至设备;开发者可通过此方式进行消息注册的测试。
Linkkit的IOT_RegisterCallback注册的回调,共用一个消息队列线程,意味着一个回调消息的阻塞将导致后续消息的延迟,一个回调的永久阻塞将导致所有回调无法抛出。因此建议开发者:不要在回调中做过于耗时的操作;认真检查回调函数中的代码是否会长时间或者永久阻塞。
Linkkit的IOT_RegisterCallback注册的ITE_SERVICE_REQUST回调中,id、serviceid、request其实指向同一个字符串的不同位置(对比示例代码中打印方式1和打印方式2),因此对serviceid的比较时,需要带上 serviceid_len。
PTZ功能中,开发者不支持的动作类型可不做支持;单位步进量在设备的具体步进大小可由开发者自行定义;实现了画面翻转功能的开发者,PTZ方向的调整也需要翻转。
直播和卡录像点播
lv_start_push_streaming_cb
接口详情:typedef int (lv_start_push_streaming_cb)(int service_id, lv_stream_type_e type, const lv_stream_param_s param)
回调函数,通知直播或者卡录像点播链路已经建立成功,并附带一些配置信息。在此回调收到后,开发者应该根据配置信息初始化编码器,并开始推送音视频数据。
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
type | lv_stream_type_e | 链路的类型,分为直播和卡录像点播 |
param | const lv_stream_param_s* | 链路的参数,如卡录像要点播的文件名称 |
- 示例代码
//demo,定义了回调函数startPushStreamingCallback作为lv_start_push_streaming_cb的实现
lv_start_push_streaming_cb = startPushStreamingCallback;
//demo,回调函数startPushStreamingCallback
int startPushStreamingCallback(int service_id, lv_stream_type_e cmd_type, const lv_stream_param_s *param)
{
//忽略不合法的service_id
if (service_id < 0) {
return -1;
}
if (cmd_type == LV_STREAM_CMD_LIVE) {
//使用lv_stream_send_video/lv_stream_send_audio推送音视频数据;
//实际使用中建议新建线程进行数据发送
......
return 0;
} else if (cmd_type == LV_STREAM_CMD_STORAGE_RECORD_BY_FILE) {
//使用lv_stream_send_video/lv_stream_send_audio推送音视频数据
//实际使用中建议新建线程进行数据发送
......
return 0;
}
return 0;
}
lv_stop_push_streaming_cb
接口详情:typedef int (*lv_stop_push_streaming_cb)(int service_id)
回调函数,通知直播或者卡录像点播链路已经断开。在此回调收到后,开发者应该停止推送音视频数据。
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
- 示例代码
//demo,定义了回调函数stopPushStreamingCallback作为lv_start_push_streaming_cb的实现
lv_stop_push_streaming_cb = stopPushStreamingCallback;
//demo,回调函数stopPushStreamingCallback
int stopPushStreamingCallback(int service_id)
{
//忽略不合法的service_id
if (service_id < 0) {
return -1;
}
if (service_id == g_live_service_id) {
//开发者停止推流
} else if (service_id == g_storage_record_service_id) {
//开发者停止推流
}
return 0;
}
lv_on_push_streaming_cmd_cb
接口详情:typedef int (*lv_on_push_streaming_cmd_cb)(int service_id, lv_on_push_streaming_cmd_type_e cmd, unsigned int timestamp_ms)
回调函数,直播、卡录像点播链接建立期间,进行一些命令控制,例如要求发送I帧命令
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
cmd | lv_on_push_streaming_cmd_type_e | 控制命令,目前有开始播放、停止播放、暂停播放、恢复播放、定位、强制I帧 |
timestamp_ms | unsigned int | 卡录像点播的定位的时间戳 |
- 示例代码
//demo,定义了回调函数stopPushStreamingCallback作为lv_start_push_streaming_cb的实现
on_push_streaming_cmd_cb = onPushStreamingCmdCb;
int onPushStreamingCmdCb(int service_id, lv_on_push_streaming_cmd_type_e cmd, unsigned int timestamp_ms)
{
if (cmd == LV_STORAGE_RECORD_START) {
//卡录像开始推流
} else if (cmd == LV_STORAGE_RECORD_STOP) {
//卡录像停止推流
} else if (cmd == LV_STORAGE_RECORD_PAUSE) {
//卡录像暂停推流
} else if (cmd == LV_STORAGE_RECORD_UNPAUSE) {
//卡录像恢复推流
} else if (cmd == LV_STORAGE_RECORD_SEEK) {
//卡录像定位到某一段
} else if (cmd == LV_LIVE_REQUEST_I_FRAME) {
//对于直播,需要强制生成一个I帧
}
return 0;
}
lv_stream_send_config
接口详情:int lv_stream_send_config(int service_id, unsigned int bitrate_kbps, double duration, const lv_video_param_s video_param, const lv_audio_param_s audio_param)
发送音视频的配置
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
bitrate_kbps | unsigned int | 链路的码流,设置的码流越大,内部音视频数据缓冲区越大 |
duration | double | 文件时长,卡录像点播有效 |
video_param | const lv_video_param_s* | 视频的参数集 |
audio_param | const lv_audio_param_s* | 音频的参数集 |
- 示例代码
lv_video_param_s video_param;
memset(&video_param, 0, sizeof(lv_video_param_s));
video_param.format = LV_VIDEO_FORMAT_H264;
video_param.fps = 25;
lv_audio_param_s audio_param;
memset(&audio_param, 0, sizeof(lv_audio_param_s));
audio_param.format = LV_AUDIO_FORMAT_G711A;
audio_param.channel = LV_AUDIO_CHANNEL_MONO;
audio_param.sample_bits = LV_AUDIO_SAMPLE_BITS_16BIT;
audio_param.sample_rate = LV_AUDIO_SAMPLE_RATE_8000;
lv_stream_send_config(service_id, 1000, 0, &video_param, &audio_param);
lv_stream_send_video
接口详情:int lv_stream_send_video(int service_id, unsigned char* buffer, unsigned int buffer_len, bool key_frame, unsigned int timestamp_ms)
视频数据发送。
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
buffer | unsigned char* | 视频的数据 |
buffer_len | unsigned int | 视频的数据长度 |
key_frame | bool | 是否为关键帧。I帧为关键帧,P帧为非关键帧,不接受B帧 |
timestamp_ms | unsigned int | 视频的时间戳 |
- 示例代码
//demo,这个函数回调输入音频帧数据
void handleVideoBuffer(unsigned char *buffer, unsigned int buffer_size,
unsigned int present_time, int nal_type, int service_id)
{
if (nal_type == 5) {
lv_stream_send_video(service_id, buffer, buffer_size, 1, present_time);
} else if (nal_type == 1) {
lv_stream_send_video(service_id, buffer, buffer_size, 0, present_time);
}
}
lv_stream_send_audio
接口详情:int lv_stream_send_audio(int service_id, unsigned char* buffer, unsigned int buffer_len, unsigned int timestamp_ms)
音频数据发送。
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
buffer | unsigned char* | 音频的数据 |
buffer_len | unsigned int | 音频的数据长度 |
timestamp_ms | unsigned int | 音频的时间戳 |
- 示例代码
//demo,这个函数回调输入音频帧数据
void handleAudioBuffer(unsigned char *buffer, unsigned int buffer_size,
unsigned int present_time, int service_id)
{
lv_stream_send_audio(service_id, buffer, buffer_size, present_time);
}
lv_query_storage_record_cb
接口详情:typedef void (lv_query_storage_record_cb)(unsigned int start_time, unsigned int stop_time, int num, const char id, int (on_complete)(int num, const char id, const lv_query_storage_record_response_s *response))
回调函数,查询卡录像的文件信息
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
start_time | unsigned int | 查询的录像的开始时间 |
stop_time | unsigned int | 查询的录像的结束时间 |
num | int | 查询的录像的数量 小于等于0的时候表示查全部的录像 |
id | const char * | 查询的会话ID,需要回传 |
on_complete | 函数指针 | 将查询结果通过该函数同步返回 |
- 示例代码
//demo,该函数是lv_query_storage_record_cb的实际实现
void queryStorageRecordCallback(unsigned int start_time,
unsigned int stop_time,
int num,
const char *id,
int (*on_complete)(int num, const char *id, const lv_query_storage_record_response_s *response))
{
DEMO_TRACE("start_time:%d stop_time:%d num:%d", start_time, stop_time, num);
if (num <= 0) {
return ;
}
int answer_num = (num > 1)?(num - 1):1;
static int index = 0;
auto *response = new lv_query_storage_record_response_s[num];
memset(response, 0, sizeof(lv_query_storage_record_response_s) * answer_num);
for (int i = 0; i < answer_num; i ++) {
response[i].file_size = 15320990;
response[i].record_type = (lv_storage_record_type_e)(i % 3);
response[i].start_time = (int)start_time + index * 60;
response[i].stop_time = (int)start_time + index * 60 + 59;
response[i].file_size = 15320990;
snprintf(response[i].file_name, 64, "%d.mp4", index);//注意不要溢出
index ++;
}
int result = on_complete(answer_num, id, response);
DEMO_TRACE("result:%d", result);
delete [] response;
}
lv_stream_force_stop
接口详情:int lv_stream_force_stop(int service_id)
强制断开一路直播、卡录像点播。开发者在自身的功能出错等无法继续正常往一路流发送数据时,自行断开连接。
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
- 示例代码
//demo,在收到推流通知的回调中,直接强制关闭该路链接
int startPushStreamingCallback(int service_id, lv_stream_type_e cmd_type, const lv_stream_param_s *param)
{
DEMO_TRACE("startPushStreamingCallback:%d %d", service_id, cmd_type);
if (service_id < 0) {
return -1;
}
lv_stream_force_stop(service_id);
}
其他说明
- 强制I帧命令发出时,为了保证尽可能快速的发出I帧,SDK内部将清空缓冲区,并不再接受音频数据和视频的P帧数据,直到I帧到达。
- 强制I帧命令发出时,为了保证尽可能快速的发出I帧,开发者应尽可能快的产生I帧。若300ms内未收到I帧,SDK会重发若干次强制I帧请求。
- 强制I帧命名发出时,开发者需要重新调用lv_stream_send_config发送流配置
- 卡录像的定位操作发生时,为了保证尽可能快速的显示定位后的数据,开发者在定位操作后,应该尽可能快速的发出I帧;同时SDK在定位操作后也不在接收音频数据和视频的P帧数据,直到I帧到达。
- H264/H265的帧结构会有一定的要求,可以以打印I帧的前256个字节进行查看,打印代码为
for (int i = 0; i < ((buffer_size > 256)?256:buffer_size); i++) {
printf("%02x ", buffer[i]);
if ((i + 1) % 30 == 0) {
printf("\n");
}
}
printf("\n");
H264要求I帧为: 帧分隔符+SPS+帧分隔符+PPS+帧分隔符+IDR,如图
0x000001或者0x00000001是帧分隔符,0x67是SPS的开始,0x68是PPS的开始,0x65是IDR的开始
H265要求I帧为: 帧分隔符+VPS+帧分隔符+SPS+帧分隔符+PPS+帧分隔符+IDR,如图:
0x000001或者0x00000001是帧分隔符,0x40是VPS的开始,0x42是SPS的开始,0x44是PPS的开始,0x26是IDR的开始
- 音频目前支持G711A/G711U/LC-AAC,编码参数支持需要参考头文件link_visual_def.h中的宏定义值
- 同一路流切换视频码流时(如主码流切为子码流、修改码流分辨率等),请保证切换后的第一帧为I帧,否则会引发花屏等现象
图片服务
lv_trigger_pic_capture_cb
接口详情:typedef int (lv_trigger_pic_capture_cb)(const char id)
通知设备开始抓取图片
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
id | const char * | 本次请求的ID,需要回传 |
lv_post_alarm_image
接口详情:int lv_post_alarm_image(const char buffer, int buffer_len, lv_event_type_e type, const char id)
发送图片和报警事件,适用于抓图回调后进行上报,或者设备主动发起上报
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
buffer | const char* | 图片数据 |
buffer_len | int | 图片数据长度 |
type | lv_event_type_e | 上报类型,分为抓图回调后进行上报和设备主动发起上报 |
id | const char * | 抓图回调的ID回传,主动上报时传空字符串或者NULL。 |
- 示例代码
//demo,读取一个图片文件并上传
void testPicUpload(lv_event_type_e type, const char *id)
{
char *buf = new char[1024*1024];
FILE *fp = nullptr;
int ret = 0;
if((fp = fopen("1.jpg", "r")) == nullptr) {
DEMO_TRACE("Failed to open file.");
return;
}
ret = fseek(fp, 0, SEEK_SET);
if(ret != 0) {
return ;
}
ret = fread(buf, 1, 1024*1024, fp);
fclose(fp);
lv_post_alarm_image(buf, ret, type, id);
delete [] buf;
}
其他说明
- 图片需要有云储存权限才可上传
- 上传的最大图片大小为1M
- 图片上传的最小间隔为1S,频繁触发的图片将会被丢弃
- 图片会由SDK内部保存一份拷贝,并形成待发送的图片队列。图片队列的长度为5张,在网络差的情况下,图片队列可能会满,满队列后新生成的图片将会被丢弃
- SDK不限制图片的格式,只要云端或者从云端拉取图片的设备能够支持解析即可。推荐使用常见的图片格式,如JPEG。
语音对讲服务
lv_start_voice_intercom_cb
接口详情:typedef int (*lv_start_voice_intercom_cb)(int service_id)
回调函数,通知一路语音对讲服务已可以建立
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
lv_voice_intercom_start_service
接口详情:int lv_voice_intercom_start_service(int service_id, const lv_audio_param_s *audio_param)
建立一路语音对讲链路,在接收到语音对讲开始回调后调用
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
audio_param | const lv_audio_param_s * | 设备的音频参数结构体 |
- 示例代码
//demo,startVoiceIntercomCallback是lv_start_voice_intercom_cb的实际实现
lv_start_voice_intercom_cb = startVoiceIntercomCallback;
int startVoiceIntercomCallback(int service_id)
{
//收到开始语音对讲请求时,主动开启对讲服务
lv_audio_param_s audio_param;
memset(&audio_param, 0, sizeof(lv_audio_param_s));
audio_param.format = LV_AUDIO_FORMAT_G711A;
audio_param.channel = LV_AUDIO_CHANNEL_MONO;
audio_param.sample_bits = LV_AUDIO_SAMPLE_BITS_16BIT;
audio_param.sample_rate = LV_AUDIO_SAMPLE_RATE_8000;
int ret = lv_voice_intercom_start_service(service_id, &audio_param);
return 0;
}
lv_stop_voice_intercom_cb
接口详情:typedef int (*lv_stop_voice_intercom_cb)(int service_id)
回调函数,通知一路语音对讲服务已可以断开
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
lv_voice_intercom_stop_service
接口详情:int lv_voice_intercom_stop_service(int service_id)
断开一路语音对讲链路,在接收到语音对讲结束回调后调用
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
//demo,stopVoiceIntercomCallback是lv_start_voice_intercom_cb的实际实现
lv_stop_voice_intercom_cb = stopVoiceIntercomCallback;
int stopVoiceIntercomCallback(int service_id)
{
lv_voice_intercom_stop_service(service_id);
}
lv_voice_intercom_receive_metadata_cb
接口详情:typedef int (lv_voice_intercom_receive_metadata_cb)(int service_id, const lv_audio_param_s audio_param)
回调函数,通知语音对讲时,APP发送的音频参数格式
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
audio_param | const lv_audio_param_s * | APP发送的音频格式结构体 |
lv_voice_intercom_receive_data_cb
接口详情:typedef void (lv_voice_intercom_receive_data_cb)(const char buffer, unsigned int buffer_len)
回调函数,语音对讲时,APP发送的音频数据
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
buffer | const char* | APP发送的音频数据 |
buffer_len | int | APP发送的音频数据长度 |
lv_voice_intercom_send_audio
接口详情:int lv_voice_intercom_send_audio(int service_id, const char* buffer, int buffer_len , unsigned int timestamp_ms)
往一路语音对讲链路发送音频数据
- 参数说明
参数 | 类型 | 说明 |
---|---|---|
service_id | int | 链路的ID |
buffer | const char* | 设备音频数据 |
buffer_len | int | 设备音频数据长度 |
timestamp_ms | unsigned int | 设备音频时间戳 |
- 示例代码
//这个回调输入音频参数
void handlerVoiceIntercomBuffer(unsigned char *buffer, unsigned int buffer_size,
unsigned int present_time, int service_id)
{
lv_voice_intercom_send_audio(service_id, (const char *)buffer, buffer_size, present_time);
}
自验收标准
1.首帧加载 95%的请求 控制在1.2s以内;
2.推流延迟控制在1s以内;音视频同步应控制在300ms以内
3.设备端支持强制I帧指令,且两次I帧的最小间隔控制在100ms以内;
4.TF卡列表查询一天的录像文件,返回索引时间控制在1s以内;
5.TF卡视频播放,Seek指令的响应延迟在500ms以内;且持续Seek 100 次稳定可用;
6.语音对讲:收到G711A之后的解码耗时要小于音频文件的实际时间;
7.PTZ:云台转到底之后,收到该方向的指令需忽略指令,避免指令堆积;保持对后续指令的快速响应;
8.抓图:产生图片的延迟在300MS以内;直播时抓图,直播画面帧率无明显受影响;
9. 7 * 24 挂机,正常使用设备不崩溃
10. 设备支持静默定期重启机制;建议每五天自动静默重启一次;可提供用户设置;
11.设备需支持OTA
12.如采用二维码配网,则二维码扫描成功时间应控制在1.5s以内;
13.语音对讲: 设备端必须要支持AEC,确保设备端采集的音频数据没有回声