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レスポンス実行するサーバと画像キャプチャを分離出来ていないので汎用性が低いよな。。。
今度は分離させる方法を調べてみたいと思います!!
分かったらブログ書こうと思います。