1. 侦测报警
涂鸦智能摄像机通常具有侦测报警的功能,可以通过设备功能点打开侦测开关。侦测告警主要分两种,声音检测和移动检测。当设备检测到声音或者物体移动时,会上报一个警告消息,如果你的 App 集成了推送功能,App 还会收到一个推送通知。集成推送请参考集成 Push。
报警消息通常会附带一张当前视频的截图。
直供电门铃设备,提供视频消息的能力。当有人按下门铃时,设备可以上传一段留言视频,这个消息也会通过报警消息获取到,消息体会附带一段 6 秒的加密视频。
1.1. 消息列表
1.1.1. 模块引用
implementation 'com.tuya.smart:tuyasmart-ipc-camera-message:3.13.0r128'
1.1.2. 消息日历
可以通过 Camera SDK 查询到某年某月有报警消息的日期,以便于在日历上直观展示。
接口说明
获取摄像机消息中心指定月含有消息的具体日期列表
void queryAlarmDetectionDaysByMonth(String jsonParams, ResultListener<JSONArray> listener)
参数说明
参数 | 类型 | 必传 | 说明 |
---|---|---|---|
msgSrcId | String | 必须 | devId(设备 ID) |
month | String | 必须 | 年月,中间以 - 拼接,例如: 2019-8 |
timeZone | String | 必须 | 时区, 例如: Asia/Shanghai |
然后将其整体包含在 json 字段中,例如
JSONObject object = new JSONObject();
object.put("msgSrcId", devId);
object.put("timeZone", TimeZoneUtils.getTimezoneGCMById(TimeZone.getDefault().getID()));
object.put("month", year + "-" + month);
messageBusiness.queryAlarmDetectionDaysByMonth(object.toJSONString(),
new Business.ResultListener<JSONArray>() {
@Override
public void onFailure(BusinessResponse businessResponse, JSONArray objects, String s) {
mHandler.sendEmptyMessage(ALARM_DETECTION_DATE_MONTH_FAILED);
}
@Override
public void onSuccess(BusinessResponse businessResponse, JSONArray objects, String s) {
List<Integer> dates = JSONArray.parseArray(objects.toJSONString(), Integer.class);
if (dates.size() > 0){
day = dates.get(0);
}
mHandler.sendEmptyMessage(ALARM_DETECTION_DATE_MONTH_SUCCESS);
}
});
返回数据结构示例(具体以实际请求为准)
*** JSON示例 ***
{
"result": ["20"],
"t": 1564390878414,
"success": true,
"status": "ok"
}
1.1.3. 消息类型
侦测报警消息根据触发方式定义有多种类型,部分类型又可以划分为一个大的分类。Camera SDK 提供获取默认分类的列表,以便于分类查询报警消息。
接口说明
获取消息类型
void queryAlarmDetectionClassify(String devId, ResultListener<ArrayList<CameraMessageClassifyBean>> listener)
参数说明
参数 | 类型 | 必传 | 描述 |
---|---|---|---|
devId | String | 必须 | devId(设备 ID) |
然后将其整体包含在 json 字段中,例如
public void queryCameraMessageClassify(String devId) {
if (messageBusiness != null) {
messageBusiness.queryAlarmDetectionClassify(devId, new Business.ResultListener<ArrayList<CameraMessageClassifyBean>>() {
@Override
public void onFailure(BusinessResponse businessResponse, ArrayList<CameraMessageClassifyBean> cameraMessageClassifyBeans, String s) {
mHandler.sendEmptyMessage(MOTION_CLASSIFY_FAILED);
}
@Override
public void onSuccess(BusinessResponse businessResponse, ArrayList<CameraMessageClassifyBean> cameraMessageClassifyBeans, String s) {
selectClassify = cameraMessageClassifyBeans.get(0);
mHandler.sendEmptyMessage(MOTION_CLASSIFY_SUCCESS);
}
});
}
}
返回数据结构示例(具体以实际请求为准)
*** JSON示例 ***
{
"result": [{
"describe": "All"
}, {
"describe": "Motion",
"msgCode": ["ipc_motion", "ipc_linger", "ipc_passby", "ipc_human", "ipc_car", "ipc_cat", "MOVEMENT_DETECT_MSG"]
}, {
"describe": "Talk",
"msgCode": ["ipc_connected", "ipc_unconnected", "ipc_refuse", "ipc_doorbell", "ipc_leave_msg"]
}],
"t": 1564390878289,
"success": true,
"status": "ok"
}
返回数据封装在 CameraMessageClassifyBean,格式说明如下
CameraMessageClassifyBean 数据模型
字段 | 描述 |
---|---|
describe | 消息描述 |
msgCode | 消息类型 |
selected | 无需关注 |
消息类型表示报警消息的触发形式,体现为报警消息数据模型的msgCode
属性。
消息类型说明
类型 | 说明 |
---|---|
ipc_motion | 移动侦测 |
ipc_doorbell | 门铃呼叫 |
ipc_dev_link | 设备联动 |
ipc_passby | 有人经过 |
ipc_linger | 有人徘徊 |
ipc_leave_msg | 门铃消息留言 |
ipc_connected | 门铃已接听 |
ipc_unconnected | 门铃未接听 |
ipc_refuse | 门铃拒接 |
ipc_human | 人形检测 |
ipc_cat | 宠物检测 |
ipc_car | 车辆检测 |
ipc_baby_cry | 婴儿哭声 |
ipc_bang | 异响 |
ipc_face | 检测到人脸 |
ipc_antibreak | 强拆报警 |
ipc_low_battery | 低电量告警 |
由于设备能力的不同,能触发的消息类型会有差别。消息分类和消息类型不同,消息类型表示报警消息的触发方式,消息分类是将一个或多个类型的消息组合成一个大类,比如
ipc_passby
、ipc_linger
、ipc_motion
可以组合成一个分类为移动侦测。
1.1.4. 消息列表
可以通过 Camera SDK 查询和删除侦测报警消息。
获取侦测报警消息列表
接口说明
void getAlarmDetectionMessageList(String json, ResultListener<JSONObject> listener)
参数说明
参数 | 类型 | 必传 | 说明 |
---|---|---|---|
msgSrcId | String | 必须 | devId (设备 ID) |
msgType | Int | 必须 | 消息类型,目前只要传4即可 |
limit | Int | 必须 | 请求的数量 |
offset | Int | 必须 | 请求的偏移量,用来做分页 |
startTime | Long | 必须 | 起始时间 传 Unix 时间戳,秒级 |
endTime | Long | 必须 | 结束时间 传 Unix 时间戳,秒级 |
msgCodes | String[] | 可选 | 如果不传,默认请求所有类型的数据,如果传了,则请求对应类型的消息数据(消息数组可以查看消息类型接口介绍) |
PS: 如果要查询某一天的全部消息数据,可以将结束时间和起始时间间隔一天的时间戳即可。然后将其整体包含在 json 字段中,例如
if (null != messageBusiness){
long startTime = DateUtils.getTodayStart(day);
long endTime = DateUtils.getTodayEnd(day) - 1L;
JSONObject object = new JSONObject();
object.put("msgSrcId", devId);
object.put("startTime", startTime);
object.put("endTime", endTime);
object.put("msgType", 4);
object.put("limit", 30);
object.put("keepOrig", true);
object.put("offset", offset);
if (null != selectClassify) {
object.put("msgCodes", selectClassify.getMsgCode());
}
messageBusiness.getAlarmDetectionMessageList(object.toJSONString(), new Business.ResultListener<JSONObject>() {
@Override
public void onFailure(BusinessResponse businessResponse, JSONObject jsonObject, String s) {
mHandler.sendMessage(MessageUtil.getMessage(MSG_GET_ALARM_DETECTION, ARG1_OPERATE_FAIL));
}
@Override
public void onSuccess(BusinessResponse businessResponse, JSONObject jsonObject, String s) {
List<CameraMessageBean> msgList;
try {
msgList = JSONArray.parseArray(jsonObject.getString("datas"), CameraMessageBean.class);
} catch (Exception e) {
msgList = null;
}
if (msgList != null) {
offset += msgList.size();
mHandler.sendMessage(MessageUtil.getMessage(MSG_GET_ALARM_DETECTION, ARG1_OPERATE_SUCCESS));
} else {
mHandler.sendMessage(MessageUtil.getMessage(MSG_GET_ALARM_DETECTION, ARG1_OPERATE_FAIL));
}
}
});
}
返回数据结构示例(具体以实际请求为准)
*** JSON示例 ***
{
"result" : {
"datas" : [ {
"actionURL" : "tuyaSmart://panel?devId=6c659ecc586a7da3b5pawj",
"msgContent" : "开关打开",
"msgType" : 4,
"time" : 1512639767,
"icon" : "https://images.tuyacn.com/smart/product_icon/kt.png",
"dateTime" : "2017-12-7 17:42",
"id" : 3261004,
"msgSrcId" : "6c659ecc586a7da3b5pawj",
"msgTypeContent" : "告警测试"
}, {
"actionURL" : "tuyaSmart://panel?devId=6c659ecc586a7da3b5pawj",
"msgContent" : "开关打开",
"msgType" : 4,
"time" : 1511420861,
"icon" : "https://images.tuyacn.com/smart/product_icon/kt.png",
"dateTime" : "2017-11-23 15:07",
"id" : 2967004,
"msgSrcId" : "6c659ecc586a7da3b5pawj",
"msgTypeContent" : "告警测试"
} ],
"totalCount" : 2
},
"t" : 1539243923807,
"success" : true,
"status" : "ok"
}
返回数据已经封装在 CameraMessageBean 中,格式如下
CameraMessageBean 数据模型
参数 | 说明 |
---|---|
dateTime | 发生日期 |
msgTypeContent | 无需关注 |
msgType | 无需关注 |
time | 发生事件的时间戳 |
attachPics | 消息事件的图片,数组格式,取第一个数据 |
attachVideos | 消息事件的视频数据,数组格式,取第一个数据 |
actionURL | / |
id | 事件 ID |
msgSrcId | 消息 ID |
批量删除报警消息
接口说明
void deleteAlarmDetectionMessageList(String ids, ResultListener<Boolean> listener)
参数说明
参数 | 类型 | 必传 | 说明 |
---|---|---|---|
ids | String | 必须 | 字符串,多个消息 id 用逗号,分隔 |
其整体包含在 json 字段中,例如
StringBuilder ids = new StringBuilder();
for (int i = 0; i < mWaitingDeleteCameraMessageList.size(); i++) {
ids.append(mWaitingDeleteCameraMessageList.get(i).getId());
if (i != mWaitingDeleteCameraMessageList.size() - 1) {
ids.append(",");
}
}
messageBusiness.deleteAlarmDetectionMessageList(ids.toString(), new Business.ResultListener<Boolean>() {
@Override
public void onFailure(BusinessResponse businessResponse, Boolean aBoolean, String s) {
mHandler.sendMessage(MessageUtil.getMessage(MSG_DELETE_ALARM_DETECTION, ARG1_OPERATE_FAIL));
}
@Override
public void onSuccess(BusinessResponse businessResponse, Boolean aBoolean, String s) {
mCameraMessageList.removeAll(mWaitingDeleteCameraMessageList);
mWaitingDeleteCameraMessageList.clear();
mHandler.sendMessage(MessageUtil.getMessage(MSG_DELETE_ALARM_DETECTION, ARG1_OPERATE_SUCCESS));
}
});
返回数据结构示例(具体以实际请求为准)
*** JSON示例 ***
{
"result" : true,
"t" : 1548058543873,
"success" : true,
"status" : "ok"
}
1.2. 视频消息
接口说明
注册监听器,只有注册之后才能获取到视频播放的回调数据
void registorOnP2PCameraListener(OnP2PCameraListener listener);
参数说明
参数 | 说明 |
---|---|
OnP2PCameraListener | 视频播放回调数据 |
接口说明
注入播放器 View,用来渲染视频画面
void generateCloudCameraView(IRegistorIOTCListener view);
参数说明
参数 | 说明 |
---|---|
IRegistorIOTCListener | 播放器组件 |
接口说明
创建云视频播放设备
void createCloudDevice(String cachePath, String devId, OperationDelegateCallBack callBack);
参数说明
参数 | 说明 |
---|---|
cachePath | 缓存文件地址 |
devId | 设备 id |
OperationDelegateCallBack | 操作回调 |
示例代码
cloudVideo?.createCloudDevice(cachePath, devId, object : OperationDelegateCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_MEDIA_DEVICE, ICameraVideoPlayModel.OPERATE_SUCCESS, data))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_MEDIA_DEVICE, ICameraVideoPlayModel.OPERATE_FAIL, errCode))
}
})
接口说明
播放报警消息中的视频
void playVideo(String videoUrl, int startTime, String encryptKey, OperationCallBack callback, OperationCallBack playFinishedCallBack);
参数说明
参数 | 说明 |
---|---|
videoUrl | 视频播放地址 |
startTime | 开始播放时间,初始值:0 |
encryptKey | 播放视频的秘钥 |
callback | 操作回调 |
playFinishedCallBack | 结束播放的操作回调 |
示例代码
override fun playVideo(videoUrl: String, startTime: Int, encryptKey: String) {
if (cloudVideo == null) {
return
}
cloudVideo!!.playVideo(videoUrl, startTime, encryptKey, object : OperationCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String, camera: Any) {
playState = CloudPlayState.STATE_PLAYING
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_PLAY, ICameraVideoPlayModel.OPERATE_SUCCESS))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int, camera: Any) {
playState = CloudPlayState.STATE_ERROR
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_PLAY, ICameraVideoPlayModel.OPERATE_FAIL))
}
}, object : OperationCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String, camera: Any) {
playState = CloudPlayState.STATE_COMPLETED
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_STOP, ICameraVideoPlayModel.OPERATE_SUCCESS))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int, camera: Any) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_STOP, ICameraVideoPlayModel.OPERATE_FAIL))
}
})
}
接口说明
暂停播放
void pauseVideo(OperationCallBack callback);
参数说明
参数 | 说明 |
---|---|
OperationDelegateCallBack | 操作回调 |
示例代码
override fun pauseVideo() {
if (cloudVideo == null) {
return
}
cloudVideo!!.pauseVideo(object : OperationCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String, camera: Any) {
playState = CloudPlayState.STATE_PAUSED
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_PAUSE, ICameraVideoPlayModel.OPERATE_SUCCESS))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int, camera: Any) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_PAUSE, ICameraVideoPlayModel.OPERATE_FAIL))
}
})
}
接口说明
恢复播放
void resumeVideo(OperationCallBack callback);
参数说明
参数 | 说明 |
---|---|
OperationDelegateCallBack | 操作回调 |
示例代码
override fun resumeVideo() {
if (cloudVideo == null) {
return
}
cloudVideo!!.resumeVideo(object : OperationCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String, camera: Any) {
playState = CloudPlayState.STATE_PLAYING
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_RESUME, ICameraVideoPlayModel.OPERATE_SUCCESS))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int, camera: Any) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_RESUME, ICameraVideoPlayModel.OPERATE_FAIL))
}
})
}
接口说明
停止播放
void stopVideo(OperationCallBack callback);
参数说明
参数 | 说明 |
---|---|
OperationDelegateCallBack | 操作回调 |
示例代码
override fun stopVideo() {
if (cloudVideo == null) {
return
}
cloudVideo!!.stopVideo(object : OperationCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String, camera: Any) {
playState = CloudPlayState.STATE_STOP
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_STOP, ICameraVideoPlayModel.OPERATE_SUCCESS))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int, camera: Any) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_STOP, ICameraVideoPlayModel.OPERATE_FAIL))
}
})
}
接口说明
设置视频播放的声音开关
void setCloudVideoMute(int mute, OperationDelegateCallBack callBack);
参数说明
参数 | 说明 |
---|---|
mute | 拾音器模式: ICameraP2P.MUTE/ICameraP2P.UNMUTE(静音/非静音) |
OperationDelegateCallBack | 操作回调 |
示例代码
private fun setCloudVideoMute(voiceMode: Int) {
if (cloudVideo == null) {
return
}
cloudVideo!!.setCloudVideoMute(voiceMode, object : OperationDelegateCallBack {
override fun onSuccess(sessionId: Int, requestId: Int, data: String) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_MUTE, IPanelModel.ARG1_OPERATE_SUCCESS, data))
}
override fun onFailure(sessionId: Int, requestId: Int, errCode: Int) {
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_MUTE, IPanelModel.ARG1_OPERATE_FAIL))
}
})
}
接口说明
当云视频不再使用的时候,销毁云视频播放设备
void deinitCloudVideo();
接口说明
视频数据回调
override fun onReceiveFrameYUVData(sessionId: Int, y: ByteBuffer, u: ByteBuffer, v: ByteBuffer, width: Int, height: Int, nFrameRate: Int, nIsKeyFrame: Int, timestamp: Long, nProgress: Long, nDuration: Long, camera: Any)
参数说明
参数 | 说明 |
---|---|
sessionId | \ |
Y | 视频Y数据 |
u | 视频U数据 |
v | 视频V数据 |
width | 视频画面的宽 |
height | 视频画面的高 |
nFrameRate | 帧率 |
nIsKeyFrame | 是否I帧 |
timestamp | 时间戳 |
nProgress | 时间进度(消息中心视频播放的进度) |
nDuration | 时长(消息中心视频播放时长) |
camera | \ |
在这里的视频播放只要关心 nProgress,nDuration 即可
示例代码
override fun onReceiveFrameYUVData(sessionId: Int, y: ByteBuffer, u: ByteBuffer, v: ByteBuffer, width: Int, height: Int, nFrameRate: Int, nIsKeyFrame: Int, timestamp: Long, nProgress: Long, nDuration: Long, camera: Any) {
val map = HashMap<String, Long>(2)
map["progress"] = nProgress
map["duration"] = nDuration
mHandler.sendMessage(MessageUtil.getMessage(ICameraVideoPlayModel.MSG_CLOUD_VIDEO_INFO, ICameraVideoPlayModel.OPERATE_SUCCESS, map))
}
流程图
1.2.1. 报警消息与存储卡回放
报警消息和存储卡回放没有直接联系,唯一的关联是在存储卡事件录制模式的情况下,报警消息和存储卡视频录制的触发原因和时间点是一样的。
报警消息保存在涂鸦云端,存储卡视频录像保存在摄像机的存储卡中,且存储卡中的视频在容量不足时,可能会被覆盖。存储卡录制的开关和侦测报警的开关也没有关联,所以即使在存储卡事件录制的模式下,报警消息和存储卡中的视频录像也不是一一对应的。
但是存在报警消息发生的时间点有视频录像的情况,Camera SDK 并不提供这种关联查找的接口,开发者可以通过报警消息的触发时间,在当天的存储卡录像视频片段中查找是否有对应的视频录像来建立这种关联。