樹莓派搭建影片監控平台

上一次用樹莓派搭建了Nexus私服,終於讓樹莓派不再成為吃灰派了,這次用樹莓派搭建影片監控平台,並實現影片畫面推流到流媒體伺服器。

1. 安裝nginx

要實現將影片畫面推動到媒體伺服器,需要搭建一個流媒體伺服器,這裡選擇nginx + flv module 來搭建,需要用到的源碼包如下:

nginx1.15.4.tar.gz
nginx-http-flv-module-1.2.6.tar.gz
openssl-1.1.0j.tar.gz
pcre-8.40.tar.gz

將上面所有的源碼在/usr/local/src下面解壓,然後配置並編譯安裝nginx。

cd nginx1.15.4
sudo ./configure \
--sbin-path=/usr/local/nginx/nginx \
--conf-path=/usr/local/nginx/nginx.conf \
--pid-path=/usr/local/nginx/nginx.pid \
--add-module=../nginx-http-flv-module-1.2.6 \
--with-http_flv_module --with-http_mp4_module \
--with-http_ssl_module \
--with-pcre=../pcre-8.40 \
--with-http_ssl_module \
--with-openssl=../openssl-1.1.0j

sudo make
sudo make install

安裝完成後會在/usr/local/中多一個nginx目錄,這裡就是安裝好的nginx,這裡備份默認nginx配置nginx.conf,然後編寫自己的nginx配置。

cd /usr/local/nginx
sudo mv nginx.conf nginx.conf.def
sudo vim nginx.conf

自己的nginx.conf如下:

worker_processes  1;
events {
    worker_connections  1024;
}
rtmp {
  server {
    listen 1935;
    application live {
      live on;
    }
  }
}

2. 測試推流

nginx + flv module 搭建完後可以是用 ffmpeg 測試推流,首先啟動nginx。

cd /usr/local/nginx
sudo ./nginx

然後在windows平台中使用ffmpeg推流

ffmpeg.exe -ss 0 -i out.mp4 -acodec copy -f flv rtmp://192.168.1.26:1935/live/t1

最後用VLC播放影片流,如果以上所有操作沒有出現錯誤,將可以在VLC中看到影片畫面。

raspi-ffmpeg

3. 測試攝影機

要搞影片監控還需要一個攝影機,這裡使用的是樹莓派CSI介面中的攝影機,如果攝影機功能沒有開啟的話需要在樹莓派開啟。

sudo raspi-config

選擇 1. Interfacing Options

raspi5

然後再選擇 P1 Camera

raspip1

選擇「是」,然後重啟樹莓派。

sudo reboot

樹莓派重啟之後,可以執行下面指令用攝影機截圖,如果截圖成功說明攝影機配置成功。

sudo modprobe bcm2835-v4l2
sudo raspistill -v -o camera.jpg

4. 平台開發

所有環境準備完成後,剩下的就是開發一個可以管理攝影機推流的平台了,這裡選用JavaCV + JFinal來開發影片監控管理平台。

下載JFinal demo

從JFinal官網下載JFinal 4.9 demo for maven的源碼,導入Eclipse開發工具,刪除不需要的程式碼包括資料庫配置等。

只留下我們需要的程式碼:

DemoConfig.java
IndexController.java
index.html

開發攝影機推流器

使用OpenCV採集攝影機的影片幀,然後使用FFmpeg推流,編碼方式採用H264,幀率是25。

package com.demo.stream;
/**
 * @author itqn
 */
public class StreamSender implements Runnable {

  private static final int FPS = 25;
  private String rtmpUri;
  private OpenCVFrameGrabber grabber;
  private FFmpegFrameRecorder recorder;
  private boolean running = false;

  public int width;
  public int height;

  public StreamSender(String rtmpUri) {
    this.rtmpUri = rtmpUri;
    this.init();
  }

  @Override
  public void run() {
    running = true;
    long startTime = System.currentTimeMillis();
    long timestamp = 0;
    while (running) {
      timestamp = 1000 * (System.currentTimeMillis() - startTime);
      if (timestamp > recorder.getTimestamp()) {
        recorder.setTimestamp(timestamp);
      }
      try {
        recorder.record(grabber.grab());
      } catch (Throwable e) {
        close();
      }
      try {
        TimeUnit.MILLISECONDS.sleep(1000 / FPS);
      } catch (Exception ignore) {}
    }
  }
  
