JavaCV 樹莓派打造監控系統平台


使用樹莓派搭建視頻監控平台去年就簡單的實現了,只不過功能比較簡陋,最近抽時間重構了原來的平台。

其他關於JavaCV的文章,可以通過下面的鏈接查看:
JavaCV-開發系列文章匯總篇(https://www.cnblogs.com/itqn/p/14696221.html)

環境搭建

環境部分,參考舊版的安裝及配置:

  1. 樹莓派搭建視頻監控平台
  2. 樹莓派視頻監控平台實現錄制歸檔

框架選型

之前的平台是采用JFinal和JavaCV開發的, 這次重構,調整為Springboot + JavaCV實現,主要用到的框架有:

  1. spring-webmvc
  2. spring-websocket
  3. spring-jdbc
  4. oshi
  5. javacv
  6. vue
  7. iview
  8. flv.js

功能完善

這次重構完善了很多功能,引入了服務器監控、采用VUE+IVIEW對頁面重新開發,另外還支持了在線回訪錄制視頻,目前已實現的功能有:

  1. 攝像頭監控畫面實時預覽
  2. 自動錄制
  3. 分段錄制
  4. 磁盤保護
  5. 樹莓派運行參數監控
  6. 錄制管理(支持錄制回放)
  7. 登錄鑒權

除了上面功能外,此次重構還大大降低了延遲,以及增加多種拉流類型,在無buffer拉流的情況下,FLV、RTMP流延遲在1秒以內(600ms左右),拉流端支持FLV、RTMP、HLS。

界面預覽

由於瀏覽器已經不再支持Flash,所以本次監控畫面實時預覽采用FLV,播放器采用flv.js,實時預覽延遲1秒內。

監控錄制由原來的FLV,調整為mp4,支持H5原生播放器觀看監控回放,這里由於我在畫面上加了時間戳濾鏡,導致錄制效果不是很好,后期需要排查優化一下。

功能實現

  1. 畫面采集,FFmpeg推流

在舊版的實現中,采用的是JavaCV采集畫面,並實現推流,在實際使用情況中,這種效果並不理想,延遲太高。所以在新版重構時,選擇raspivid + ffmpeg來替代JavaCV實現。

畫面采集采用raspivid:

raspivid -n -t 0 -w 1920 -h 1080 -fps 25 -b 600000 -o - 

推流采用FFmpeg:

ffmpeg -i - -vcodec copy -an -f flv rtmp://192.168.1.5:1935/hls/stream0
  1. 拉流,錄制

拉流還是采用JavaCV,這里分為兩部分,拉流及流加工錄制,如果不需要對流進行處理的話,直接錄制流即可。

拉流:

grabber = new FFmpegFrameGrabber(streamUri);
Frame f = grabber.grab();
if (f != null && f.image != null) {
  if (f != null && f.image != null) {
    Iterator <StreamListener> it = listeners.iterator();
    while (it.hasNext()) {
      StreamListener sl = it.next();
      final Frame ff = f.clone();
      sl.onVideo(ff);
    }
  }
}

錄制:

public void onVideo(Frame frame) {
  // 分段錄制
  if (timeLimit > 0 && System.currentTimeMillis() - startTime > timeLimit) {
    try {
      RecordINF inf = stop();
      if (timeoutCB != null) {
        timeoutCB.accept(inf);
      }
      startTime = System.currentTimeMillis();
      timestamp = 0;
      videoFile = new File(videoDir, startTime + ".mp4");
      start();
    } catch (Exception e) {
      LOG.error(e.getMessage(), e);
    }
  }

  try {
    if (timestamp == 0) {
      timestamp = System.currentTimeMillis();
    }
    videoTS = 1000 * (System.currentTimeMillis() - timestamp);
    if (videoTS > recorder.getTimestamp()) {
      recorder.setTimestamp(videoTS);
    }
    recorder.record(frame);
  } catch (org.bytedeco.javacv.FrameRecorder.Exception e) {
    LOG.error(e.getMessage(), e);
  }
}
  1. 磁盤保護

監控平台會不斷錄制視頻,所有如果不對磁盤容量進行監控的話,可能會出現錄制視頻塞爆磁盤的情況,這里只針對錄制文件存放的掛載盤進行監控。

@Scheduled(fixedRate = 30_000)
public void execute() {
  long byteG = settingService.setting().getStorageLimit() * G;
  List<OSFileStore> dsList = si.getOperatingSystem().getFileSystem().getFileStores();
  dsList.forEach(osf -> {
    if (appCfg.getMonitorMountDir().equals(osf.getMount()) && osf.getFreeSpace() < byteG) {
      clean();
    }
  });
}
  1. 樹莓派運行參數監控

平台啟動后定時采集運行參數信息,采用websocket的形式,定時推送到web端展示。

@Scheduled(fixedRate = 5000)
public void send() throws InterruptedException {
  OperatingSystem os = si.getOperatingSystem();
  CentralProcessor cpu = si.getHardware().getProcessor();

  info.setOs(os.getManufacturer() + " " + os.getFamily() + " " + os.getVersionInfo().getVersion());
  String ipV4Gateway = os.getNetworkParams().getIpv4DefaultGateway();
  ipV4Gateway = ipV4Gateway.substring(0, ipV4Gateway.lastIndexOf("."));
  info.setIp("");
  List < NetworkIF > nList = si.getHardware().getNetworkIFs();
  for (NetworkIF f : nList) {
    if (Arrays.toString(f.getIPv4addr()).replace("[", "").replace("]", "").startsWith(ipV4Gateway)) {
      info.setIp(Arrays.toString(f.getIPv4addr()).replace("[", "").replace("]", ""));
      break;
    }
  }

  long[] pts = cpu.getSystemCpuLoadTicks();
  TimeUnit.SECONDS.sleep(1);
  long[] cts = cpu.getSystemCpuLoadTicks();
  long t = 0;
  for (int i = 0; i < pts.length; i++) {
    t += cts[i];
  }
  info.setCpr(
    new DecimalFormat("#.##%").format(1.0 - (cts[CentralProcessor.TickType.IDLE.getIndex()] * 1.0 / t)));
  info.setCpt(String.valueOf(si.getHardware().getSensors().getCpuTemperature()));

  LocalDateTime btime = LocalDateTime.ofInstant(Instant.ofEpochSecond(os.getSystemBootTime()),
    ZoneId.systemDefault());
  info.setBtime(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(btime));
  info.setUtime(FormatUtil.formatElapsedSecs(os.getSystemUptime()));

  List < OSFileStore > dsList = si.getOperatingSystem().getFileSystem().getFileStores();
  info.getFs().clear();
  dsList.forEach(ds -> info.addFs(ds.getName() + "(" + ds.getMount() + ")" + ":" // 
    + FormatUtil.formatBytes(ds.getFreeSpace()) + "/"
    + FormatUtil.formatBytes(ds.getTotalSpace())));
  template.convertAndSend(AppWSConst.TOPIC_MONITOR, JSON.toJSONString(info));
}
  1. 實時監控畫面預覽

由於瀏覽器已經不再支持Flash,所以畫面預覽采用FLV流。

livePlay(url) {
  if (!!!url) return
  if (this.player) {
    this.player.destroy()
  }
  this.living = true
  this.buildPlayer({
    live: true, autoplay: true,
    video: {
      url: url,
      type: 'flv'
    },
    pluginOptions: {
      flv: {
        config: {
          enableStashBuffer: false
        }
      }
    }
  })
}
  1. 錄制回放

錄制的視頻是mp4格式,采用flv.js直接播放即可。

videoPlay(url) {
  if (!!!url) return
  if (this.living || !this.player) {
    this.buildPlayer({
      autoplay: true, hotkey: true,
      video: {
        url: url,
        type: 'mp4'
      }
    })
  } else {
    this.player.switchVideo({ url: url, type: 'mp4' })
  }
  this.living = false
}

應用部署

在部署方面,所有服務均部署在樹莓派3B+中,並設置開機自啟,這樣只要樹莓派通電,所有功能就啟動並運行。

關注公眾號“HiIT青年”,了解更多關於樹莓派及JavaCV開發的文章

=========================================================
關注 公眾號 “HiIT青年” 發送 “javacv-raspi-iview” 獲取樹莓派開機推流設置方式。

HiIT青年
關注公眾號,閱讀更多文章。


免責聲明!

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



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