From 926bfd8267b6d07832eca00c38aeeee5b309dd2b Mon Sep 17 00:00:00 2001 From: bupt <1872456455@qq.com> Date: Thu, 12 Dec 2024 21:59:01 +0800 Subject: [PATCH 1/2] fps --- config/config.go | 26 ++++++-- config/config_test.go | 32 +++++++++- go.mod | 2 +- main.go | 1 + .../ros_mediadevices_adapter.go | 16 +++-- .../peer_connection_channel.go | 31 +++++++-- .../peer_connection_channel_test.go | 64 +++++++++++++++++++ 7 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 sender/peer_connection_channel/peer_connection_channel_test.go 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, + } +} diff --git a/sender/peer_connection_channel/peer_connection_channel_test.go b/sender/peer_connection_channel/peer_connection_channel_test.go new file mode 100644 index 0000000..fef99c3 --- /dev/null +++ b/sender/peer_connection_channel/peer_connection_channel_test.go @@ -0,0 +1,64 @@ +package peerconnectionchannel + +import ( + "testing" + "github.com/3DRX/webrtc-ros-bridge/config" +) + +func TestImgSpecfunc(t *testing.T) { + tests := []struct { + name string + cfg *config.Config + expected [2]int + }{ + { + name: "valid config", + cfg: &config.Config{ + Mode: "receiver", + Addr: "10.3.9.3:8080", + Topics: []config.TopicConfig{ + { + NameIn: "image_raw", + NameOut: "image", + Type: "sensor_msgs/msg/Image", + ImgSpec: config.ImageSpecifications{ + Width: 1920, + Height: 1080, + FrameRate: 30, + }, + }, + }, + }, + expected: [2]int{1920, 1080}, + }, + { + name: "valid config with hostname", + cfg: &config.Config{ + Mode: "receiver", + Addr: "localhost:8080", + Topics: []config.TopicConfig{ + { + NameIn: "image_raw", + NameOut: "image", + Type: "sensor_msgs/msg/Image", + ImgSpec: config.ImageSpecifications{ + Width: 0, + Height: 480, + FrameRate: 29.97, + }, + }, + }, + }, + expected: [2]int{640, 480}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := checkImgSpec(tt.cfg) + if res != tt.expected { + t.Errorf("TestCheckfunc failed: expected status %v, got %v", tt.expected, res) + } + }) + } +} From ae46978f5754f3f25f73f24d7ceab6924dfaf66b Mon Sep 17 00:00:00 2001 From: bupt <1872456455@qq.com> Date: Thu, 12 Dec 2024 22:07:31 +0800 Subject: [PATCH 2/2] rm useless test file --- .../peer_connection_channel_test.go | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 sender/peer_connection_channel/peer_connection_channel_test.go diff --git a/sender/peer_connection_channel/peer_connection_channel_test.go b/sender/peer_connection_channel/peer_connection_channel_test.go deleted file mode 100644 index fef99c3..0000000 --- a/sender/peer_connection_channel/peer_connection_channel_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package peerconnectionchannel - -import ( - "testing" - "github.com/3DRX/webrtc-ros-bridge/config" -) - -func TestImgSpecfunc(t *testing.T) { - tests := []struct { - name string - cfg *config.Config - expected [2]int - }{ - { - name: "valid config", - cfg: &config.Config{ - Mode: "receiver", - Addr: "10.3.9.3:8080", - Topics: []config.TopicConfig{ - { - NameIn: "image_raw", - NameOut: "image", - Type: "sensor_msgs/msg/Image", - ImgSpec: config.ImageSpecifications{ - Width: 1920, - Height: 1080, - FrameRate: 30, - }, - }, - }, - }, - expected: [2]int{1920, 1080}, - }, - { - name: "valid config with hostname", - cfg: &config.Config{ - Mode: "receiver", - Addr: "localhost:8080", - Topics: []config.TopicConfig{ - { - NameIn: "image_raw", - NameOut: "image", - Type: "sensor_msgs/msg/Image", - ImgSpec: config.ImageSpecifications{ - Width: 0, - Height: 480, - FrameRate: 29.97, - }, - }, - }, - }, - expected: [2]int{640, 480}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res := checkImgSpec(tt.cfg) - if res != tt.expected { - t.Errorf("TestCheckfunc failed: expected status %v, got %v", tt.expected, res) - } - }) - } -}