Unverified Commit 6361b68e authored by Leeward Bound's avatar Leeward Bound 💼

feat(success): 0.2 build

parent 697b09a3
Pipeline #168 passed with stage
in 1 minute and 25 seconds
......@@ -28,7 +28,11 @@ build: go_init protos
run:
echo "Running local demo: http://localhost:7070"
go run ./cmd/noir/main.go -c ./config.toml
go run ./cmd/noir/main.go -c ./config.toml -d :7070 -j :7000
run_second:
echo "Running second demo: http://localhost:7071"
go run ./cmd/noir/main.go -c ./config.toml -d :7071 -j :7001
docker: protos
docker build . -t ${CI_REGISTRY_IMAGE}:latest
......@@ -43,7 +47,7 @@ tag:
|| echo "usage: make tag TAG=..."
test_redis:
docker run -d --rm -p 6379:6379 --name noir-redis sameersbn/redis
docker rm -f noir-redis ; docker run -d --rm -p 6379:6379 --name noir-redis sameersbn/redis
demo:
echo "Starting local demonstration at http://localhost:7070" && docker run --net host ghcr.io/net-prophet/noir:latest -d :7070 -j :7000
......
......@@ -14,12 +14,10 @@ import (
"github.com/spf13/viper"
log "github.com/pion/ion-log"
sfu "github.com/pion/ion-sfu/pkg/sfu"
"github.com/pion/webrtc/v3"
)
var (
conf = sfu.Config{}
conf = noir.Config{}
ctx = context.Background()
file string
redisURL string
......@@ -47,26 +45,6 @@ func showHelp() {
fmt.Println(" -h (show help info)")
}
type Command struct {
Method string `json:"method"`
}
// Join message sent when initializing a peer connection
type Join struct {
Sid string `json:"sid"`
Offer webrtc.SessionDescription `json:"offer"`
}
// Negotiation message sent when renegotiating the peer connection
type Negotiation struct {
Desc webrtc.SessionDescription `json:"desc"`
}
// Trickle message sent when renegotiating the peer connection
type Trickle struct {
Candidate webrtc.ICECandidateInit `json:"candidate"`
}
func load() bool {
_, err := os.Stat(file)
if err != nil {
......@@ -87,12 +65,12 @@ func load() bool {
return false
}
if len(conf.WebRTC.ICEPortRange) > 2 {
if len(conf.Ion.WebRTC.ICEPortRange) > 2 {
fmt.Printf("config file %s loaded failed. range port must be [min,max]\n", file)
return false
}
if len(conf.WebRTC.ICEPortRange) != 0 && conf.WebRTC.ICEPortRange[1]-conf.WebRTC.ICEPortRange[0] < portRangeLimit {
if len(conf.Ion.WebRTC.ICEPortRange) != 0 && conf.Ion.WebRTC.ICEPortRange[1]-conf.Ion.WebRTC.ICEPortRange[0] < portRangeLimit {
fmt.Printf("config file %s loaded failed. range port must be [min, max] and max - min >= %d\n", file, portRangeLimit)
return false
}
......@@ -104,10 +82,10 @@ func load() bool {
func parse() bool {
flag.StringVar(&file, "c", "/configs/sfu.toml", "config file")
flag.StringVar(&redisURL, "u", "localhost:6379", "redisURL to use")
flag.StringVar(&demoAddr, "d", ":7070", "http addr to listen for demo")
flag.StringVar(&publicJrpcAddr, "j", ":7000", "jsonrpc addr for public")
flag.StringVar(&adminJrpcAddr, "a", ":7001", "jsonrpc addr for admin")
flag.StringVar(&grpcAddr, "g", ":50051", "grpc addr for admin")
flag.StringVar(&demoAddr, "d", "", "http addr to listen for demo")
flag.StringVar(&publicJrpcAddr, "j", "", "jsonrpc addr for public")
flag.StringVar(&adminJrpcAddr, "a", "", "jsonrpc addr for admin")
flag.StringVar(&grpcAddr, "g", "", "grpc addr for admin")
flag.StringVar(&cert, "cert", "", "public jsonrpc https cert file")
flag.StringVar(&key, "key", "", "public jsonrpc https key file")
help := flag.Bool("h", false, "help info")
......@@ -133,7 +111,9 @@ func main() {
fixByFunc := []string{"Handle"}
log.Init(conf.Log.Level, fixByFile, fixByFunc)
log.Infof("--- noiR SFU ---")
id := noir.RandomString(8)
log.Infof("--- noiR SFU %s ---", id)
rdb := redis.NewClient(&redis.Options{
Addr: redisURL,
......@@ -149,7 +129,7 @@ func main() {
}
sfu := noir.NewNoirSFU(conf)
mgr := noir.NewManager(&sfu, rdb, noir.RandomString(8))
mgr := noir.NewManager(&sfu, rdb, id)
go mgr.Noir()
defer mgr.Cleanup()
......
[sfu]
[ion.sfu]
# Ballast size in MiB, will allocate memory to reduce the GC trigger upto 2x the
# size of ballast. Be aware that the ballast should be less than the half of memory
# available.
ballast = 0
ballast = 64
[router]
[ion.router]
# Limit the remb bandwidth in kbps
# zero means no limits
maxbandwidth = 1500
# max buffer time by ms for video tracks
maxbuffertime = 1000
[router.simulcast]
[ion.router.simulcast]
# Prefer best quality initially
bestqualityfirst = true
# EXPERIMENTAL enable temporal layer change is currently an experimental feature,
# enable only for testing.
enabletemporallayer = false
[webrtc]
[ion.webrtc]
# Range of ports that ion accepts WebRTC traffic on
# Format: [min, max] and max - min >= 100
# portrange = [50000, 60000]
......@@ -36,7 +36,7 @@ enabletemporallayer = false
# "unified-plan-with-fallback"
sdpsemantics = "unified-plan"
[webrtc.candidates]
[ion.webrtc.candidates]
# In case you're deploying ion-sfu on a server which is configured with
# a 1:1 NAT (e.g., Amazon EC2), you might want to also specify the public
# address of the machine using the setting below. This will result in
......@@ -50,4 +50,4 @@ sdpsemantics = "unified-plan"
# icelite = true
[log]
level = "trace"
level = "debug"
......@@ -63,20 +63,23 @@
</div>
</div>
<div class="row">
<div class="col-12 pt-4">Media</div>
</div>
<div class="row">
<div class="col-6 pt-2">
<span
style="position: absolute; margin-left: 5px; margin-top: 5px;"
style="position: absolute; margin-left: 5px; margin-top: 5px"
class="badge badge-primary"
>Local</span
>
<video
id="local-video"
style="background-color: black;"
style="background-color: black"
width="320"
height="240"
></video>
<div class="controls" style="display: none;">
<div class="controls" style="display: none">
<div class="row pt-3">
<div class="col-3">
<strong>Video</strong>
......@@ -135,31 +138,31 @@
</div>
<div class="col-6 pt-2">
<span
style="position: absolute; margin-left: 5px; margin-top: 5px;"
style="position: absolute; margin-left: 5px; margin-top: 5px"
class="badge badge-primary"
>Remote</span
>
<span
id="size-tag"
style="position: absolute; margin-left: 5px; top: 225px;"
style="position: absolute; margin-left: 5px; top: 225px"
class="badge badge-primary"
></span>
<span
id="br-tag"
style="position: absolute; left: 270px; top: 225px;"
style="position: absolute; left: 270px; top: 225px"
class="badge badge-primary"
></span>
<video
id="remote-video"
style="background-color: black;"
style="background-color: black"
width="320"
height="240"
></video>
<div class="controls" style="display: none;">
<div class="controls" style="display: none">
<div class="row pt-3">
<div class="col-3">
<strong>Video</strong>
<div id="simulcast-controls" style="display: none;">
<div id="simulcast-controls" style="display: none">
<div class="radio">
<label
><input
......@@ -206,7 +209,7 @@
</div>
</div>
<div id="simple-controls" style="display: none;">
<div id="simple-controls" style="display: none">
<div class="radio">
<label
><input
......@@ -263,11 +266,50 @@
<pre
id="api"
class="d-block border"
style="background-color: #f8f9fa; height: 117px;"
style="
background-color: #f8f9fa;
height: 117px;
width: 320px;
margin: 5px 0;
"
></pre>
</div>
</div>
</div>
<div class="row">
<div class="col-12 pt-4">Data</div>
</div>
<div class="row">
<div class="col-6 pt-2">
<textarea
id="local-data"
class="d-block border"
style="
background-color: #f8f9fa;
height: 117px;
width: 320px;
margin: 5px 0;
padding: 5px;
"
placeholder="Send a message"
></textarea>
<button type="button" class="btn btn-primary" onclick="send()">
send
</button>
</div>
<div class="col-6 pt-2">
<pre
id="remote-data"
class="d-block border"
style="
background-color: #f8f9fa;
height: 117px;
width: 320px;
margin: 5px 0;
"
></pre>
</div>
</div>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
......@@ -286,13 +328,16 @@
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/ion-sdk-js@1.4.1/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.0/dist/ion-sdk.min.js"></script>
<script>
const localVideo = document.getElementById("local-video");
const remoteVideo = document.getElementById("remote-video");
const localData = document.getElementById("local-data");
const remoteData = document.getElementById("remote-data");
const sizeTag = document.getElementById("size-tag");
const brTag = document.getElementById("br-tag");
let simulcast = false;
let localDataChannel;
remoteVideo.addEventListener("loadedmetadata", function () {
sizeTag.innerHTML = `${remoteVideo.videoWidth}x${remoteVideo.videoHeight}`;
......@@ -325,7 +370,7 @@
})
.then((media) => {
localStream = media;
localVideo.srcObject = media.stream;
localVideo.srcObject = media;
localVideo.autoplay = true;
localVideo.controls = true;
localVideo.muted = true;
......@@ -333,6 +378,13 @@
clientLocal.publish(media);
})
.catch(console.error);
localDataChannel = clientLocal.createDataChannel("data");
};
const send = () => {
if (localDataChannel.readyState === "open") {
localDataChannel.send(localData.value);
}
};
let remoteStream;
......@@ -356,6 +408,12 @@
}
};
clientRemote.ondatachannel = ({ channel }) => {
channel.onmessage = ({ data }) => {
remoteData.innerHTML = data;
};
};
const api = {
streamId: "",
video: "high",
......
......@@ -134,7 +134,7 @@
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/ion-sdk-js@1.1.1/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.0/dist/ion-sdk.min.js"></script>
<script>
const localVideo = document.getElementById("local-video");
const remotesDiv = document.getElementById("remotes");
......@@ -158,18 +158,18 @@
let localStream;
const start = () => {
clientLocal.getUserMedia({
IonSDK.LocalStream.getUserMedia({
resolution: "vga",
audio: true,
})
.then((media) => {
localStream = media;
localVideo.srcObject = media.stream;
localVideo.srcObject = media;
localVideo.autoplay = true;
localVideo.controls = true;
localVideo.muted = true;
joinBtns.style.display = "none";
media.publish(media);
clientLocal.publish(media);
})
.catch(console.error);
};
......
......@@ -7,13 +7,10 @@ require (
github.com/golang/protobuf v1.4.3
github.com/gorilla/websocket v1.4.2
github.com/pion/ion-log v1.0.0
github.com/pion/ion-sfu v1.6.2
github.com/pion/ion-sfu v1.6.4
github.com/pion/webrtc/v3 v3.0.0-beta.15.0.20201209023348-63401a8837fb
github.com/smartystreets/gunit v1.4.2 // indirect
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1 // indirect
golang.org/x/tools v0.0.0-20200815165600-90abf76919f3 // indirect
google.golang.org/grpc v1.33.2
google.golang.org/protobuf v1.25.0
)
This diff is collapsed.
package noir
import (
log "github.com/pion/ion-log"
"github.com/pion/ion-sfu/pkg/sfu"
)
type Config struct {
Ion sfu.Config
Log log.Config `mapstructure:"log"`
}
......@@ -65,17 +65,12 @@ func (m *Manager) CloseRoom(roomID string) {
func (m *Manager) CloseClient(clientID string) {
client := m.clients[clientID]
roomID := m.redis.Get(pb.KeyPeerToRoom(clientID)).Val()
room := m.rooms[roomID]
session := room.Session()
session.RemovePeer(clientID)
defer m.redis.Del(pb.KeyPeerToRoom(clientID))
client.Close()
m.mu.Lock()
delete(m.clients, clientID)
m.mu.Unlock()
m.redis.Del(pb.KeyPeerToRoom(clientID))
client.Close()
}
func (m *Manager) CreateClient(signal *pb.SignalRequest) *sfu.Peer {
......@@ -94,6 +89,10 @@ func (m *Manager) ClientPing(pid string, sid string) error {
return m.redis.Set(pb.KeyPeerToRoom(pid), sid, PeerPingFrequency).Err()
}
func(m *Manager) GetQueue(topic string, maxAge time.Duration) Queue {
return NewRedisQueue(m.redis, topic, maxAge)
}
func (m *Manager) WorkerForRoom(roomID string) (string, error) {
workerID, err := m.redis.Get(pb.KeyRoomToWorker(roomID)).Result()
if err != nil {
......@@ -321,7 +320,7 @@ func (m *Manager) LoadStatus(key string) (*pb.NoirStatus, error) {
// Local Memory Manager
func NewTestManager(driver string, config sfu.Config) Manager {
func NewTestManager(driver string, config Config) Manager {
rdb := redis.NewClient(&redis.Options{
Addr: driver,
Password: "",
......@@ -357,13 +356,13 @@ func (m *Manager) Noir() {
case <-update.C:
m.UpdateAvailableWorkers()
case <-info.C:
log.Infof("%s: noirs=%d users=%d rooms=%d", m.worker.ID(), len(m.statuses), len(m.clients), m.RoomCount())
log.Debugf("%s: noirs=%d users=%d rooms=%d", m.worker.ID(), len(m.statuses), len(m.clients), m.RoomCount())
case <-quit:
log.Infof("quit requested, cleaning up...")
log.Warnf("quit requested, cleaning up...")
info.Stop()
update.Stop()
m.Cleanup()
log.Infof("cleaned up ok!")
log.Debugf("cleaned up ok!")
os.Exit(1)
return
}
......
package noir
import "github.com/pion/webrtc/v3"
// Join message sent when initializing a peer connection
type Join struct {
Sid string `json:"sid"`
Offer webrtc.SessionDescription `json:"offer"`
Pid string `json:"pid"`
}
// Negotiation message sent when renegotiating the peer connection
type Negotiation struct {
Desc webrtc.SessionDescription `json:"desc"`
}
// Trickle message sent when renegotiating the peer connection
type Trickle struct {
Candidate webrtc.ICECandidateInit `json:"candidate"`
Target int `json:"target"`
}
type Play struct {
Sid string `json:"roomID"`
Pid string `json:"pid"`
Filename string `json:"filename"`
Repeat bool `json:"repeat"`
}
type RPCCall struct {
ID string `json:"clientID"`
Method string `json:"method"`
Params interface{} `json:"params"`
}
type RPCJoin struct {
RPCCall
Params Join `json:"params"`
}
type RPCPlay struct {
RPCCall
Params Play `json:"params"`
}
type ResultOrNotify struct {
ID string `json:"clientID"`
ResultType string `json:"type"`
Method string `json:"method"`
Params interface{} `json:"params"`
Result interface{} `json:"result"`
}
type Result struct {
ID string `json:"clientID"`
Result interface{} `json:"result"`
JSONRPC string `json:"jsonrpc"`
}
type Notify struct {
Method string `json:"method"`
Params interface{} `json:"params"`
JSONRPC string `json:"jsonrpc"`
}
......@@ -29,13 +29,13 @@ type NoirSFU interface {
}
// NewNoirSFU will create an object that represent the NoirSFU interface
func NewNoirSFU(c sfu.Config) NoirSFU {
func NewNoirSFU(c Config) NoirSFU {
rand.Seed(time.Now().UnixNano())
id := RandomString(8)
// Init ballast
ballast := make([]byte, c.SFU.Ballast*1024*1024)
ballast := make([]byte, c.Ion.SFU.Ballast*1024*1024)
w := sfu.NewWebRTCTransportConfig(c)
w := sfu.NewWebRTCTransportConfig(c.Ion)
runtime.KeepAlive(ballast)
......
......@@ -4,19 +4,27 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/golang/protobuf/proto"
noir "github.com/net-prophet/noir/pkg/noir"
"github.com/net-prophet/noir/pkg/proto"
strings "strings"
pb "github.com/net-prophet/noir/pkg/proto"
log "github.com/pion/ion-log"
"github.com/pion/webrtc/v3"
"github.com/sourcegraph/jsonrpc2"
strings "strings"
"time"
)
type clientJSONRPCBridge struct {
pid string
pid string
manager *noir.Manager
}
// Trickle message sent when renegotiating the peer connection
type Trickle struct {
Target int `json:"target"`
Candidate webrtc.ICECandidateInit `json:"candidate"`
}
func NewClientJSONRPCBridge(pid string, manager *noir.Manager) *clientJSONRPCBridge {
return &clientJSONRPCBridge{pid: pid, manager: manager}
}
......@@ -33,11 +41,18 @@ func (s *clientJSONRPCBridge) Handle(ctx context.Context, conn *jsonrpc2.Conn, r
requestId := strings.Replace(req.ID.String(), "\"", "", -1)
router := (*s.manager).GetRouter()
routerQueue := (*router).GetQueue()
toPeerQueue := s.manager.GetQueue(pb.KeyTopicToPeer(s.pid), noir.PeerPingFrequency)
log.Debugf("from jsonrpc %s %s", s.pid, req.Method)
switch req.Method {
case "join":
var join noir.Join
var join pb.Join
err := json.Unmarshal(*req.Params, &join)
if err != nil {
......@@ -46,64 +61,102 @@ func (s *clientJSONRPCBridge) Handle(ctx context.Context, conn *jsonrpc2.Conn, r
break
}
command := &proto.NoirRequest{
Command: &proto.NoirRequest_Signal{
Signal: &proto.SignalRequest{
command := &pb.NoirRequest{
Command: &pb.NoirRequest_Signal{
Signal: &pb.SignalRequest{
// SignalRequest.id should be called pid but we are ion-sfu compatible
Id: s.pid,
Payload: &proto.SignalRequest_Join{&proto.JoinRequest{
Id: s.pid,
RequestId: requestId,
Payload: &pb.SignalRequest_Join{&pb.JoinRequest{
Sid: join.Sid,
Description: []byte(join.Offer.SDP),
},
},
},
}}
router := (*s.manager).GetRouter()
queue := (*router).GetQueue()
noir.EnqueueRequest(*queue, command)
noir.EnqueueRequest(*routerQueue, command)
go s.Listen(ctx, conn, req)
case "offer":
var negotiation noir.Negotiation
var negotiation pb.Negotiation
err := json.Unmarshal(*req.Params, &negotiation)
marshaled, _ := json.Marshal(negotiation)
if err != nil {
log.Errorf("connect: error parsing offer: %v", err)
replyError(err)
break
}
json.Marshal(noir.RPCCall{requestId, "offer", negotiation})
//r.LPush("peer-send/"+s.PeerID(), message)
command := &pb.NoirRequest{
Command: &pb.NoirRequest_Signal{
Signal: &pb.SignalRequest{
// SignalRequest.id should be called pid but we are ion-sfu compatible
Id: s.pid,
RequestId: requestId,
Payload: &pb.SignalRequest_Description{
Description: marshaled,
},
},
}}
noir.EnqueueRequest(toPeerQueue, command)
case "answer":
var negotiation noir.Negotiation
var negotiation pb.Negotiation
err := json.Unmarshal(*req.Params, &negotiation)
marshaled, _ := json.Marshal(negotiation)
if err != nil {
log.Errorf("connect: error parsing offer: %v", err)
replyError(err)
break
}