go言語でのストリーミング配信
以前、pythonを用いてストーリング配信を行ってみました。
pythonを用いた時にはサーバサイドにdangoを用いていました。
今回はgo言語を用いてストリーミング配信を行ってみることにしました。
■構成
こちらの構成で実施してみました。
ただ実際、LinuxサーバはVirtualBoxを用いた仮想環境を用いました。

・PC:windows10
・Linuxサーバ:ubuntu16.04(Virtual Box上)
・カメラ:LOGICOOL HDプロ ウェブカム C920
■go言語でのカメラキャプチャ
カメラキャプチャにはgocvを用いました。
gocvはgo言語版のOpenCVです。
インストールなどはこちらを参考に。
elsammit-beginnerblg.hatenablog.com
gocvを用いたカメラキャプチャはこちらのようなコードを作成すればOKです。
package main
import (
"fmt"
"gocv.io/x/gocv"
)
var (
err error
webcam *gocv.VideoCapture
)
func main() {
webcam, err = gocv.OpenVideoCapture(0)
if err != nil {
fmt.Printf("Error opening capture device: \n")
return
}
defer webcam.Close()
img := gocv.NewMat()
defer img.Close()
window := gocv.NewWindow("Video Capture")
for {
ok := webcam.Read(&img)
if !ok {
fmt.Printf("Device closed\n")
return
}
window.IMShow(img)
window.WaitKey(1)
}
}こちらでキャプチャ画像(動画)を取得するカメラ番号を取得。
webcam, err = gocv.OpenVideoCapture(0)
こちらで1フレーム毎の画像を読み出し、IMShowで表示させます。
imgはMat形式になっており、Mat形式でのデータ読み出し、表示になります。
for {
ok := webcam.Read(&img)
if !ok {
fmt.Printf("Device closed\n")
return
}
window.IMShow(img)
window.WaitKey(1)
}
■ストリーミング配信(サーバサイド)
カメラキャプチャのストリーミング配信のコードはこちらのように作成しました。
package main
import (
"fmt"
"html/template"
"image"
"log"
"net/http"
"sync"
"time"
"gocv.io/x/gocv"
)
var (
err error
webcam *gocv.VideoCapture
)
var buffer = make(map[int][]byte)
var frame []byte
var mutex = &sync.Mutex{}
func main() {
go getframes()
host := "0.0.0.0:8080"
http.HandleFunc("/video", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("call video")
w.Header().Set("Content-Type", "multipart/x-mixed-replace; boundary=frame")
data := ""
for {
mutex.Lock()
data = "--frame\r\n Content-Type: image/jpeg\r\n\r\n" + string(frame) + "\r\n\r\n"
mutex.Unlock()
time.Sleep(1 * time.Millisecond)
w.Write([]byte(data))
}
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("index.html")
t.Execute(w, "index")
})
log.Fatal(http.ListenAndServe(host, nil))
}
func getframes() {
webcam, err = gocv.OpenVideoCapture(0)
if err != nil {
fmt.Printf("Error opening capture device: \n")
return
}
defer webcam.Close()
img := gocv.NewMat()
defer img.Close()
for {
if ok := webcam.Read(&img); !ok {
fmt.Printf("Device closed\n")
return
}
if img.Empty() {
continue
}
gocv.Resize(img, &img, image.Point{}, float64(1.5), float64(1.5), 0)
frame, _ = gocv.IMEncode(".jpg", img)
}
}getframes()関数とmain関数を並列処理させております。
var frame []byte
をグローバル変数で定義し、
getframes()関数にてframeを格納していきます。
同時に、
http.HandleFunc("/video", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "multipart/x-mixed-replace; boundary=frame")
data := ""
for {
mutex.Lock()
data = "--frame\r\n Content-Type: image/jpeg\r\n\r\n" + string(frame) + "\r\n\r\n"
mutex.Unlock()
time.Sleep(1 * time.Millisecond)
w.Write([]byte(data))
}
})により、/videoに対してhttpリクエストがあった場合にレスポンスとしてストリーミング配信を行っております。
for文でframeを読み出し、文字列変換、レスポンス返信をtime.sleep(1*time.Millisecond)の感覚で行っております。
■ストリーミング配信(フロントサイド)
フロントサイドでのhtmlはこちらです。
<head>
<h1>ストリーミング</h1>
</head>
<body>
<iframe src="httpパス/video" width="960" Height="720"></iframe><br/>
</body>フロントサイドは単純でiframeによりhtml内に別ページであるvideoパスを表示させております。
■結果
先ほどのソースコードを実行するとこんな感じになります。

■最後に
今回はgo言語でストリーミング配信頑張ってみました!!
だけど、、、現状httpレスポンス実行するサーバと画像キャプチャを分離出来ていないので汎用性が低いよな。。。
今度は分離させる方法を調べてみたいと思います!!
分かったらブログ書こうと思います。