  public void close() {
    running = false;
    try {
      TimeUnit.SECONDS.sleep(1);
    } catch (Exception ignore) {}
    destroy();
  }

  private void init() {
    try {
      grabber = new OpenCVFrameGrabber(0);
      grabber.start();
      Frame frame = grabber.grab();
      width = frame.imageWidth;
      height = frame.imageHeight;
    
      recorder = new FFmpegFrameRecorder(rtmpUri, width, height);
      recorder.setFormat("flv");
      recorder.setFrameRate(FPS);
      recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
      recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
      recorder.setVideoOption("preset", "slow");
      recorder.setVideoOption("tune", "zerolatency");
      recorder.start();
    } catch (Throwable e) {
      throw new RuntimeException(e);
    }
  }
  
  private void destroy() {
    try {
      recorder.close();
    } catch (Throwable ignore) {}
    try {
      grabber.close();
    } catch (Throwable ignore) {}
  }
}

編寫控制介面

調整一下IndexController的程式碼,新增啟動監控和停止監控的介面。

public void start() {
  String rtmpUri = get("rtmpUri");
  if (StrKit.isBlank(rtmpUri)) {
    redirect("/?e=1");
    return;
  }
  try {
    StreamManager.INSTANCE.startSender(rtmpUri);
    redirect("/");
  } catch (Throwable e) {
    redirect("/?e=2");
  }
}

public void stop() {
  StreamManager.INSTANCE.stopSender();
  redirect("/");
}

編寫播放頁面

rtmp流播放採用videojs這個庫

<!DOCTYPE html>
<html>
<head>
<title>樹莓派影片監控</title>
<link href="/css/video-js.css" rel="stylesheet">
<script src="/js/video.js"></script>
</head>
<body>
  <video id="video" class="video-js vjs-default-skin" controls
     poster="//vjs.zencdn.net/v/oceans.png" preload="auto"
     width="#(width)" height="#(height)" data-setup="{}">
    <source src="#(streamUri)" type="rtmp/flv">
  </video>
  <br>
  <br>
  <div style="color: #ff0000">#(e)</div>
  <div>
    <form action="/start">
      推流地址 : 
      <input name="rtmpUri" value="#(streamUri??'rtmp://127.0.0.1:1935/hls/test')"/>
      <br><br>
      <button>開啟監控</button>
    </form>
    <br>
    <form action="/stop">
      <button>斷開監控</button>
    </form>
  </div>
</body>
</html>

5. 部署平台

平台開發完成後需要將平台部署到樹莓派中運行起來即可,JFinal默認的demo已經提供了啟停腳本,所以只需要在Eclipse中執行 mvn install 即可。

打包成功後,在項目的target目錄下面會有如下結構:

jfinal_demo_for_maven-release
\jfinal_demo_for_maven
\-\config
\-\lib
\-\webapp
\-\jfinal.sh
# 另外我們需要將target目錄下的jfinal_demo_for_maven-4.9.jar複製到lib目錄下。

將上面的jfinal_demo_for_maven整個目錄FTP上傳到樹莓派中,啟動平台:

sudo ./jfinal.sh start
# 這裡可以改動jfinal.sh中指定的平台訪問埠

啟動成功後,可以在電腦上訪問平台:

//192.168.1.26:8080

然後填寫推流地址,點擊開始監控即可。

raspi-stream

這樣,基於樹莓派的影片監控平台就部署好了。如果要關閉影片監控,只需要點擊頁面上的 斷開監控 即可。

6. 拓展玩法

這裡為了實踐我是自己在樹莓派上搭建了一個基於nginx + flv module 的流媒體伺服器,當然還有很多玩法。

比如:

a. 在線上伺服器搭建流媒體伺服器,然後將影片流推送到線上伺服器,這樣就可以實現遠程影片監控。

b. 另外也可以將影片流推送到直播平台,實現直播。

=========================================================
項目源碼可關注公眾號 「HiIT青年」 發送 「raspi-video」 獲取。

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

Tags: