GB28181设备接入侧录像查询和录像下载技术探究之实时录像

news2024/9/22 6:24:29

技术背景

我们在对接GB28181设备接入侧的时候,除了常规实时音视频按需上传外,还有个重要的功能,就是本地实时录像,录像后的数据,在执法记录仪等前端设备留底,然后,到工作站拷贝到专门的平台。

本文探讨的是,基于GB28181设备接入更进一步的处理:录像查询和录像下载,本文以我们Android平台开发的GB28181设备接入为例,做个简单的分析。

本地录像存储

GB28181设备接入侧,非常重要的功能属性就是实时录像,我们在做实时录像的时候,设计如下:

先说录像参数设置:

/**
	 * SmartPublisherJniV2.java
	 * Author: daniusdk.com
   * Created on 2015/09/20.
	 */
/**
	 * 音频录制开关, 目的是为了更细粒度的去控制录像, 一般不需要调用这个接口, 这个接口使用场景比如同时推送音视频,但只想录制视频,可以调用这个接口关闭音频录制
	 *
	 * @param is_recoder: 0: do not recorder; 1: recorder; sdk默认是1
	 *
	 * @return {0} if successful
	 */
public native int SmartPublisherSetRecorderAudio(long handle, int is_recoder);

/**
	 * 视频录制开关, 目的是为了更细粒度的去控制录像, 一般不需要调用这个接口, 这个接口使用场景比如同时推送音视频,但只想录制音频,可以调用这个接口关闭视频录制
	 *
	 * @param is_recoder: 0: do not recorder; 1: recorder; sdk默认是1
	 *
	 * @return {0} if successful
	 */
public native int SmartPublisherSetRecorderVideo(long handle, int is_recoder);

/**
     * Create file directory(创建录像存放目录)
     * 
     * @param path,  E.g: /sdcard/daniulive/rec
     * 
     * <pre> The interface is only used for recording the stream data to local side. </pre> 
     * 
     * @return {0} if successful
     */
public native int SmartPublisherCreateFileDirectory(String path);

/**
     * Set recorder directory(设置录像存放目录)
     * 
     * @param path: the directory of recorder file.
     * 
     * <pre> NOTE: make sure the path should be existed, or else the setting failed. </pre>
     * 
     * @return {0} if successful
     */
public native int SmartPublisherSetRecorderDirectory(long handle, String path);

/**
     * Set the size of every recorded file(设置单个录像文件大小,如超过最大文件大小,自动切换到下个文件录制)
     * 
     * @param size: (MB), (5M~500M), if not in this range, set default size with 200MB.
     * 
     * @return {0} if successful
     */
public native int SmartPublisherSetRecorderFileMaxSize(long handle, int size);

录像控制:

/**
	* Start recorder(开始录像)
	*
	* @return {0} if successful
	*/
public native int SmartPublisherStartRecorder(long handle);

/**
	 * Pause recorder(暂停/恢复录像)
	 *
	 * is_pause: 1表示暂停, 0表示恢复录像, 输入其他值将调用失败
	 *
	 * @return {0} if successful
	 */
public native int SmartPublisherPauseRecorder(long handle, int is_pause);

/**
   	* Stop recorder(停止录像)
   	*
   	* @return {0} if successful
   	*/
public native int SmartPublisherStopRecorder(long handle);

录像状态回调

private static class EventHandlerPublisherV2 implements NTSmartEventCallbackV2 {
  @Override
  public void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {

    Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);

    String publisher_event = "";

