上一次用樹莓派搭建了視頻監控平臺,成功實現了利用樹莓派當監控攝像頭,可是隻能在線監控沒有存檔功能,此次針對上次的監控平臺進行了改造,實現了錄製歸檔功能。html
樹莓派相關文章:java
此次主要針對上次的平臺作如下幾點改造:ide
視頻錄製模塊不像視頻推流模塊那樣,能夠一直不中止的工做(推流),由於錄製模塊須要考慮錄製視頻的大小和斷流等因素,因此在必要的時候須要錄製流程進行處理。工具
針對斷流的狀況,視頻錄製模塊使用一個監控線程,當超過兩分鐘未錄製視頻幀時,中止當前錄製,錄製器經過調用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(); }
當視頻持續錄製是,須要限制視頻的大小,這裏視頻最長只錄制一小時,當錄製時長超過一小時後,歸檔從新錄製。this
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(); } } }
這裏直接改造上次的監控管理頁面,將佈局調整爲左右模式,並新增了「開啓錄製」和「中止錄製」按鈕、以及「錄製歸檔列表」的入口跳轉,總體頁面效果以下:code
須要注意的是:要實現錄製,必須開啓監控,只有開啓了監控才能夠錄製。orm
跟上次開發監控控制接口同樣,在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(); }
視頻錄製後會以開始錄製時間戳爲名稱存放在錄製目錄中(程序設置的是:/home/pi/RevVideo),錄製的視頻格式是FLV,採用JavaCV錄製FLV沒法直接使用HTML5的video播放,要播放錄製的視頻,能夠用樹莓派自帶的媒體播放工具VLC, 下面視頻VLC播放已錄製的視頻畫面:
至此,視頻監控平臺就實現了錄製歸檔功能。
爲了方便查看樹莓派中錄製的視頻列表,能夠開發一個簡單的頁面用於顯示已經錄製的視頻,實現這個功能只須要簡單的兩步便可。
<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>
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"); }
最終效果以下:
雖然這個視頻監控平臺已經實現了監控和錄製功能,但仍有部分缺陷,若是有興趣能夠進行拓展。
好比:
=========================================================
項目源碼可關注公衆號 「HiIT青年」 發送 「raspi-record」 獲取。
關注公衆號,閱讀更多文章。
因爲上次發表的文章,被人盜用發佈在頭條上,這裏我在文章的圖片加了LOGO水印,不便之處請多包涵。