Unverified Commit 6a0c2937 authored by Leeward Bound's avatar Leeward Bound 💼

feat: gallerytest and adding track info

parent 027f7043
Pipeline #203 passed with stage
in 1 minute and 38 seconds
# Echo Test
Echo test provides a simple demonstration of sfu functionality.
## Instructions
### Run docker-compose
```
docker-compose up
```
### Navigate to demo
```
http://localhost:8000/
```
Play around! 🚀
version: "3"
services:
pubsub:
image: nginx
volumes:
- ./:/usr/share/nginx/html
ports:
- 8000:80
sfu:
image: pionwebrtc/ion-sfu:latest-jsonrpc
ports:
- "5000-5200:5000-5200/udp"
- 7000:7000
......@@ -69,17 +69,17 @@
<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>
......@@ -138,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
......@@ -170,6 +170,7 @@
onclick="controlRemoteVideo(this)"
value="high"
name="optremotevideos"
checked
/>
High</label
>
......@@ -209,7 +210,7 @@
</div>
</div>
<div id="simple-controls" style="display: none">
<div id="simple-controls" style="display: none;">
<div class="radio">
<label
><input
......@@ -328,7 +329,8 @@
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.0/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/json-rpc.min.js"></script>
<script>
const localVideo = document.getElementById("local-video");
const remoteVideo = document.getElementById("remote-video");
......@@ -350,15 +352,20 @@
/* eslint-env browser */
const joinBtns = document.getElementById("start-btns");
const signalLocal = new IonSDK.IonSFUJSONRPCSignal(
const signalLocal = new Signal.IonSFUJSONRPCSignal(
"ws://localhost:7000/ws"
);
const signalRemote = new IonSDK.IonSFUJSONRPCSignal(
const signalRemote = new Signal.IonSFUJSONRPCSignal(
"ws://localhost:7000/ws"
);
const clientLocal = new IonSDK.Client("test session", signalLocal);
const clientRemote = new IonSDK.Client("test session", signalRemote);
const clientLocal = new IonSDK.Client(signalLocal);
const clientRemote = new IonSDK.Client(signalRemote);
const session = window.location.hash.toString().replace("#", "") || "test session"
signalLocal.onopen = () => clientLocal.join(session);
signalRemote.onopen = () => clientRemote.join(session);
let localStream;
const start = (sc) => {
......
# Gallery Test
Gallery test provides a demonstration large session negotiation.
## Instructions
### Run docker-compose
```
docker-compose up
```
### Navigate to demo
```
http://localhost:8000/
```
Build cool stuff! 🚀
version: "3"
services:
pubsub:
image: nginx
volumes:
- ./:/usr/share/nginx/html
ports:
- 8000:80
sfu:
image: pionwebrtc/ion-sfu:latest-jsonrpc
ports:
- "5000-5200:5000-5200/udp"
- 7000:7000
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<!-- Bootstrap CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
crossorigin="anonymous"
/>
<style>
#remotes video {
width: 160px;
}
</style>
<title>Pion ion-sfu | Gallery Test</title>
</head>
<body>
<nav class="navbar navbar-light bg-light border-bottom">
<h3>Pion</h3>
</nav>
<div class="container pt-4">
<div class="row">
<div class="col-12" id="join-btn">
<button type="button" class="btn btn-primary" onclick="join()">
Join
</button>
</div>
<div class="col-12" style="display: none" id="publish-btn">
<button type="button" class="btn btn-primary" onclick="publish()">
Publish
</button>
<button type="button" class="btn btn-primary" onclick="unpublish()">
Unpublish
</button>
</div>
</div>
<div class="row">
<div id="remotes" class="col-12 pt-2">
<span
style="position: absolute; margin-left: 5px; margin-top: 5px"
class="badge badge-primary"
>Remotes</span
>
</div>
</div>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/json-rpc.min.js"></script>
<script>
const localVideo = document.getElementById("local-video");
const remotesDiv = document.getElementById("remotes");
/* eslint-env browser */
const joinBtn = document.getElementById("join-btn");
const publishBtn = document.getElementById("publish-btn");
class Peer {
constructor() {
this.signal = new Signal.IonSFUJSONRPCSignal(
"ws://localhost:7000/ws"
);
const videosDiv = document.createElement("div");
videosDiv.style.display = "flex";
remotesDiv.appendChild(videosDiv);
this.client = new IonSDK.Client(this.signal);
const session = window.location.hash.toString().replace("#", "") || "test session"
this.signal.onopen = () => this.client.join(session);
this.client.ontrack = (track, stream) => {
if (track.kind === "video") {
const remoteVideo = document.createElement("video");
remoteVideo.srcObject = stream;
remoteVideo.autoplay = true;
remoteVideo.muted = true;
videosDiv.appendChild(remoteVideo);
track.onremovetrack = () => videosDiv.removeChild(removeVideo);
}
};
}
capture() {
IonSDK.LocalStream.getUserMedia({
resolution: "hd",
audio: true,
})
.then((media) => {
this.media = media;
})
.catch(console.error);
}
publish() {
this.client.publish(this.media);
}
unpublish() {
this.media.unpublish();
}
}
const peers = [];
const join = () => {
joinBtn.style.display = "none";
publishBtn.style.display = "block";
for (var i = 0; i < 5; i++) {
const peer = new Peer();
peer.capture();
peers.push(peer);
}
};
const publish = () => peers.forEach((peer) => peer.publish());
const unpublish = () => peers.forEach((peer) => peer.unpublish());
</script>
</body>
</html>
......@@ -26,5 +26,6 @@
<li><a href="/storybook">storybook</a></li>
<li><a href="/echotest">echotest</a></li>
<li><a href="/pubsubtest">pubsubtest</a></li>
<li><a href="/gallerytest">gallerytest</a></li>
</body>
</html>
# Pub Sub Test
Pub Sub test provides a simple demonstration of multi client sfu session.
## Instructions
### Run docker-compose
```
docker-compose up
```
### Navigate to demo
Open the pub sub page in multiple tabs/browsers to simulate multiple clients.
```
http://localhost:8000/
```
Have fun! 🚀
version: "3"
services:
pubsub:
image: nginx
volumes:
- ./:/usr/share/nginx/html
ports:
- 8000:80
sfu:
image: pionwebrtc/ion-sfu:latest-jsonrpc
ports:
- "5000-5200:5000-5200/udp"
- 7000:7000
......@@ -41,17 +41,17 @@
<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>
......@@ -110,7 +110,7 @@
</div>
<div id="remotes" 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"
>Remotes</span
>
......@@ -134,7 +134,8 @@
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.0/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/ion-sdk.min.js"></script>
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/json-rpc.min.js"></script>
<script>
const localVideo = document.getElementById("local-video");
const remotesDiv = document.getElementById("remotes");
......@@ -150,11 +151,15 @@
],
};
const signalLocal = new IonSDK.IonSFUJSONRPCSignal(
const signalLocal = new Signal.IonSFUJSONRPCSignal(
"ws://localhost:7000/ws"
);
const clientLocal = new IonSDK.Client("test session", signalLocal);
const clientLocal = new IonSDK.Client(signalLocal, config);
const session = window.location.hash.toString().replace("#", "") || "test session"
signalLocal.onopen = () => clientLocal.join(session);
let localStream;
const start = () => {
......@@ -184,7 +189,7 @@
remoteVideo.muted = true;
remotesDiv.appendChild(remoteVideo);
track.onremovetrack = () => remotesDiv.removeChild(removeVideo);
track.onremovetrack = () => remotesDiv.removeChild(remoteVideo);
};
}
};
......
package main
import (
"github.com/pion/rtp/codecs"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media/samplebuilder"
"os"
)
import (
"time"
"github.com/at-wat/ebml-go/webm"
)
var (
audioWriter, videoWriter webm.BlockWriteCloser
audioBuilder, videoBuilder *samplebuilder.SampleBuilder
audioTimestamp, videoTimestamp time.Duration
streamKey string
)
func main() {
if len(os.Args) != 2 {
panic("example requires stream-key to be passed as an argument")
}
streamKey = os.Args[1]
// Everything below is the pion-WebRTC API! Thanks for using it ❤️.
// Prepare the configuration
config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
// Create a MediaEngine object to configure the supported codec
m := &webrtc.MediaEngine{}
// Setup the codecs you want to use.
// Only support VP8 and OPUS, this makes our WebM muxer code simpler
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
PayloadType: 96,
}, webrtc.RTPCodecTypeVideo); err != nil {
panic(err)
}
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "audio/opus", ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
PayloadType: 111,
}, webrtc.RTPCodecTypeAudio); err != nil {
panic(err)
}
audioBuilder = samplebuilder.New(10, &codecs.OpusPacket{}, 48000)
videoBuilder = samplebuilder.New(10, &codecs.VP8Packet{}, 90000)
}
\ No newline at end of file
......@@ -3,11 +3,15 @@ module github.com/net-prophet/noir
go 1.13
require (
github.com/at-wat/ebml-go v0.11.0
github.com/go-redis/redis v6.15.9+incompatible
github.com/golang/protobuf v1.4.3
github.com/gorilla/websocket v1.4.2
github.com/pion/example-webrtc-applications v1.0.0
github.com/pion/ion-log v1.0.0
github.com/pion/ion-sfu v1.6.4
github.com/pion/rtcp v1.2.6
github.com/pion/rtp v1.6.1
github.com/pion/sdp/v3 v3.0.3
github.com/pion/webrtc/v3 v3.0.0-beta.15.0.20201209023348-63401a8837fb
github.com/sourcegraph/jsonrpc2 v0.0.0-20200429184054-15c2290dcb37
......
......@@ -31,6 +31,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/at-wat/ebml-go v0.11.0 h1:E6V+h/XWDhAdbphMIYIa3B61kaeU4lnNfKLSy0wLP4o=
github.com/at-wat/ebml-go v0.11.0/go.mod h1:w1cJs7zmGsb5nnSvhWGKLCxvfu4FVx5ERvYDIalj1ww=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
......@@ -278,6 +280,8 @@ github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXm
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
github.com/pion/example-webrtc-applications v1.0.0 h1:2Md50nmbYqyZa/7rdQs+o/quL3vJtQQqnvPnve1WGmo=
github.com/pion/example-webrtc-applications v1.0.0/go.mod h1:bF8s97iYIF475azjb/4yr4X6F2iZsaJaQWNqPH21QJA=
github.com/pion/ice/v2 v2.0.13 h1:lVe7g86tQ0vKdH430hQR/t7zV1oeXbK75130TUArrnw=
github.com/pion/ice/v2 v2.0.13/go.mod h1:mZlypgoynMn2ayhGsjrPY/G/WiRiYO8WCPC6gUeg1RA=
github.com/pion/interceptor v0.0.5 h1:BOwlubM1lntji3eNaVrhW1Qk3u1UoemrhM4mbv24XGM=
......
......@@ -22,7 +22,7 @@ type router struct {
}
func NewRedisRouter(client *redis.Client, mgr *Manager) Router {
queue := NewRedisQueue(client, RouterTopic, RouterMaxAge)
queue := NewRedisQueue(client, pb.KeyRouterTopic(), RouterMaxAge)
return &router{queue, mgr}
}
......
......@@ -6,23 +6,23 @@ import (
pb "github.com/net-prophet/noir/pkg/proto"
log "github.com/pion/ion-log"
"github.com/pion/ion-sfu/pkg/sfu"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3"
)
func (w *worker) HandleSignal(request *pb.NoirRequest) error {
signal := request.GetSignal()
if request.Action == "request.signal.join" {
return w.HandleJoin(signal)
return w.HandleJoin(request)
}
return nil
}
func (w *worker) HandleJoin(signal *pb.SignalRequest) error {
func (w *worker) HandleJoin(request *pb.NoirRequest) error {
w.mu.Lock()
defer w.mu.Unlock()
mgr := *w.manager
signal := request.GetSignal()
join := signal.GetJoin()
pid := signal.Id
......@@ -105,6 +105,7 @@ func (w *worker) HandleJoin(signal *pb.SignalRequest) error {
packed, _ := json.Marshal(answer)
w.SignalReply(pid, &pb.NoirReply{
Id: request.Id,
Command: &pb.NoirReply_Signal{
Signal: &pb.SignalReply{
Id: pid,
......@@ -168,23 +169,32 @@ func (w *worker) PeerChannel(userData *pb.UserData, peer *sfu.Peer) {
validated, err := w.manager.ValidateOffer(roomData, userData.Id, desc.Desc)
numTracks := len(validated.MediaDescriptions)
if numTracks == 1 && validated.MediaDescriptions[0].MediaName.Media == "application" {
A, V, D, summary := TrackSummary(validated)
roomType := "room"
// Just one data track
if D == 1 && A == 0 && V == 0 {
userData.Publishing = false
} else if numTracks >= 1 {
} else if A > 0 || V > 0 {
// Publishing
options := roomData.GetOptions()
if options.GetIsChannel() == true {
roomType = "channel"
if roomData.GetPublisher() != "" {
log.Infof("channel already has a publisher, denying: %s", roomData.Id)
continue
} else if A > 1 || V > 1 {
log.Infof("cannot publish multiple video or audio tracks into channel %s: %s", roomData.Id, summary)
continue
} else {
log.Infof("publishing into channel %s", userData.RoomID)
roomData.Publisher = userData.Id
SaveRoomData(userData.RoomID, roomData, w.manager)
}
}
userData.Publishing = true
log.Infof("publishing [%dA/%dV/%dD] into %s %s: %s", A, V, D, roomType, userData.RoomID, summary)
}
if err != nil {
......@@ -194,8 +204,9 @@ func (w *worker) PeerChannel(userData *pb.UserData, peer *sfu.Peer) {
answer, _ := peer.Answer(desc.Desc)
bytes, err := json.Marshal(answer)
log.Debugf("got offer, sending reply %s", string(bytes))
log.Debugf("got offer %s, sending reply %s", request.Id, summary)
w.SignalReply(userData.Id, &pb.NoirReply{
Id: request.Id,
Command: &pb.NoirReply_Signal{
Signal: &pb.SignalReply{
Id: userData.Id,
......@@ -229,3 +240,41 @@ func (w *worker) PeerChannel(userData *pb.UserData, peer *sfu.Peer) {
}
}
}
func TrackSummary(desc *sdp.SessionDescription) (int, int, int, string) {
summary := ""
audioTracks, videoTracks, dataTracks := 0, 0, 0
for _, track := range desc.MediaDescriptions {
media := track.MediaName
switch media.Media {
case "application":
dataTracks += 1
case "audio":
audioTracks += 1
case "video":
videoTracks += 1
}