• 树莓派视频监控平台实现录制归档


    上一次用树莓派搭建了视频监控平台,成功实现了利用树莓派当监控摄像头,但是只能在线监控没有存档功能,这次针对上次的监控平台进行了改造,实现了录制归档功能。

    树莓派相关文章:

    1. 树莓派搭建nexus2.x私服
    2. 树莓派搭建视频监控平台
    3. 树莓派视频监控平台实现录制归档(本文)
    4. 树莓派实现人脸打卡机

    这次主要针对上次的平台做以下几点改造:

    1. 新增视频流录制模块
    2. 调整监控管理页面
    3. 新增录制归档列表页面

    1. 开发视频流录制模块

    视频录制模块不像视频推流模块那样,可以一直不停止的工作(推流),因为录制模块需要考虑录制视频的大小和断流等因素,所以在必要的时候需要录制流程进行处理。

    针对断流的情况,视频录制模块使用一个监控线程,当超过两分钟未录制视频帧时,停止当前录制,录制器通过调用ping方法来实现心跳:

    public void run() {
      while (true) {
        try {
          TimeUnit.MINUTES.sleep(2);
        } catch (InterruptedException ignore) {
        }				
        if (System.currentTimeMillis() - timestamp > 2 * 60 * 1000) {
          destroy();
        }
      }
    }
    public void ping() {
      timestamp = System.currentTimeMillis();
    }
    

    当视频持续录制是,需要限制视频的大小,这里视频最长只录制一小时,当录制时长超过一小时后,归档重新录制。

    if (System.currentTimeMillis() - startTime > MAX_RECORD_TIME) {
      destroy();
    }
    if (recorder == null) {
      init(frame.imageWidth, frame.imageHeight);
    }
    

    这里的录制模块是单例,所以当对象创建的时候,就创建监听线程并启动它,以下是完成的实现:

    public class StreamRecorder {
    
      public static final StreamRecorder INSTANCE = new StreamRecorder();
      private static final int FPS = 25;
      private static final int MAX_RECORD_TIME = 60 * 60 * 1000;
    
      private long startTime;
      private FFmpegFrameRecorder recorder;
      private AtomicBoolean wait = new AtomicBoolean(false);
    
      private StreamRecorder() {
        new Thread(this.new ALiveWatcher()).start();
      }
    
      public void record(Frame frame) {
        if (wait.get() || frame == null) {
          return;
        }
        if (System.currentTimeMillis() - startTime > MAX_RECORD_TIME) {
          destroy();
        }
        if (recorder == null) {
          init(frame.imageWidth, frame.imageHeight);
        }
        long timestamp = 1000 * (System.currentTimeMillis() - startTime);
        if (timestamp > recorder.getTimestamp()) {
          recorder.setTimestamp(timestamp);
        }
        try {
          recorder.record(frame);
        } catch (Exception e) {
          destroy();
        }
      }
    
      private void init(int width, int height) {
        try {
          startTime = System.currentTimeMillis();
          String f = Const.RECORD_DIR + File.separator + startTime + ".flv";
          recorder = new FFmpegFrameRecorder(f, width, height);
          recorder.setFormat("flv");
          recorder.setFrameRate(FPS);
          recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
          recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
          recorder.start();
        } catch (Throwable e) {
          throw new RuntimeException(e);
        }
      }
    
      public void destroy() {
        if (recorder == null) {
          return;
        }
        try {
          wait.set(true);
          TimeUnit.SECONDS.sleep(1);
          recorder.close();
          recorder = null;
        } catch (Throwable ignore) {
        } finally {
          wait.set(false);
        }
      }
    
      class ALiveWatcher implements Runnable {
        private long timestamp;
        @Override
        public void run() {
          while (true) {
            try {
              TimeUnit.MINUTES.sleep(2);
            } catch (InterruptedException ignore) {
            }
            if (System.currentTimeMillis() - timestamp > 2 * 60 * 1000) {
              destroy();
            }
          }
        }
        public void ping() {
          timestamp = System.currentTimeMillis();
        }
      }
    }
    

    2. 改造监控管理页面

    这里直接改造上次的监控管理页面,将布局调整为左右模式,并新增了“开启录制”和“停止录制”按钮、以及“录制归档列表”的入口跳转,整体页面效果如下:

    stream-record.png

    需要注意的是:要实现录制,必须开启监控,只有开启了监控才可以录制。

    3. 开发录制控制接口

    跟上次开发监控控制接口一样,在IndexController中新增两个接口用于控制“开启录制”和“停止录制”。

    public void startRecord() {
      StreamManager.INSTANCE.startRecord();
      redirect("/");
    }
    public void stopRecord() {
      StreamManager.INSTANCE.stopRecord();
      redirect("/");
    }
    

    上面的StreamManager是视频流管控中心,在这里往推流器注册一个视频帧消费者,然后将视频帧塞给录制器实现录制。

    private void registerFrameConsumer() {
      if (sender == null) {
        return;
      }
      sender.registerFrameConsumer(f -> {
        if (record) {
          StreamRecorder.INSTANCE.record(f);
        }
      });
    }
    

    所以当开启录制时,只需要将record置为true即可。

    public void startRecord() {
      record = true;
    }
    

    而停止录制时则将record置为false,同时关闭录制。

    public void stopRecord() {
      record = false;
      StreamRecorder.INSTANCE.destroy();
    }
    

    4. 播放录制视频

    视频录制后会以开始录制时间戳为名称存放在录制目录中(程序设置的是:/home/pi/RevVideo),录制的视频格式是FLV,采用JavaCV录制FLV无法直接使用HTML5的video播放,要播放录制的视频,可以用树莓派自带的媒体播放工具VLC, 下面视频VLC播放已录制的视频画面:

    stream-record-play

    至此,视频监控平台就实现了录制归档功能。

    5. 开发视频归档列表页面

    为了方便查看树莓派中录制的视频列表,可以开发一个简单的页面用于显示已经录制的视频,实现这个功能只需要简单的两步即可。

    1. 视频列表显示页面开发
    <body>
      <a href="/"> 查看视频监控 >>> </a> 
      <br>
      <br>
      <div>
        <table border="1">
          <tr>
            <td>视频名称</td>
            <td>视频大小</td>
            <td>录制时间</td>
          </tr>
          #for(v : fList)
          <tr>
            <td>#(v.name)</td>
            <td>#(v.size)</td>
            <td>#(v.time)</td>
          </tr>
          #end
        </table>
      </div>
    </body>
    
    1. 归档视频列表接口开发
    public void index() {
      List<VideoVO> fList = new ArrayList<>(20);
      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    	
      File dir = new File(Const.RECORD_DIR);
      File[] fArray = dir.listFiles(f -> f.getName().endsWith(".flv"));
      if (fArray != null) {
        for (File f : fArray) {
          VideoVO vo = new VideoVO();
          vo.setName(f.getName());
          vo.setSize(f.length());
          vo.setTime(sdf.format(new Date(Long.parseLong(f.getName().replace(".flv", "")))));
          fList.add(vo);
        }
      }
      setAttr("fList", fList);
      render("index.html");
    }
    

    最终效果如下:

    stream-record-list

    6. 拓展玩法

    虽然这个视频监控平台已经实现了监控和录制功能,但仍有部分缺陷,如果有兴趣可以进行拓展。
    比如:

    1. 录制视频使用ffmpeg进行转码,然后使用HTML5-video标签进行播放回看。
    2. 录制视频提供删除和定期清理功能。
    3. 录制视频提供下载功能。

    =========================================================
    项目源码可关注公众号 “HiIT青年” 发送 “raspi-record” 获取。

    HiIT青年
    关注公众号,阅读更多文章。

    由于上次发表的文章,被人盗用发布在头条上,这里我在文章的图片加了LOGO水印,不便之处请多包涵。

  • 相关阅读:
    C++调用Java的Jar包(带参数)
    SOA创建Form表单
    vs2013 IntelliSense: "const char *" 类型的实参与 "LPCWSTR" 类型的形参不兼容
    idea无法正常使用SVN的解决方法
    关于C语言中print输出问题
    TC怎么在代码中实现选中一个TCCcomponent对象?
    Unity 1.0 中文文档:1 Unity 简介
    Unity(四):使用场景Ⅰ:建立类型映射
    这个世界上没有白干的活
    Unity(三):快速入门
  • 原文地址:https://www.cnblogs.com/itqn/p/13281792.html
Copyright © 2020-2023  润新知