    switch (id) {
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
        publisher_event = "开始..";
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
        publisher_event = "连接中..";
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
        publisher_event = "连接失败..";
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
        publisher_event = "连接成功..";
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
        publisher_event = "连接断开..";
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
        publisher_event = "关闭..";
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
        publisher_event = "开始一个新的录像文件 : " + param3;
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
        if (record_executor_ != null) {
          RecordExecutorService executor = record_executor_.get();
          if (executor != null)
            executor.execute(new RecordFileFinishedHandler().set(handle, param3, param1));
        }
        publisher_event = "已生成一个录像文件 : " + param3;
        break;

      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:
        publisher_event = "发送时延: " + param1 + " 帧数:" + param2;
        break;

      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:
        publisher_event = "快照: " + param1 + " 路径:" + param3;

        if (param1 == 0) {
          publisher_event = publisher_event + "截取快照成功..";
        } else {
          publisher_event = publisher_event + "截取快照失败..";
        }
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:
        publisher_event = "RTSP服务URL: " + param3;
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:
        publisher_event ="RTSP status code received, codeID: " + param1 + ", RTSP URL: " + param3;
        break;
      case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:
        publisher_event ="服务器不支持RTSP推送, 推送的RTSP URL: " + param3;
        break;
    }

    String str = "当前回调状态:" + publisher_event;

    Log.i(TAG, str);

    if (handler_ != null) {
      android.os.Handler handler = handler_.get();
      if (handler != null) {
        Message message = new Message();
        message.what = PUBLISHER_EVENT_MSG;
        message.obj = publisher_event;
        handler.sendMessage(message);
      }
    }
  }

  public NTSmartEventCallbackV2 set(android.os.Handler handler, RecordExecutorService record_executor) {
    this.handler_ = new WeakReference<>(handler);
    this.record_executor_ = new WeakReference<>(record_executor);
    return this;
  }

  private WeakReference<android.os.Handler> handler_;
  private WeakReference<RecordExecutorService> record_executor_;
}

为适配GB28181的录像查询和处理,我们会把录像的文件,文件名做一定的处理,比如加上开始、结束时间还有duration和file size。

录像调用逻辑如下:

void ConfigRecorderParam() {
  if (libPublisher != null && publisherHandle != 0) {
    if (recDir != null && !recDir.isEmpty()) {

      int ret = libPublisher.SmartPublisherCreateFileDirectory(recDir);
      if (0 == ret) {
        if (0 != libPublisher.SmartPublisherSetRecorderDirectory(publisherHandle, recDir)) {
          Log.e(TAG, "Set record dir failed , path:" + recDir);
          return;
        }

        // 更细粒度控制录像的, 一般情况无需调用
        //libPublisher.SmartPublisherSetRecorderAudio(publisherHandle, 0);
        // libPublisher.SmartPublisherSetRecorderVideo(publisherHandle, 0);

        if (0 != libPublisher.SmartPublisherSetRecorderFileMaxSize(publisherHandle, 200)) {
          Log.e(TAG, "SmartPublisherSetRecorderFileMaxSize failed.");
          return;
        }

      } else {
        Log.e(TAG, "Create record dir failed, path:" + recDir);
      }
    }
  }
}

class ButtonStartRecorderListener implements View.OnClickListener {
  public void onClick(View v) {
    if (isRecording) {
      stopRecorder();

      if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
        ConfigControlEnable(true);
      }

      btnStartRecorder.setText("实时录像");

      btnPauseRecorder.setText("暂停录像");
      btnPauseRecorder.setEnabled(false);
      isPauseRecording = true;

      return;
    }

    Log.i(TAG, "onClick start recorder..");

    if (libPublisher == null)
      return;

    if (!isPushingRtmp && !isRTSPPublisherRunning&& !isGB28181StreamRunning) {
      InitAndSetConfig();
    }

    ConfigRecorderParam();

    int startRet = libPublisher.SmartPublisherStartRecorder(publisherHandle);
    if (startRet != 0) {
      if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
        if (publisherHandle != 0) {
          long handle = publisherHandle;
          publisherHandle = 0;
          libPublisher.SmartPublisherClose(handle);
        }
      }

      Log.e(TAG, "Failed to start recorder.");
      return;
    }

    if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
      CheckInitAudioRecorder();
      ConfigControlEnable(false);
    }

    startLayerPostThread();

    btnStartRecorder.setText("停止录像");
    isRecording = true;

    btnPauseRecorder.setEnabled(true);
    isPauseRecording = true;
  }
}

