diff --git a/config/config.go b/config/config.go index 4f2409d..50484c6 100644 --- a/config/config.go +++ b/config/config.go @@ -15,10 +15,16 @@ import ( "github.com/3DRX/webrtc-ros-bridge/consts" ) +type ImageSpecifications struct { + Width int `json:"Width"` + Height int `json:"Height"` + FrameRate float64 `json:"FrameRate"` +} type TopicConfig struct { - NameIn string `json:"name_in"` - NameOut string `json:"name_out"` - Type string `json:"type"` // only "sensor_msgs/msg/Image" is supported + NameIn string `json:"name_in"` + NameOut string `json:"name_out"` + Type string `json:"type"` // only "sensor_msgs/msg/Image" is supported + ImgSpec ImageSpecifications `json:"image_spec"` // only valid when type is "Image" } type Config struct { @@ -116,7 +122,12 @@ func checkCfg(c *Config) error { return fmt.Errorf("wrong topic name format: \"" + topic.NameIn + "\" or \"" + topic.NameOut + "\"") } switch topic.Type { - case consts.MSG_IMAGE, consts.MSG_LASER_SCAN: + case consts.MSG_IMAGE: + tmp := topic.ImgSpec + if !(tmp.Width > 0 && tmp.Height > 0 && tmp.FrameRate > 0) { + return fmt.Errorf(fmt.Sprintf("wrong params: \"%d %d %f\"", tmp.Width, tmp.Height, tmp.FrameRate)) + } + case consts.MSG_LASER_SCAN: // check passed default: return fmt.Errorf("unsupported topic type: \"" + topic.Type + "\"") @@ -134,13 +145,18 @@ func LoadCfg() *Config { if _, err := os.Stat(args[1]); errors.Is(err, os.ErrNotExist) { slog.Info(args[1] + " not found, using default config") return &Config{ - Mode: "receiver", + Mode: "sender", Addr: "localhost:8080", Topics: []TopicConfig{ { NameIn: "image_raw", NameOut: "image", Type: "sensor_msgs/msg/Image", + ImgSpec: ImageSpecifications{ + Width: 1920, + Height: 1080, + FrameRate: 30, + }, }, }, } diff --git a/config/config_test.go b/config/config_test.go index 2918dff..0c009c3 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -20,11 +20,21 @@ func TestCheckfunc(t *testing.T) { NameIn: "image_raw", NameOut: "image", Type: "sensor_msgs/msg/Image", + ImgSpec: ImageSpecifications{ + Width: 640, + Height: 480, + FrameRate: 30, + }, }, { NameIn: "image_raw", NameOut: "image", Type: "sensor_msgs/msg/Image", + ImgSpec: ImageSpecifications{ + Width: 640, + Height: 480, + FrameRate: 29.97, + }, }, }, }, @@ -40,11 +50,26 @@ func TestCheckfunc(t *testing.T) { NameIn: "image_raw", NameOut: "image", Type: "sensor_msgs/msg/Image", + ImgSpec: ImageSpecifications{ + Width: 0, + Height: 480, + FrameRate: 29.97, + }, }, + }, + }, + expected: false, + }, + { + name: "valid config", + cfg: &Config{ + Mode: "receiver", + Addr: "localhost:8080", + Topics: []TopicConfig{ { NameIn: "image_raw", NameOut: "image", - Type: "sensor_msgs/msg/Image", + Type: "sensor_msgs/msg/LaserScan", }, }, }, @@ -61,6 +86,11 @@ func TestCheckfunc(t *testing.T) { NameOut: "image", Type: "sensor_msgs/msg/Image", }, + { + NameIn: "image_raw", + NameOut: "image", + Type: "sensor_msgs/msg/LaserScan", + }, }, }, expected: false, diff --git a/go.mod b/go.mod index d414487..d80b0d4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/3DRX/webrtc-ros-bridge -go 1.23.1 +go 1.23 require ( github.com/gorilla/websocket v1.5.3 diff --git a/main.go b/main.go index 2eb7b30..5278bd3 100644 --- a/main.go +++ b/main.go @@ -71,6 +71,7 @@ func sender(cfg *config.Config) { sendCandidateChan, recvCandidateChan, actions, + &(cfg.Topics[0].ImgSpec), ) go pc.Spin() select {} diff --git a/ros_mediadevices_adapter/ros_mediadevices_adapter.go b/ros_mediadevices_adapter/ros_mediadevices_adapter.go index 24e282b..7079df4 100644 --- a/ros_mediadevices_adapter/ros_mediadevices_adapter.go +++ b/ros_mediadevices_adapter/ros_mediadevices_adapter.go @@ -16,10 +16,13 @@ type rosImageAdapter struct { lastFrame *image.RGBA doneCh chan struct{} imgChan <-chan *sensor_msgs_msg.Image + imgWidth int + imgHeight int + frameRate float64 } -func Initialize(imgChan <-chan *sensor_msgs_msg.Image) { - adapter := newROSImageAdapter() +func Initialize(imgChan <-chan *sensor_msgs_msg.Image, width, height int, frameRate float64) { + adapter := newROSImageAdapter(width, height, frameRate) adapter.imgChan = imgChan driver.GetManager().Register(adapter, driver.Info{ Label: "ros_image_topic", @@ -28,8 +31,8 @@ func Initialize(imgChan <-chan *sensor_msgs_msg.Image) { }) } -func newROSImageAdapter() *rosImageAdapter { - return &rosImageAdapter{} +func newROSImageAdapter(width, height int, frameRate float64) *rosImageAdapter { + return &rosImageAdapter{imgWidth: width, imgHeight: height, frameRate: frameRate} } func (a *rosImageAdapter) getRgba() (*image.RGBA, error) { @@ -70,9 +73,10 @@ func (a *rosImageAdapter) VideoRecord(selectedProp prop.Media) (video.Reader, er func (a *rosImageAdapter) Properties() []prop.Media { supportedProp := prop.Media{ Video: prop.Video{ - Width: 640, - Height: 480, + Width: a.imgWidth, + Height: a.imgHeight, FrameFormat: frame.FormatRGBA, + FrameRate: float32(a.frameRate), }, } return []prop.Media{supportedProp} diff --git a/sender/peer_connection_channel/peer_connection_channel.go b/sender/peer_connection_channel/peer_connection_channel.go index 7355773..2fc07b4 100644 --- a/sender/peer_connection_channel/peer_connection_channel.go +++ b/sender/peer_connection_channel/peer_connection_channel.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "log/slog" - + "github.com/3DRX/webrtc-ros-bridge/config" sensor_msgs_msg "github.com/3DRX/webrtc-ros-bridge/rclgo_gen/sensor_msgs/msg" rosmediadevicesadapter "github.com/3DRX/webrtc-ros-bridge/ros_mediadevices_adapter" send_signalingchannel "github.com/3DRX/webrtc-ros-bridge/sender/signaling_channel" @@ -47,6 +47,7 @@ func InitPeerConnectionChannel( sendCandidateChan chan<- webrtc.ICECandidateInit, recvCandidateChan <-chan webrtc.ICECandidateInit, action *send_signalingchannel.Action, + imgSpec *config.ImageSpecifications, ) *PeerConnectionChannel { // parse action if action.Type != "configure" { @@ -74,8 +75,15 @@ func InitPeerConnectionChannel( // create a dispatch goroutine to split image message from other sensor messages imgChan := make(chan *sensor_msgs_msg.Image, 10) sensorChan := make(chan types.Message, 10) + var imgWidth, imgHeight int = 640, 480 + var frameRate float64 = 30.00 + if imgSpec.Width != 0 && imgSpec.Height != 0 && imgSpec.FrameRate != 0 { + imgWidth = imgSpec.Width + imgHeight = imgSpec.Height + frameRate = imgSpec.FrameRate + } - rosmediadevicesadapter.Initialize(imgChan) + rosmediadevicesadapter.Initialize(imgChan, imgWidth, imgHeight, frameRate) vp8Params, err := vpx.NewVP8Params() if err != nil { panic(err) @@ -106,8 +114,9 @@ func InitPeerConnectionChannel( mediaStream, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{ Video: func(constraint *mediadevices.MediaTrackConstraints) { - constraint.Width = prop.Int(640) - constraint.Height = prop.Int(480) + constraint.Width = prop.Int(imgWidth) + constraint.Height = prop.Int(imgHeight) + constraint.FrameRate = prop.Float(frameRate) }, Codec: codecselector, }) @@ -216,3 +225,17 @@ func unmarshalAction(rawAction interface{}, action interface{}) error { } return nil } + +func checkImgSpec(cfg *config.Config) [2]int { + tmp := cfg.Topics[0].ImgSpec + + width, height := 640, 480 + if tmp.Width != 0 && tmp.Height!= 0 { + width = tmp.Width + height = tmp.Height + } + return [2]int{ + width, + height, + } +}