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

## はじめに
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](https://gstreamer.freedesktop.org/documentation/videotestsrc/index.html?gi-language=c)


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

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

![](https://kenpos.dev/wp-content/uploads/2021/09/markdown-img-paste-20210924003724393-600x354.png)

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

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

![](https://kenpos.dev/wp-content/uploads/2021/09/markdown-img-paste-20210924003658374-600x340.png)

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




## gocvでGstreamerの映像を取得する
映像の配信で使用していたautovideosink をappsinkに変更すればOKです。

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

![](https://kenpos.dev/wp-content/uploads/2021/09/markdown-img-paste-20210924004142607-600x233.png)

指定されたサイズの映像が、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になっていることが確認できます。

![](https://kenpos.dev/wp-content/uploads/2021/09/markdown-img-paste-2021092400553703-600x355.png)


## 機器間配信のテスト
次は機器間をまたいで映像を取得します。
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台構成で組んでいるのでかなり安定しているように思います。いいですね。

<iframe width="560" height="315" src="https://www.youtube.com/embed/1Pb60K_xIuw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


## 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))

}
```

### 実行結果
良い感じに動いています。
やりましたね！！

<iframe width="560" height="315" src="https://www.youtube.com/embed/QXUEGbl17WM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


## まとめ
GolangでGstreamerをいじって遊んでいる人って少ないようなので記事に残しました。

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

[心のデブを信じろ](https://kenpos.dev)　というブログもやってますのでよければお願いします。