From 6274a3f60ba8dcd4cf8d4588e22fd92bc1653af3 Mon Sep 17 00:00:00 2001 From: sean yu <55464069+hexbabe@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:13:43 -0500 Subject: [PATCH] [RSDK-9513] Add VideoSourceFromCamera helper in stream camera utils (#4625) --- robot/web/stream/camera/camera.go | 26 +++++++++++++++++- robot/web/stream/camera/camera_test.go | 37 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 robot/web/stream/camera/camera_test.go diff --git a/robot/web/stream/camera/camera.go b/robot/web/stream/camera/camera.go index 948bfc112dc..842f2bcf271 100644 --- a/robot/web/stream/camera/camera.go +++ b/robot/web/stream/camera/camera.go @@ -1,7 +1,12 @@ -// Package camera provides functions for looking up a camera from a robot using a stream +// Package camera provides utilities for working with camera resources in the context of streaming. package camera import ( + "context" + "image" + + "github.com/pion/mediadevices/pkg/prop" + "go.viam.com/rdk/components/camera" "go.viam.com/rdk/gostream" "go.viam.com/rdk/resource" @@ -19,3 +24,22 @@ func Camera(robot robot.Robot, stream gostream.Stream) (camera.Camera, error) { } return cam, nil } + +// VideoSourceFromCamera converts a camera resource into a gostream VideoSource. +// This is useful for streaming video from a camera resource. +func VideoSourceFromCamera(ctx context.Context, cam camera.Camera) gostream.VideoSource { + reader := gostream.VideoReaderFunc(func(ctx context.Context) (image.Image, func(), error) { + img, err := camera.DecodeImageFromCamera(ctx, "", nil, cam) + if err != nil { + return nil, func() {}, err + } + return img, func() {}, nil + }) + + img, err := camera.DecodeImageFromCamera(ctx, "", nil, cam) + if err == nil { + return gostream.NewVideoSource(reader, prop.Video{Width: img.Bounds().Dx(), Height: img.Bounds().Dy()}) + } + // Okay to return empty prop because processInputFrames will tick and set them + return gostream.NewVideoSource(reader, prop.Video{}) +} diff --git a/robot/web/stream/camera/camera_test.go b/robot/web/stream/camera/camera_test.go new file mode 100644 index 00000000000..8b2770e4a4e --- /dev/null +++ b/robot/web/stream/camera/camera_test.go @@ -0,0 +1,37 @@ +package camera_test + +import ( + "context" + "image" + "testing" + + "go.viam.com/test" + + "go.viam.com/rdk/components/camera" + "go.viam.com/rdk/rimage" + camerautils "go.viam.com/rdk/robot/web/stream/camera" + "go.viam.com/rdk/testutils/inject" + "go.viam.com/rdk/utils" +) + +func TestVideoSourceFromCamera(t *testing.T) { + sourceImg := image.NewRGBA(image.Rect(0, 0, 3, 3)) + cam := &inject.Camera{ + ImageFunc: func(ctx context.Context, mimeType string, extra map[string]interface{}) ([]byte, camera.ImageMetadata, error) { + imgBytes, err := rimage.EncodeImage(ctx, sourceImg, utils.MimeTypePNG) + test.That(t, err, test.ShouldBeNil) + return imgBytes, camera.ImageMetadata{MimeType: utils.MimeTypePNG}, nil + }, + } + vs := camerautils.VideoSourceFromCamera(context.Background(), cam) + + stream, err := vs.Stream(context.Background()) + test.That(t, err, test.ShouldBeNil) + + img, _, err := stream.Next(context.Background()) + test.That(t, err, test.ShouldBeNil) + + diffVal, _, err := rimage.CompareImages(img, sourceImg) + test.That(t, err, test.ShouldBeNil) + test.That(t, diffVal, test.ShouldEqual, 0) +}