Raspberry pi 4 2台を使って映像配信と受信してみた。【Gstreamer +Golang (gocv)】

2021-09-28

はじめに

OpenCV4の入力にカメラ映像ではなく、Gstreamerからの入力を受け取り表示します。 Raspberry piのカメラユニットの映像を別PCで動かしているOpenCvで受信してみます。

今回動かすシステムの構成としてはこのようになっています。

WEB camera - Raspbery pi 4(カメラ配信サーバ) =====  Raspberry pi 4(受信サーバ)

Gstreamer の勉強も兼ねているので、先ずは単機で動かしてステップアップ方式で進めていきます。

記事の構成がわかりにくくてクソなのですが、「機器間配信のテスト」まではWindows10で実行しています。 それ以降はRaspberry pi4 2台を使って配信と受信を行っています。

大まかな流れは変わらないのでご容赦ください。

Gstreamerでの配信について

配信する側もゆくゆくはソフトとして作っていきますが、今回はコマンドラインで代用します。

Gstreamerの動作確認

インストール済みだとは思いますが、映像を配信できるかを確かめます。

videotestsrcで起動すると映像が配信されます。pattern=snowを選択すると、 地デジになる前のテレビで電波が届いていないときに表示されていたような映像が流れます。(伝わるかしら) 映画ドラえもん ブリキのラビリンスで、深夜子どもたちが見ていたあのジャギジャギの映像です。

参考 videotestsrc

gst-launch-1.0 -v videotestsrc pattern=snow ! video/x-raw,width=1280,height=720 ! autovideosink

実行するとこんな感じで映像が取得されます。

ジャギジャギなのはあまり落ち着かないので別の映像で試します。

gst-launch-1.0 -v videotestsrc ! video/x-raw,width=1280,height=720 ! autovideosink

後ろでYoutube見ているのがバレちゃいますが、Terminalと合わせてこのように出力されているのが確認できます。

gocvでGstreamerの映像を取得する

映像の配信で使用していたautovideosink をappsinkに変更すればOKです。

gst-launch-1.0 -v videotestsrc ! video/x-raw,width=1280,height=720 ! appsink

指定されたサイズの映像が、appsinkに流れています。 Terminalでは時間が経過するばかりで、映像が表示されません。

gocvを使ってappsinkを表示するプログラムを書いていきます。 gocvのTutorialにあるHello, videoをべ-スに作ります。

OpenVideoCaptureにgstreamerのコマンドを引数にとして渡してやればOKです。

実行コード

package main

import (
    "gocv.io/x/gocv"
)

func main() {
    src := `videotestsrc ! video/x-raw,width=1280,height=720 ! appsink`
    webcam, _ := gocv.OpenVideoCapture(src)
    window := gocv.NewWindow("Hello")
    img := gocv.NewMat()

    for {
        webcam.Read(&img)
        window.IMShow(img)
        window.WaitKey(1)
    }
}

実行結果

先ほどと表示内容に変化はないですが、Windowの名前がHelloになっていることが確認できます。

機器間配信のテスト

次は機器間をまたいで映像を取得します。 Golangで組む前にコマンドで動作することを確認しましょう。

Raspberry pi4(カメラ配信サーバ)IP:192.168.0.3

GstreamerからVideoカメラのデバイスを指定し、配信を開始します。 RTPパケットを使って映像配信を行っていきます。 udpsinkでは受信先のIPアドレスとポート番号を指定してあげます。

gst-launch-1.0 -v v4l2src device=/dev/video1 ! "image/jpeg,width=640, height=360,framerate=30/1" ! rtpjpegpay ! udpsink host=192.168.0.4 port=9000

Raspberry pi4 (映像受信サーバ) IP:192.168.0.4

gst-launch-1.0 -e -v udpsrc port=9000 ! application/x-rtp, encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! autovideosink

実行結果

カメラ配信サーバに取り付けたUSBカメラの映像を同一ネットワークにおいている別の機器で出力してみました。 想像以上に遅延なくて少し驚いています。こんなに低遅延で実現できるのが驚いています。

tcpserversinkで配信したときは20秒ぐらい遅延があったのでそれを思うとかなり高速になっています。 あとは、WindowsとRaspberry pi2ってのも影響しているのでしょうか。 今回はRaspberry pi4を2台構成で組んでいるのでかなり安定しているように思います。いいですね。

golangから呼び出してみよう

この記事の本題に入ります。

Raspberry pi4 (映像受信サーバ) IP:192.168.0.4

Golangから読み出してやるときMat型で呼び出すためにappsinkを使います。

jpegdec以下をこのように変換します。

jpegdec ! videoconvert ! appsink

全文コード

package main

import (
    "gocv.io/x/gocv"
)

func main() {
    src := `udpsrc port=9000  ! application/x-rtp, encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! videoconvert ! appsink`
    webcam, _ := gocv.OpenVideoCapture(src)
    window := gocv.NewWindow("Hello")
    img := gocv.NewMat()

    for {
        webcam.Read(&img)
        window.IMShow(img)
        window.WaitKey(1)
    }
}

Raspberry pi4(カメラ配信サーバ)IP:192.168.0.3

Golangから呼び出すコードを書いていきます。 Gstreamerで呼び出したコマンドをexecで実行してあげます。 他に良いやり方があれば是非教えて下さい。お待ちしております。

全文コード

package main

import (
  "fmt"
  "os"
  "os/exec"
    "strings"
)

func main() {
    args := strings.Fields("-v v4l2src device=/dev/video1 ! image/jpeg,width=640, height=360,framerate=30/1 ! rtpjpegpay ! udpsink host=192.168.0.4 port=9000")
    out, err := exec.Command("gst-launch-1.0", args...).Output()
    if err != nil {
      fmt.Println(err.Error())
      os.Exit(1)
    }
    fmt.Println(string(out))

}

実行結果

良い感じに動いています。 やりましたね!!

まとめ

今日は久しぶりに技術ブログっぽい内容になってるんじゃないでしょうか。 GolangでGstreamerをいじって遊んでいる人って少ないのかあまり居ないんですね。

今回はRaspberry pi4とRaspberry pi4で配信と受信をやっていますが、Golangで書いているので どのOSでもあるていど使い回しが出来っるんじゃないかと期待してます。

満足です。