class ButtonPauseRecorderListener implements View.OnClickListener {
  public void onClick(View v) {
    if (isRecording) {

      if(isPauseRecording)
      {
        int ret = libPublisher.SmartPublisherPauseRecorder(publisherHandle, 1);

        if (ret == 0)
        {
          isPauseRecording = false;
          btnPauseRecorder.setText("恢复录像");
        }
        else if(ret == 3)
        {
          Log.e(TAG, "Pause recorder failed, please re-try again..");
        }
        else
        {
          Log.e(TAG, "Pause recorder failed..");
        }
      }
      else
      {
        int ret = libPublisher.SmartPublisherPauseRecorder(publisherHandle, 0);

        if (ret == 0)
        {
          isPauseRecording = true;
          btnPauseRecorder.setText("暂停录像");
        }
        else if(ret == 3)
        {
          Log.e(TAG, "Resume recorder failed, please re-try again..");
        }
        else
        {
          Log.e(TAG, "Resume recorder failed..");
        }
      }
    }
  }
}

总结

如果需要实现GB28181平台的录像查询和录像下载,实时录像的处理必不可少。下一章节,我们将根据GB28181规范探讨录像查询和录像下载。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

订单系统、报名、预约、表单系统 定制开发功能展示

安装教程环境说明&#xff1a;正常情况下PHP5.3-5.6、阿帕奇、mysql安装即可 安装说明&#xff1a; 1、上传源码压缩包到网站根目录&#xff08;这个请去问下空间商哪个是根目录&#xff0c;每家服务器商不一样&#xff0c;我们也不能确定&#xff0c;请确定是根目录再安装&am…

Java设计模式之行为型-解释器模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色设计 四、案例分析 五、总结 一、基础概念 解释器模式是指给定一个语言&#xff08;表达式&#xff09;&#xff0c;来表示它的文法&#xff0c;并定义一个解释器&#xff0c;使用该解释器来解释语言中的句子&#xff08;表达式&a…

Linux基础内容(25)—— 线程控制和线程结构

Linux基础内容&#xff08;24&#xff09; —— 线程概念_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131294692?spm1001.2014.3001.5501 目录 1.线程操作 1.线程创建问题 2.线程终止问题 1.exit退出 2.pthread_exit退出 3.直接退出 3…

只需一个提示词解除GPT-4的字符限制!

ChatGPT的内存有限,GPT-3.5-turbo的限制为4897个令牌,而GPT-4的最大限制为8192。如果您在使用GPT-4进行聊天时超过8192个令牌(约6827个单词),它就会开始遗忘。我想出了一种新的技巧,可以轻松将对话扩展10倍。 这种技巧不会将对话中的每个字都保存到内存中。当您去开会时,会有人…

0502事务原理-InnoDB引擎-MySQL-数据库

1 概述 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务特性 原子性&#xff08;Atomatic&#xff09;&#xff1a;事…

MiniGPT4系列之一部署篇:在RTX-3090 Ubuntu服务器部署步骤详解

MiniGPT4系列之一部署篇&#xff1a;在RTX-3090 Ubuntu服务器部署步骤详解_seaside2003的博客-CSDN博客 MiniGPT4系列之二推理篇命令行方式&#xff1a;在RTX-3090 Ubuntu服务器推理详解_seaside2003的博客-CSDN博客 MiniGPT4系列之三模型推理 (Web UI)&#xff1a;在RTX-309…

外包软件定制开发中关于沟通障碍及对应解决方案

引言 外包软件定制开发在当今的商业环境中越来越常见。它为公司提供了许多好处&#xff0c;包括降低成本、加速交付和专注于核心业务。然而&#xff0c;沟通障碍常常是外包软件定制开发中的一个重要挑战。由于外包团队和客户位于不同的地理位置、文化和语言差异&#xff0c;沟…

Python 列表 sort()函数使用详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 sort函数使用详解 1、升序降序2、sort()和sorted()的区别3、切片排序4、指定排序…

【C++】设计模式-单例模式

目录 一、单例模式 单例模式的三个要点 针对上述三要点的解决方案 常用的两类单例模式 二、懒汉模式实现 1.基本实现 2.锁静态成员析构单例 3.双层检查锁定优化 4.双层检查锁定智能指针 三、饿汉模式实现 1.基础实现 2.嵌套内部类解决内存泄漏 3.智能指针解决内存泄…

