diff --git a/image.go b/image.go index 044bc05..410f974 100644 --- a/image.go +++ b/image.go @@ -15,10 +15,10 @@ const ( LedpanelHeight = 48 ) -func Listen() (chan image.Image, chan error) { +func Listen() (<-chan BitvisImage, chan error) { errs := make(chan error, 1) - out := make(chan image.Image, 1) - out <- bitvisImage{} + out := make(chan BitvisImage, 1) + out <- BitvisImage{} go func() { defer close(errs) defer close(out) @@ -43,7 +43,7 @@ func Listen() (chan image.Image, chan error) { return out, errs } -func handleConnection(conn net.Conn, out chan<- image.Image) error { +func handleConnection(conn net.Conn, out chan<- BitvisImage) error { for { if err := conn.SetReadDeadline(time.Now().Add(time.Second * 4)); err != nil { return err @@ -58,7 +58,7 @@ func handleConnection(conn net.Conn, out chan<- image.Image) error { } io.CopyN(ioutil.Discard, conn, 2) - var img bitvisImage + var img BitvisImage if _, err := io.ReadFull(conn, img[:]); err != nil { return err } @@ -69,20 +69,20 @@ func handleConnection(conn net.Conn, out chan<- image.Image) error { } } -type bitvisImage [LedpanelWidth * LedpanelHeight / 4]uint8 +type BitvisImage [LedpanelWidth * LedpanelHeight / 4]uint8 -func (img bitvisImage) ColorModel() color.Model { +func (img BitvisImage) ColorModel() color.Model { return color.RGBAModel } -func (img bitvisImage) Bounds() image.Rectangle { +func (img BitvisImage) Bounds() image.Rectangle { return image.Rectangle{ Min: image.Point{X: 0, Y: 0}, Max: image.Point{X: LedpanelWidth, Y: LedpanelHeight}, } } -func (img bitvisImage) At(x, y int) color.Color { +func (img BitvisImage) At(x, y int) color.Color { return bitvisColor(img[y*LedpanelWidth/4+x/4] >> ((3 - uint(x)%4) * 2)) } diff --git a/main.go b/main.go index 159215c..dc616d4 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "log" "net/http" "sync" - "time" ) func main() { @@ -17,8 +16,9 @@ func main() { err := <-errors log.Fatal(err) }() - var currentFrame []byte + var currentFrame BitvisImage var currentFrameLock sync.RWMutex + currentFrameUpdate := sync.NewCond(currentFrameLock.RLocker()) mux := http.NewServeMux() mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) { @@ -42,39 +42,45 @@ func main() { `)) }) - mux.HandleFunc("/stream.mpng", func(res http.ResponseWriter, req *http.Request) { - res.Header().Set("Content-Type", "multipart/x-mixed-replace; boundary=--pngboundary") - res.WriteHeader(http.StatusOK) + mux.HandleFunc("/stream.mpng", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "multipart/x-mixed-replace; boundary=--pngboundary") + w.WriteHeader(http.StatusOK) for { currentFrameLock.RLock() - buf := currentFrame + currentFrameUpdate.Wait() + img := currentFrame currentFrameLock.RUnlock() - res.Write([]byte("--pngboundary")) - res.Write([]byte("Content-Type: image/png\n")) - res.Write([]byte(fmt.Sprintf("Content-Length: %d\n\n", len(buf)))) - if _, err := io.Copy(res, bytes.NewReader(buf)); err != nil { + buf := encodeImage(&img) + w.Write([]byte("--pngboundary")) + w.Write([]byte("Content-Type: image/png\n")) + w.Write([]byte(fmt.Sprintf("Content-Length: %d\n\n", len(buf)))) + if _, err := io.Copy(w, bytes.NewReader(buf)); err != nil { return } - time.Sleep(time.Millisecond * 2) } }) - mux.HandleFunc("/frame.png", func(res http.ResponseWriter, req *http.Request) { - res.Header().Set("Content-Type", "image/png") + mux.HandleFunc("/frame.png", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "image/png") currentFrameLock.RLock() - buf := currentFrame + img := currentFrame currentFrameLock.RUnlock() - io.Copy(res, bytes.NewReader(buf)) + w.Write(encodeImage(&img)) }) go func() { log.Fatal(http.ListenAndServe(":13378", mux)) }() for img := range images { - var buf bytes.Buffer - enc := png.Encoder{CompressionLevel: png.BestSpeed} - enc.Encode(&buf, img) currentFrameLock.Lock() - currentFrame = buf.Bytes() + currentFrame = img + currentFrameUpdate.Broadcast() currentFrameLock.Unlock() } } + +func encodeImage(img *BitvisImage) []byte { + var buf bytes.Buffer + enc := png.Encoder{CompressionLevel: png.BestSpeed} + enc.Encode(&buf, img) + return buf.Bytes() +}