Android GB28181

您所在的位置:网站首页 安卓android国标gb28181 Android GB28181

Android GB28181

2024-07-11 23:53| 来源: 网络整理| 查看: 265

  GB28181-2022 新增图像抓拍功能, 这个功能很有用, 无需在设备实时点播的情况下,就可以抓图上传到指定的图像存储服务器上。如果配置合适的抓拍间隔,JPEG也选择适当的压缩参数,相比实时音视频回传更省流量,设备功耗也低。

  GB28181图像抓拍分为三步,一是下发图像抓拍配置命令给设备,  二是设备抓图并上传图像,三是设备通知服务器抓拍图像已完成传输。

  图像抓拍配置走的是有应答设备控制流程,配置信息采用SIP MESSAGE方法携带,Content-type是:Application/MANSCDP+xml, 命令协议还是MANSCDP协议,采用XML封装。设备收到图像抓拍配置命令后,需要发送配置响应命令,响应命令中包含执行结果信息。

  抓拍配置命令包含: 连拍张数(最多10张,最小1张), 单张抓拍间隔(单位秒,最短间隔是1秒), 抓拍图对象上传路径,会话ID。例如:

7 2 http://192.168.0.7:8070/snap 97593257101100111123900141597596

  设备收到抓拍配置命令后,按抓拍间隔时间和连拍张数来执行抓图, 抓拍的图像格式推荐为JPEG有损压缩格式(多数安卓设备也支持JPEG硬编码), 抓拍图像分辨率使用主码流分辨率, 图像文件名规则:设备ID(长度20)+ "02" + 日期时间(YYYYMMDDhhmmssSSS)+ 抓拍序号(长度为2) + ".jpeg"。抓拍到单张图像后建议立即上传,推荐用https/http传输图像.

  单次配置抓拍的所有图像上传完成后,需发送Message消息通知SIP服务器图像传输已完成, 通知消息中的SessionID应与抓拍配置命令中的SessionID一致。例如:

97593257101100111123900141597596 34020000001380000037022023122814385675501.jpeg 34020000001380000037022023122814385868002.jpeg 34020000001380000037022023122814390073803.jpeg 34020000001380000037022023122814390262604.jpeg 34020000001380000037022023122814390471205.jpeg 34020000001380000037022023122814390686506.jpeg 34020000001380000037022023122814390869907.jpeg

  相关实现代码:

