Интерпретация и проектирование спецификаций захвата изображений GB/T28181-2022.
Интерпретация и проектирование спецификаций захвата изображений GB/T28181-2022.

Технический бэкграунд

По сравнению с версией 2016 года, GB/T28181-2022 имеет четкое определение захвата изображений. Захват изображений очень важен в индустрии видеонаблюдения. Терминал доступа к устройству GB28181 на платформе Android может захватывать изображения без загрузки аудио- и видеоданных в реальном времени. Загрузка на указанный сервер хранения изображений в реальном времени.

Основные требования к захвату изображений следующие:

  1. Когда исходное устройство отправляет команду настройки захвата изображения целевому устройству, ему необходимо передать такую ​​информацию, как путь передачи и идентификатор сеанса.
  2. После того как целевое устройство завершает передачу изображения, оно отправляет команду уведомления о завершении передачи захвата изображения, которая реализована с использованием метода MESSAGE в IETF RFC3428.
  3. Правило именования файлов изображений должно иметь форму «код устройства (20 бит), код изображения (2 бита), временной код (17 бит), серийный код (2 бита)».
  4. Формат изображения должен быть JPEG, а разрешение изображения должно быть таким же, как у основного потока.

Следует отметить, что поле заголовка Content-type заголовка сообщения MESSAGE имеет значение Content-type:Application/MANSCDP+xml, которое инкапсулировано в XML. После получения команды конфигурации захвата изображения устройство отправляет команду ответа на конфигурацию, и команда ответа содержит информацию о результате выполнения.

Процесс захвата изображения выглядит следующим образом:

Техническая реализация

SmartGBD из Daniu Live SDK завершил захват образа стороны доступа устройства GB28181.

Общая функциональная схема выглядит следующим образом:

  • [Формат видео] H.264/H.265 (жестко закодирован в Android H.265);
  • [Аудиоформат] G.711 A-law, AAC;
  • [Регулировка громкости] Терминал сбора данных на платформе Android поддерживает регулировку громкости в режиме реального времени;
  • [Жесткое кодирование H.264] Поддержка жесткого кодирования H.264 для определенных моделей;
  • [Жесткое кодирование H.265] Поддержка жесткого кодирования H.265 для определенных моделей;
  • [Конфигурация параметров программного и жесткого кодирования] Поддержка интервала кадров, частоты кадров, настроек скорости передачи данных;
  • [Конфигурация параметров программного кодирования] поддерживает профиль мягкого кодирования, скорость мягкого кодирования и настройки переменной скорости кодирования;
  • Поддерживает горизонтальную и вертикальную потоковую передачу на экране;
  • Платформа Android поддерживает push-экран фоновой службы (для push-экрана требуется версия 5.0+);
  • Поддержка чистой передачи видео, аудио и видео PS пакета;
  • Поддержка пассивного режима RTP OVER UDP и RTP OVER TCP (клиент потоковой передачи мультимедиа TCP);
  • Поддержка настроек протокола передачи сети канала сигнализации TCP/UDP;
  • Поддерживает регистрацию, отмену, обновление регистрации и настройку периода действия регистрации;
  • Поддержка ответа на запрос каталога устройства;
  • Поддержка механизма пульса, поддержка интервала пульса и настроек времени обнаружения пульса;
  • Поддержка подписки и уведомлений о местонахождении мобильного устройства (MobilePosition);
  • Применимые национальные стандарты: GB/T 28181-2016;
  • Поддержка голосового вещания;
  • Поддержка голосовой связи;
  • Поддержка захвата изображений;
  • Поддержка поиска исторических видео и аудио файлов;
  • Поддерживает загрузку исторических видео и аудио файлов;
  • Поддержка воспроизведения исторических видео и аудио файлов;
  • Поддержка управления PTZ и запроса предустановленного положения;
  • [Водяной знак в реальном времени] поддерживает динамические текстовые водяные знаки и водяные знаки PNG;
  • [Зеркалирование] Платформа Android поддерживает функцию зеркального отображения передней камеры в реальном времени;
  • [Отключение звука в реальном времени] Поддерживает отключение/включение звука в реальном времени;
  • [Снимок в реальном времени] Поддержка моментального снимка в реальном времени;
  • [Шумоподавление] поддерживает обработку шумоподавления, автоматическое усиление и обнаружение VAD, вызванного звуками окружающей среды, помехами мобильного телефона и т. д.;
  • [Стыковка видеоданных перед внешним кодированием] поддерживает стыковку данных YUV;
  • [Стыковка аудиоданных перед внешним кодированием] поддерживает стыковку PCM;
  • [Стыковка внешних кодированных видеоданных] Поддерживает стыковку внешних данных H.264;
  • [Стыковка аудиоданных после внешнего кодирования] Стыковка внешних данных AAC;
  • [Расширенная функция записи] Поддерживает использование в сочетании с SDK записи для обеспечения функций, связанных с записью.

Обработка сигналов, связанных с захватом изображения, осуществляется следующим образом:

Язык кода:java
копировать
/*
 * Author: daniusdk.com
 */
 
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);
 
}

Конфигурация устройства Listener следующая:

Язык кода:java
копировать
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);
}

Интерфейс настройки моментальных снимков выглядит следующим образом:

Язык кода:java
копировать
public interface SnapShotConfig {
    int snap_num();
    int interval();
    String upload_url();
    String session_id();
}

Интерфейс JNI захвата изображений устроен следующим образом:

Язык кода:java
копировать
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);
	 
}

Основной код прослушивателя снимков устройства выглядит следующим образом:

Язык кода:java
копировать
/*
 * Author: daniusdk.com
 */
 
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. Это хорошая отправная точка. Помимо поддержки регулярного доступа к аудио- и видеоданным, терминалы Android также могут поддерживать подписку и уведомление о местонахождении мобильного устройства (MobilePosition), захват изображений. голосовое вещание и голосовое сопряжение, загрузка и воспроизведение исторических видео и аудио. Заинтересованные разработчики могут отправить мне личное сообщение для обсуждения.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose