🍤
ブラウザ上でEbitengineを使ってwebカメラから取得した映像を描画してみた
目的
GoCVを使わずに、ブラウザで実行可能なwebカメラを使ったEbitengine製アプリを作りたい。
成果物
ブラウザ上でwebカメラから取得した映像をEbitengineを用いて描画することができました。
環境
$ go version
go version go1.21.5 darwin/arm64
ブラウザはChromeです。
コード
.
├── go.mod
├── go.sum
├── index.html
├── main.go
├── main.wasm
└── wasm_exec.js
コード
main.go
package main
import (
"bytes"
"encoding/base64"
"fmt"
"image"
"log"
"syscall/js"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
var (
video js.Value
stream js.Value
canvas js.Value
ctx js.Value
)
const (
ScreenWidth = 320
ScreenHeight = 240
)
func init() {
doc := js.Global().Get("document")
video = doc.Call("createElement", "video")
canvas = doc.Call("createElement", "canvas")
video.Set("autoplay", true)
video.Set("muted", true)
video.Set("videoWidth", ScreenWidth)
video.Set("videoHeight", ScreenHeight)
mediaDevices := js.Global().Get("navigator").Get("mediaDevices")
promise := mediaDevices.Call("getUserMedia", map[string]interface{}{
"video": true,
"audio": false,
})
promise.Call("then", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
stream = args[0]
video.Set("srcObject", stream)
video.Call("play")
canvas.Set("width", ScreenWidth)
canvas.Set("height", ScreenHeight)
ctx = canvas.Call("getContext", "2d")
return nil
}))
}
type Game struct {}
func newGame() *Game {
return &Game{}
}
func (g *Game) Update() error {
if !ctx.Truthy() {
return nil
}
ctx.Call("drawImage", video, 0, 0, ScreenWidth, ScreenHeight)
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
if stream.Truthy() && ctx.Truthy() {
b64 := canvas.Call("toDataURL", "image/png").String()
dec, err := base64.StdEncoding.DecodeString(b64[22:])
if err != nil {
log.Fatal(err)
}
img, _, err := image.Decode(bytes.NewReader(dec))
if err != nil {
log.Fatal(err)
}
screen.DrawImage(ebiten.NewImageFromImage(img), nil)
}
ebitenutil.DebugPrint(screen, fmt.Sprintf("%f", ebiten.ActualFPS()))
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return ScreenWidth * 2, ScreenHeight * 2
}
func main() {
ebiten.SetWindowSize(ScreenWidth*2, ScreenHeight*2)
ebiten.SetWindowTitle("Hello, World!")
if err := ebiten.RunGame(newGame()); err != nil {
log.Fatal(err)
}
}
その他のコードは以下の手順の通りに行ったものです。
解説
syscall/js
を使うことでJavaScriptを実行し、画像データを取得して、それをEbitengineで描画しています。
JavaScriptによるwebカメラの使い方は以下の記事を参考にしてください。
Update
内でvideoタグの映像をcanvasに移して、Draw
内でbase64文字列を取得し、image.Image
にDecodeしてからebiten.Image
としてscreen
に描画しています。
以下の記事を参考にしてください。
まとめ
まだ使い道は思いついていませんが、とりあえずEbitengineでwebカメラを使えることがわかってよかったです。
Discussion