一种用于RBF神经网络的新型自适应内核研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

vuejs源码之虚拟dom中的vnode

在虚拟dom中&#xff0c;vnode是比较重要的。 什么是vnode 在vuejs中&#xff0c;有一个Vnode类 使用它可以实例不同类型的vnode实例&#xff0c;而不同类型的vnode实例各自表示不同类型的dom元素。 例如dom元素有文本节点&#xff0c;元素节点&#xff0c;注释节点等。 co…

Spring IoC及DI依赖注入

Spring 1.Spring的含义&#xff1a; Spring 可从狭义与广义两个角度看待 狭义的 Spring 是指 Spring 框架(Spring Fremework) 广义的 Spring 是指 Spring 生态体系 2.狭义的 Spring 框架 Spring 框架是企业开发复杂性的一站式解决方案 Spring 框架的核心是 IoC 容器和 AO…

数据库java中jdbcTemplate的事务问题

1.什么都不设置事务是默认提交的 两次获取的连接是不是一样的 参考文献(重磅): (542条消息) JdbcTemplate的事务控制_jdbctemplate transactionmanager_DayDayUp丶的博客-CSDN博客 PostMapping("/pinYin22")CrossOriginTransactionalpublic String pinYin22(HttpS…

【js实现语言国际化】使用json配置文件实现

需求&#xff1a;使用js让项目实现中文简体、繁体跟英文的切换&#xff0c;实现语言国际化 首先准备三种json配置文件&#xff1a; en.json {"textOne": "Today is Monday","textTwo": "Tomorrow is Tuesday","textThree"…

F#奇妙游(14):F#实现WPF的绑定

WPF中的绑定 绑定在UI开发中是一个非常重要的概念&#xff0c;它可以让我们的UI界面和数据模型之间建立起联系&#xff0c;当数据模型发生变化时&#xff0c;UI界面也会随之变化&#xff0c;反之亦然。这样的好处是显而易见的&#xff0c;我们不需要手动去更新UI界面&#xff…

金智教育IPO过会:计划募资约6亿元,郭超、史鸣杰为实控人

7月13日&#xff0c;深圳证券交易所披露的信息显示&#xff0c;江苏金智教育信息股份有限公司&#xff08;下称“金智教育”&#xff09;获得上市委会议通过。据贝多财经了解&#xff0c;金智教育于2022年6月30日递交上市申请材料&#xff0c;先后递交了6个版本的招股书&#x…

NDK OpenGL与OpenCV实现大眼萌特效

NDK​系列之OpenGL与OpenCV实现大眼萌特效&#xff0c;本节主要是在上一节OpenGL仿抖音极快极慢录制特效视频上增加大眼萌的特效。 OpenGL视频特效系列&#xff1a; NDK OpenGL渲染画面效果 NDK OpenGL离屏渲染与工程代码整合 NDK OpenGL仿抖音极快极慢录制特效视频 NDK O…

通讯录实现

普通版 需求 通讯录可以用来存储1000个人的信息&#xff0c;每个人的信息包括&#xff1a;姓名、性别、年龄、电话、住址 提供方法&#xff1a; 添加联系人信息删除指定联系人信息查找指定联系人信息修改指定联系人信息显示所有联系人信息清空所有联系人以名字排序所有联系…

【Linux后端服务器开发】UDP协议

目录 一、端口号 二、UDP报头格式 三、UDP的特点 四、UDP协议实现网络聊天群 一、端口号 端口号port标识了一个主机上进行通信的不同的应用程序。 0 ~ 1023&#xff1a;系统端口号&#xff0c;HTTP、FTP、SSH等这些广为使用的应用层协议&#xff0c;它们的端口号都是固定…

Windows软件开发常用技巧总结

本文总结了本人在日常工作学习中遇到的问题及其解决方法&#xff0c;没有固定的涉及领域 目的就是为了在下一次遇到类似问题的时候方便查找&#xff0c;从而快速解决问题 本文不定时更新~ 目录 Windows使用 如何实现桌面图标随意排列 文件资源管理器相关 显示隐藏文件 修改…