首页 > 其他分享 >Android GB28181-2022 图像抓拍

Android GB28181-2022 图像抓拍

时间:2024-01-03 14:32:09浏览次数:32  
标签:file name GB28181 private 2022 snap Android id String

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

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

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

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

<SnapShotConfig>
<SnapNum>7</SnapNum>
<Interval>2</Interval>
<UploadURL>http://192.168.0.7:8070/snap</UploadURL>
<SessionID>97593257101100111123900141597596</SessionID>
</SnapShotConfig>

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

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

<SessionID>97593257101100111123900141597596</SessionID>
<SnapShotList>
<SnapShotFileID>34020000001380000037022023122814385675501.jpeg</SnapShotFileID>
<SnapShotFileID>34020000001380000037022023122814385868002.jpeg</SnapShotFileID>
<SnapShotFileID>34020000001380000037022023122814390073803.jpeg</SnapShotFileID>
<SnapShotFileID>34020000001380000037022023122814390262604.jpeg</SnapShotFileID>
<SnapShotFileID>34020000001380000037022023122814390471205.jpeg</SnapShotFileID>
<SnapShotFileID>34020000001380000037022023122814390686506.jpeg</SnapShotFileID>
<SnapShotFileID>34020000001380000037022023122814390869907.jpeg</SnapShotFileID>
</SnapShotList>

  相关实现代码:

/*
* 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<String> 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<String> 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<String> 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<String> extra_info_list_;

            public Runnable set(String from_user_name, String from_user_name_at_domain,
                                String sn, String device_id, SnapShotConfig config,
                                List<String> 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<SnapConfig> 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<String> 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<String> notified_files = null;

        Iterator<SnapConfig> 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<GBSIPAgent> 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<String> extra_info_list_;

        private ArrayList<SnapItem> 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<String> notified_files) {
            ArrayList<String> 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<String> file_name_list_;

        public DeleteFilesTask(List<String> 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<android.os.Handler> os_handler_;
        private WeakReference<SnapShotGBImpl> 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<SnapShotGBImpl> 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++完成, 整体代码较多,上面只给出部分接口定义和实现代码。

标签:file,name,GB28181,private,2022,snap,Android,id,String
From: https://blog.51cto.com/u_15527397/9067945

相关文章

  • GB18030-2022 标准学习
    GB18030-2022标准学习下载https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=A1931A578FE14957104988029B0833D3感觉挺大:240MB简单学习......
  • Visual Studio 2022 for Mac v17.0 发布,离线安装包下载
    为Mac打造的.NETIDE2022年5月23日,印度微软公布了VisualStudio2022forMacv17.0正式版发布(GA)的消息,并且现在可以下载了。这是迄今为止最快的VisualStudioforMac版本(sysin),具有全新的原生macOSUI,完全在.NET6上运行,并针对AppleSilicon(ARM64)处理......
  • Windows 11 简体中文版、英文版(64-bit、ARM64)下载(2022 年 1 月发布)
    Windows11最新正式版,2021年12月更新作者主页:www.sysin.org全新推出Windows11全新Windows体验,让您与热爱的人和事物离得更近。获得全新视角Windows11提供一个让人平静而富有创意的空间,全新体验引导您全力追逐热爱。从焕然一新的开始菜单,到与您关心的人、关注的消息、游戏......
  • Windows Server 2022 英文版、简体中文版下载 (updated Dec 2021)(2022 年 1 月发布)
    WindowsServer2022正式版,2021年12月更新2021.09.01更新,微软官方确认该版本为正式版:WindowsServer2022nowgenerallyavailable—deliversinnovationinsecurity,hybrid,andcontainers2021.08.19,微软在VLSC和MSDN发上发布了WindowsServer2022镜像下载,虽然并......
  • Windows Server 2022 中文版、英文版下载 (updated Jul 2023)
    WindowsServer2022中文版、英文版下载(updatedJul2023)WindowsServer2022正式版,2023年7月更新作者主页:sysin.org早期直观体验版本21H2,根据名称预计今年秋季发布正式版(已经发布)设置和控制面板仍然混乱,麦德龙风格和经典风格分裂设计仍然没有解决Edge成为默认浏览器(自带......
  • Windows Subsystem for Android (WSA) 下载:在 Windows 11 上运行 Android 应用 (July
    WindowsSubsystemforAndroid(WSA)下载:在Windows11上运行Android应用(July2023)适用于Android™️的Windows子系统,2023年7月更新:2306.40000.4.0作者主页:sysin.org适用于Android™️的Windows子系统使你的Windows11设备能够运行AmazonAppstore中提供的Andr......
  • [Android] 如何把自定义的 可执行文件/库文件/apk 放到系统目录下
    找到源码目录device/......./<devicename>/<devicename>.mk,以waydroid为例:/device/waydroid/waydroid/waydroid_arm64打开如下文件: lineage_waydroid_arm64.mk添加PRODUCT_PACKAGES变量到上述文件中,比如:PRODUCT_PACKAGES+=可执行文件名/动态库名字/静态库文件名字/apk文件名......
  • Android头像列表重叠最简实现
    手把手教程,针对初学者(老家伙请跳过),先看效果,能用得上的再继续往下看……针对上图重叠头像的展示实现方式,最简单的就是使用RecyclerView,利用其装饰器。平常使用RecyclerView都是每个item之间有间距,而这里不仅没间距还重叠了一部分,其实本质上也可以看成是有间距,只不过间距是负值。直接......
  • ThreadLocal:你不知道的优化技巧,Android开发者都在用
    引言在Android开发中,多线程是一个常见的话题。为了有效地处理多线程的并发问题,Android提供了一些工具和机制。其中,ThreadLocal是一个强大的工具,它可以使得每个线程都拥有自己独立的变量副本,从而避免了线程安全问题。本文将深入探讨Android中的ThreadLocal原理及其使用技巧,帮助你更......
  • 为什么Android系统开发工程师这么稀缺?
    前言今日得闲,思考了一个问题。为什么Android系统开发工程师各个公司都在招聘,而且是每天都在招,工资待遇也很客观,但结果却找不到人?首先,我们来看下Android系统开发工程师的主要职责是什么,以下是招聘网的要求。根据以上要求,我们来分析一下:本科学历,这个要求目前基本上都可以满足,再高一点......