/* * Copyright (C) [email protected]. All rights reserved. */ /** * 部分信令接口 */ package com.gb.ntsignalling; public interface GBSIPAgent { void setDeviceConfigListener(GBSIPAgentDeviceConfigListener deviceConfigListener); /* * 通知图像抓拍传输完成 */ boolean notifyUploadSnapShotFinished(String fromUserName, String fromUserNameAtDomain, String deviceID, String sessionID, java.util.List snapShotList); } /* * Device Config Listener */ package com.gb.ntsignalling; public interface GBSIPAgentDeviceConfigListener { /* * 图像抓拍配置 */ void ntsOnDeviceSnapShotConfig(String from_user_name, String from_user_name_at_domain, String sn, String device_id, SnapShotConfig config, List extra_info_list); } package com.gb.ntsignalling; public interface SnapShotConfig { int snap_num(); int interval(); String upload_url(); String session_id(); } /** * 抓拍JNI接口 */ public class SmartPublisherJniV2 { /** * 截图接口, 支持JPEG和PNG两种格式 * @param compress_format: 压缩格式, 0:JPEG格式, 1:PNG格式, 其他返回错误 * @param quality: 取值范围:[0, 100], 值越大图像质量越好, 仅对JPEG格式有效, 若是PNG格式,请填100 * @param file_name: 图像文件名, 例如:/dirxxx/test20231113100739.jpeg, /dirxxx/test20231113100739.png * @param user_data_string: 用户自定义字符串 * @return {0} if successful */ public native int CaptureImage(long handle, int compress_format, int quality, String file_name, String user_data_string); } /** * Device Snap Shot Listener 部分实现代码 */ public class GBDeviceSnapShotListenerImpl implements GBSIPAgentDeviceControlListener { @Override public void ntsOnDeviceSnapShotConfig(String from_user_name, final String from_user_name_at_domain, String sn, String device_id, final SnapShotConfig config, List extra_info_list) { if (null == config) return; handler_.postDelayed(new Runnable() { @Override public void run() { Log.i(TAG, "ntsOnDeviceSnapShotConfig device_id:" + device_id_ + " session_id:" + config.session_id() + ", snap_num:" + config.snap_num() + ", interval:" + config.interval() + ", upload_url:" + config.upload_url()); if (null == gb28181_agent_) return; if (null == snap_shot_impl_) { snap_shot_impl_ = new SnapShotGBImpl(image_path_, context_, handler_, lib_publisher_jni, snap_shot_publisher_); snap_shot_impl_.start(); } snap_shot_impl_.add_config(gb28181_agent_, from_user_name_, from_user_name_at_domain_, sn_, device_id_, snap_shot_config_, extra_info_list_); } private String from_user_name_; private String from_user_name_at_domain_; private String sn_; private String device_id_; private SnapShotConfig snap_shot_config_; private List extra_info_list_; public Runnable set(String from_user_name, String from_user_name_at_domain, String sn, String device_id, SnapShotConfig config, List extra_info_list) { this.from_user_name_ = from_user_name; this.from_user_name_at_domain_ = from_user_name_at_domain; this.sn_ = sn; this.device_id_ = device_id; this.snap_shot_config_ = config; this.extra_info_list_ = extra_info_list; return this; } }.set(from_user_name, from_user_name_at_domain, sn, device_id, config, extra_info_list), 0); } } public class SnapShotGBImpl extends SnapShotImpl { private List config_list_ = new LinkedList(); public SnapShotGBImpl(String dir, Context context, android.os.Handler handler, SmartPublisherJniV2 lib_sdk, LibPublisherWrapper publisher) { super(dir, context, handler, lib_sdk, publisher); } public boolean add_config(GBSIPAgent agent, String from_user_name, String from_user_name_at_domain, String sn, String device_id, SnapShotConfig config, List extra_info_list) { if (null == agent) return false; if (is_null_or_empty(device_id)) return false; if (null == config) return false; if (config.snap_num() < 1) return false; if (config.interval() < 1) return false; if (is_null_or_empty(config.session_id())) return false; SnapConfig c = new SnapConfig(agent, from_user_name, from_user_name_at_domain, sn, device_id, config, extra_info_list); config_list_.add(c); return true; } public void on_captured_image(long result, String file_name, long file_date_time_ms, String user_data) { SnapConfig config = find_config(user_data); if (null == config) { super.on_captured_image(result, file_name, file_date_time_ms, user_data); return; } SnapItem item = config.find_capturing_item(file_name); if (null == item) { super.on_captured_image(result, file_name, file_date_time_ms, user_data); return; } if (result != 0) { item.set_status(SnapItem.ERROR_STATUS); item.set_error_info("capture failed"); Log.e(TAG, "capture failed, file:" + file_name + ", session_id:" + user_data); return; } item.set_status(SnapItem.CAPTURE_COMPLETION_STATUS); } public void on_uploaded(boolean is_ok, String file_name, String session_id, String gb_name) { SnapConfig config = find_config(session_id); if (null == config) { Log.w(TAG, "on_uploaded cannot find config, session_id:" + session_id + ", gb_name:" + gb_name); return; } SnapItem item = config.find_uploading_item(gb_name); if (null == item) { Log.w(TAG, "on_uploaded cannot find item, session_id:" + session_id + ", gb_name:" + gb_name); return; } if (is_ok) { item.set_status(SnapItem.UPLOAD_COMPLETION_STATUS); Log.i(TAG, "on_uploaded ok, session_id:" + session_id + ", file:" + file_name); }else { item.set_status(SnapItem.ERROR_STATUS); item.set_error_info("upload failed"); Log.e(TAG, "on_uploaded failed, session_id:" + session_id + ", file:" + file_name); } } @Override public void on_stop() { this.config_list_.clear(); shutdown(200, TimeUnit.MILLISECONDS); } private void process_upload() { android.os.Handler app_handler = os_handler(); for(SnapConfig c : config_list_) c.upload_files(app_handler, this); } private void process_finished() { List notified_files = null; Iterator iterator = config_list_.iterator(); while(iterator.hasNext()) { SnapConfig c = iterator.next(); if (!c.is_can_notify_server()) continue; iterator.remove(); if (null == notified_files) notified_files = new LinkedList(); c.notify_server(notified_files); } // 暂时删除这些文件, 根据业务需求随时调整就好 if(notified_files != null && !notified_files.isEmpty()) execute(new DeleteFilesTask(notified_files)); } private static class SnapItem { private int status_ = INITIALIZATION_STATUS; private final String device_id_; private final int sn_; // 序列码, 40~41 private final String dir_; private String file_name_; } private static class SnapConfig { private WeakReference agent_; private final String from_user_name_; private final String from_user_name_at_domain_; private final String sn_; private final String device_id_; private final String session_id_; private final int snap_num_; private final String upload_url_; private final int interval_sec_; private final List extra_info_list_; private ArrayList items_; public final String session_id() { return this.session_id_; } public void upload_files(android.os.Handler os_handler, SnapShotGBImpl snap) { if (null == items_) return; for (SnapItem i : items_) { if (i.is_capture_completion_status()) { i.set_status(SnapItem.UPLOADING_STATUS); BaseUploadTask upload_task = new MyUploadTask(upload_url_, i.file_name(), i.gb_snap_shot_file_id(), session_id(), i.gb_name(), os_handler, snap); if (!snap.submit(upload_task) ) { i.set_status(SnapItem.ERROR_STATUS); i.set_error_info("submit upload task failed"); } } } } public void notify_server(List notified_files) { ArrayList snap_shot_list = new ArrayList(items_.size()); for (SnapItem i : items_) { if (SnapItem.UPLOAD_COMPLETION_STATUS == i.status()) snap_shot_list.add(i.gb_snap_shot_file_id()); if (notified_files != null) notified_files.add(i.file_name()); } if (null == agent_) return; GBSIPAgent agent = agent_.get(); if (null == agent) return; agent.notifyUploadSnapShotFinished(from_user_name_, from_user_name_at_domain_, device_id_, this.session_id(), snap_shot_list); } } private static class DeleteFilesTask implements Runnable { private List file_name_list_; public DeleteFilesTask(List file_name_list) { this.file_name_list_ = file_name_list; } @Override public void run() { if (null == file_name_list_) return; if (file_name_list_.isEmpty()) { file_name_list_ = null; return; } for (String i : file_name_list_) { try { File f = new File(i); if (!f.exists()||!f.isFile() ) continue; if (f.delete()) Log.i(TAG, "delete file:" + i); else Log.w(TAG, "delete file failed, " + i); } catch(Exception e) { Log.e(TAG, "DeleteFilesTask.run Exception:", e); } } file_name_list_.clear(); file_name_list_ = null; } } public static class BaseUploadTask extends CancellableTask { private final String upload_url_; private final String file_name_; private final String gb_snap_shot_file_id_; private final String session_id_; private final String gb_name_; private WeakReference os_handler_; private WeakReference snap_; public BaseUploadTask(String upload_url, String file_name, String gb_snap_shot_file_id, String session_id, String gb_name, android.os.Handler os_handler, SnapShotGBImpl snap) { this.upload_url_ = upload_url; this.file_name_ = file_name; this.gb_snap_shot_file_id_ = gb_snap_shot_file_id; this.session_id_ = session_id; this.gb_name_ = gb_name; if (os_handler !=null) this.os_handler_ = new WeakReference(os_handler); if (snap != null) this.snap_ = new WeakReference(snap); } protected final String upload_url() { return this.upload_url_; } protected final String file_name() { return this.file_name_; } protected final String gb_snap_shot_file_id() { return this.gb_snap_shot_file_id_; } protected final String session_id() { return this.session_id_; } protected final String gb_name() { return this.gb_name_; } protected final android.os.Handler os_handler() { if (os_handler_ != null) return os_handler_.get(); return null; } protected final SnapShotGBImpl snap() { if (snap_ != null) return snap_.get(); return null; } private static class ResultRunnable implements Runnable { private final boolean result_; private final String file_name_; private final String session_id_; private final String gb_name_; private WeakReference snap_; public ResultRunnable(boolean result, String file_name, String session_id, String gb_name, SnapShotGBImpl snap) { this.result_ = result; this.file_name_ = file_name; this.session_id_ = session_id; this.gb_name_ = gb_name; if (snap != null) this.snap_ = new WeakReference(snap); } @Override public void run(){ if (null == this.snap_) return; SnapShotGBImpl snap = this.snap_.get(); if (null == snap) return; snap.on_uploaded(result_, file_name_, session_id_, gb_name_); } } protected void post_result(boolean is_ok) { android.os.Handler handler = os_handler(); if (null == handler) return; SnapShotGBImpl gb_snap = snap(); if (null == gb_snap) return; handler.post(new ResultRunnable(is_ok, file_name_,session_id_, gb_name_, gb_snap)); } } }

  考虑到GB28181服务端实现非常多, 每家的上传图像方式可能有所不同,有些用https上传,有些用ftp上传,上传图像这块代码只能对接具体服务器时再做调整(我的代码只要增加一个新的上传实现类就好), 整个图像抓拍功能有些用Java实现有些用C++完成, 整体代码较多,上面只给出部分接口定义和实现代码, 如有不清楚的地方请联系qq: 1130758427, 微信:ldxevt。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3