...
Advanced code syntax highlighter | ||
---|---|---|
| ||
{"code":"admin@merlin-99997:~/gomerlin $ go run cmd/simplestream/main.go\n{\"level\":\"info\",\"time\":\"2024-12-27T17:33:01+01:00\",\"message\":\"Serving images on [:55553/stream]\"}","theme":"coy","language":"bash","showLineNumbers":true} |
Open a browser on the host computer that is connected to Merlin’s access point and navigate to http://192.168.50.1:55553/stream
...
The browser will show the live video stream from the camera. Controls for focus, IR-LEDs as well as image processing (cropping, mirroring, …) will be added in other examples. Please find below a brief introduction to this examples code.
Advanced code syntax highlighter | ||
---|---|---|
| ||
{"code":"package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"image\"\n\t\"image/jpeg\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/textproto\"\n\n\t\"github.com/rs/zerolog/log\"\n\n\t\"github.com/vladimirvivien/go4vl/device\"\n\t\"github.com/vladimirvivien/go4vl/v4l2\"\n)\n\nconst (\n\tPORT = \":55553\" // Port number for the server\n\tDEVNAME = \"/dev/video0\" // Name of the video device\n)\n\nvar (\n\tframes <-chan []byte // Channel to receive frames from the video device\n\tpixfmt v4l2.PixFormat // Pixel format of the video stream\n\tframeWidth int // Width of the frame\n\tframeHeight int // Height of the frame\n)\n\n// frameSrv is a handler that serves the frames from the video device\n// as a multipart stream.\nfunc frameSrv(w http.ResponseWriter, r *http.Request) {\n\t// --- Create a multipart writer and set the boundary ------------------------\n\tmimeWriter := multipart.NewWriter(w)\n\tmimeWriter.SetBoundary(\"frame\")\n\tw.Header().Set(\"Content-Type\", fmt.Sprintf(\"multipart/x-mixed-replace; boundary=%s\", mimeWriter.Boundary()))\n\tpartHeader := make(textproto.MIMEHeader)\n\tpartHeader.Add(\"Content-Type\", \"image/jpeg\")\n\n\tvar frame []byte // bugger for frames as slice of byte\n\timg := image.NewGray(image.Rect(0, 0, frameWidth, frameHeight)) // buffer for frame as image data type\n\n\t// --- read frames from video device and send them to the host ---------------\n\tfor frame = range frames {\n\t\tpartWriter, err := mimeWriter.CreatePart(partHeader) // create part to write to mulitpart writer\n\t\tif err != nil {\n\t\t\tlog.Error().Msgf(\"Failed to create multipart writer part: %v\", err)\n\t\t\treturn\n\t\t}\n\n\t\timg.Pix = frame // copy the frame into the image\n\t\terr = jpeg.Encode(partWriter, img, nil) // encode the image as jpeg into the writer\n\t\tif err != nil {\n\t\t\tlog.Error().Msgf(\"failed to encode frame to jpeg image: %v\", err)\n\t\t}\n\t}\n}\n\n// main is the entry point of the program.\n// It opens the video device, starts the device and serves the frames\n// on the specified port.\nfunc main() {\n\t// --- Open the video device -------------------------------------------------\n\tcam, err := device.Open(DEVNAME, device.WithBufferSize(1))\n\tif err != nil {\n\t\tlog.Fatal().Msgf(\"Failed to open device %s: %v\", DEVNAME, err)\n\t}\n\tdefer cam.Close() // Close the device when the program exits\n\n\t// --- Get the pixel format of the video stream ------------------------------\n\tpixfmt, err = cam.GetPixFormat()\n\tif err != nil {\n\t\tlog.Fatal().Msgf(\"Failed to get pixel format: %v\", err)\n\t}\n\n\t// --- Get frame width/height and print the pixel format ---------------------\n\tframeWidth = int(pixfmt.BytesPerLine)\n\tframeHeight = int(pixfmt.Height)\n\tfmt.Sprintf(\"Video stream format: %d x %d\\n %s\\n\", frameWidth, frameHeight, pixfmt)\n\n\t// --- Start the video device ------------------------------------------------\n\terr = cam.Start(context.TODO())\n\tif err != nil {\n\t\tlog.Fatal().Msgf(\"Failed to start device: %v\", err)\n\t}\n\n\t// --- Get the frames from the video device ----------------------------------\n\tframes = cam.GetOutput()\n\n\t// --- Serve the frames on the specified port --------------------------------\n\tlog.Info().Msgf(\"Serving images on [%s/stream]\", PORT)\n\thttp.HandleFunc(\"/stream\", frameSrv)\n\terr = http.ListenAndServe(PORT, nil)\n\tif err != nil {\n\t\tlog.Fatal().Msgf(\"Failed to start server: %v\", err)\n\t}\n}","theme":"coy","language":"go","showLineNumbers":true} |