1. 程式人生 > >樹莓派視訊監控平臺實現錄製歸檔

樹莓派視訊監控平臺實現錄製歸檔

上一次用[樹莓派搭建了視訊監控平臺](https://www.cnblogs.com/itqn/p/13268916.html),成功實現了利用樹莓派當監控攝像頭,但是隻能線上監控沒有存檔功能,這次針對上次的監控平臺進行了改造,實現了錄製歸檔功能。 這次主要針對上次的平臺做以下幾點改造: 1. 新增視訊流錄製模組 2. 調整監控管理頁面 3. 新增錄製歸檔列表頁面 ### 1. 開發視訊流錄製模組 視訊錄製模組不像視訊推流模組那樣,可以一直不停止的工作(推流),因為錄製模組需要考慮錄製視訊的大小和斷流等因素,所以在必要的時候需要錄製流程進行處理。 針對斷流的情況,視訊錄製模組使用一個監控執行緒,當超過兩分鐘未錄製視訊幀時,停止當前錄製,錄製器通過呼叫ping方法來實現心跳: ```java 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(); } ``` 當視訊持續錄製是,需要限制視訊的大小,這裡視訊最長只錄制一小時,當錄製時長超過一小時後,歸檔重新錄製。 ```java if (System.currentTimeMillis() - startTime > MAX_RECORD_TIME) { destroy(); } if (recorder == null) { init(frame.imageWidth, frame.imageHeight); } ``` 這裡的錄製模組是單例,所以當物件建立的時候,就建立監聽執行緒並啟動它,以下是完成的實現: ```java 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](https://img2020.cnblogs.com/blog/2083963/202007/2083963-20200710220424274-552463272.png) 需要注意的是:要實現錄製,必須開啟監控,只有開啟了監控才可以錄製。 ### 3. 開發錄製控制介面 跟上次開發監控控制介面一樣,在IndexController中新增兩個介面用於控制“開啟錄製”和“停止錄製”。 ```java public void startRecord() { StreamManager.INSTANCE.startRecord(); redirect("/"); } public void stopRecord() { StreamManager.INSTANCE.stopRecord(); redirect("/"); } ``` 上面的`StreamManager`是視訊流管控中心,在這裡往推流器註冊一個視訊幀消費者,然後將視訊幀塞給錄製器實現錄製。 ```java private void registerFrameConsumer() { if (sender == null) { return; } sender.registerFrameConsumer(f -> { if (record) { StreamRecorder.INSTANCE.record(f); } }); } ``` 所以當開啟錄製時,只需要將`record`置為true即可。 ```java public void startRecord() { record = true; } ``` 而停止錄製時則將`record`置為false,同時關閉錄製。 ```java public void stopRecord() { record = false; StreamRecorder.INSTANCE.destroy(); } ``` ### 4. 播放錄製視訊 視訊錄製後會以開始錄製時間戳為名稱存放在錄製目錄中(程式設定的是:/home/pi/RevVideo),錄製的視訊格式是FLV,採用JavaCV錄製FLV無法直接使用HTML5的video播放,要播放錄製的視訊,可以用樹莓派自帶的媒體播放工具VLC, 下面視訊VLC播放已錄製的視訊畫面: ![stream-record-play](https://img2020.cnblogs.com/blog/2083963/202007/2083963-20200710222133377-1837093013.png) 至此,視訊監控平臺就實現了錄製歸檔功能。 ### 5. 開發視訊歸檔列表頁面 為了方便檢視樹莓派中錄製的視訊列表,可以開發一個簡單的頁面用於顯示已經錄製的視訊,實現這個功能只需要簡單的兩步即可。 1. 視訊列表顯示頁面開發 ```html 檢視視訊監控 >>>

#for(v : fList) #end
視訊名稱 視訊大小 錄製時間
#(v.name) #(v.size) #(v.time)
``` 2. 歸檔視訊列表介面開發 ```java public void index()