上一次用樹莓派搭建了視頻監控平台,成功實現了利用樹莓派當監控攝像頭,但是只能在線監控沒有存檔功能,這次針對上次的監控平台進行了改造,實現了錄制歸檔功能。
樹莓派相關文章:
- 樹莓派搭建nexus2.x私服
- 樹莓派搭建視頻監控平台
- 樹莓派視頻監控平台實現錄制歸檔(本文)
- 樹莓派實現人臉打卡機
這次主要針對上次的平台做以下幾點改造:
- 新增視頻流錄制模塊
- 調整監控管理頁面
- 新增錄制歸檔列表頁面
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. 改造監控管理頁面
這里直接改造上次的監控管理頁面,將布局調整為左右模式,並新增了“開啟錄制”和“停止錄制”按鈕、以及“錄制歸檔列表”的入口跳轉,整體頁面效果如下:
需要注意的是:要實現錄制,必須開啟監控,只有開啟了監控才可以錄制。
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播放已錄制的視頻畫面:
至此,視頻監控平台就實現了錄制歸檔功能。
5. 開發視頻歸檔列表頁面
為了方便查看樹莓派中錄制的視頻列表,可以開發一個簡單的頁面用於顯示已經錄制的視頻,實現這個功能只需要簡單的兩步即可。
- 視頻列表顯示頁面開發
<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");
}
最終效果如下:
6. 拓展玩法
雖然這個視頻監控平台已經實現了監控和錄制功能,但仍有部分缺陷,如果有興趣可以進行拓展。
比如:
- 錄制視頻使用ffmpeg進行轉碼,然后使用HTML5-video標簽進行播放回看。
- 錄制視頻提供刪除和定期清理功能。
- 錄制視頻提供下載功能。
=========================================================
項目源碼可關注公眾號 “HiIT青年” 發送 “raspi-record” 獲取。
關注公眾號,閱讀更多文章。
由於上次發表的文章,被人盜用發布在頭條上,這里我在文章的圖片加了LOGO水印,不便之處請多包涵。