樹莓派視頻監控平台實現錄制歸檔


上一次用樹莓派搭建了視頻監控平台,成功實現了利用樹莓派當監控攝像頭,但是只能在線監控沒有存檔功能,這次針對上次的監控平台進行了改造,實現了錄制歸檔功能。

樹莓派相關文章:

  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水印,不便之處請多包涵。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM