diff --git a/.gitignore b/.gitignore index 66fd13c90..f8b989a5d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +livekit-server # Dependency directories (remove the comment below to include it) # vendor/ +.idea/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..343032129 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +server: + go build -i -o livekit-server diff --git a/main.go b/main.go new file mode 100644 index 000000000..6e8781524 --- /dev/null +++ b/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + "github.com/livekit/livekit-server/pkg/node" +) + +func main() { + n, err := node.NewLocalNode() + if err != nil { + panic(err) + } + + if err := n.DiscoverNetworkInfo(); err != nil { + panic(err) + } + + fmt.Println("my IP", n.IP) +} diff --git a/pkg/data/redis.go b/pkg/data/redis.go new file mode 100644 index 000000000..ba213e4d8 --- /dev/null +++ b/pkg/data/redis.go @@ -0,0 +1,2 @@ +package data + diff --git a/pkg/node/node.go b/pkg/node/node.go new file mode 100644 index 000000000..c07ce53be --- /dev/null +++ b/pkg/node/node.go @@ -0,0 +1,66 @@ +package node + +import ( + "github.com/google/uuid" + "github.com/pion/stun" +) + +const ( + googleStunServer = "stun.l.google.com:19302" +) + +type Node struct { + ID string + IP string + Stats NodeStats +} + +type NodeStats struct { + NumRooms int32 + NumClients int32 + NumVideoChannels int32 + NumAudioChannels int32 + BytesPerMin int64 +} + +func NewLocalNode() (*Node, error) { + id, err := uuid.NewRandom() + if err != nil { + return nil, err + } + return &Node{ + ID: id.String(), + }, nil +} + +func (n *Node) DiscoverNetworkInfo() error { + c, err := stun.Dial("udp", googleStunServer) + if err != nil { + return err + } + + message, err := stun.Build(stun.TransactionID, stun.BindingRequest) + if err != nil { + return err + } + + var stunErr error + err = c.Do(message, func(res stun.Event) { + if res.Error != nil { + stunErr = res.Error + return + } + + var xorAddr stun.XORMappedAddress + if err := xorAddr.GetFrom(res.Message); err != nil { + stunErr = err + return + } + n.IP = xorAddr.IP.String() + }) + + if stunErr != nil { + err = stunErr + } + return err +} \ No newline at end of file diff --git a/proto/model.proto b/proto/model.proto new file mode 100644 index 000000000..7f8c40e50 --- /dev/null +++ b/proto/model.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package livekit; + +// internal types, declaring in proto to get serialization for free + +message Node { + string id = 1; + string ip = 2; + int32 port = 3; + NodeStats stats = 4; +} + +message NodeStats { + int32 num_rooms = 1; + int32 num_clients = 2; +} \ No newline at end of file diff --git a/proto/service.proto b/proto/service.proto new file mode 100644 index 000000000..6bbf85d16 --- /dev/null +++ b/proto/service.proto @@ -0,0 +1,68 @@ +syntax = "proto3"; + +package livekit; + +// Room service that can be performed on any node +service RoomService { + // TODO: how do we secure room service? + // should be accessible to only internal servers, not external + rpc CreateRoom(CreateRoomRequest) returns (CreateRoomResponse); + rpc JoinRoom(JoinRoomRequest) returns (JoinRoomResponse); + rpc DeleteRoom(DeleteRoomRequest) returns (DeleteRoomResponse); +} + +message CreateRoomRequest { + +} + +message CreateRoomResponse { + +} + +message JoinRoomRequest { + string room = 1; + string client_id = 2; +} + +message JoinRoomResponse { + string node_ip = 1; + string node_port = 2; +} + +message DeleteRoomRequest { + string room = 1; +} + +message DeleteRoomResponse { +} + +// RTC methods performed on target node +service RTCService { + rpc Offer(SessionDescription) returns (SessionDescription); + + // push channel to allow server to push commands to client + rpc Signal(SignalRequest) returns (stream SignalResponse); + rpc Trickle(TrickleRequest) returns (TrickleResponse); +} + +message SignalRequest { +} + +message SignalResponse { + oneof payload { + SessionDescription desc = 1; + TrickleRequest trickle = 2; + } +} + +message TrickleRequest { + string candidate = 1; +} + +message TrickleResponse { +} + +message SessionDescription { + string type = 1; // "answer" | "offer" | "pranswer" | "rollback" + bytes sdp = 2; +}