Git Product home page Git Product logo

sfu's Introduction

Important

This library still in very early development. The API might change in the future. We use it in production for inLive Hub project. We're a small team, so we can't guarantee that we can fix all issues. But we will try our best to fix it especially when it's related with our product. If you have any questions or find any issues, please write it to this repo Github issue. We will appreciate it.

inLive Hub SFU

This Golang package is an SFU library based on Pion WebRTC. It is designed to be used in the inLive Hub project, but it can also be used in other projects. The goal is to have a Golang SFU library that is portable but easy to extend.

Installation

This is a Go module, so you can install it by running this command in your app directory:

go get github.com/inlivedev/sfu

Features

  • Group call
  • Screen sharing
  • Perfect negotiation
  • Data channel - note: only room channel currenty available
  • Simulcast - note: still need some tuning on bitrate management
  • Events
  • Room statistic

Components:

This SFU package has four components:

  1. Client, a WebRTC client as a relay client in the SFU.
  2. SFU, the SFU server.
  3. Room, a signaling controller between SFU and clients.
  4. Room Manager, a controller to manage multiple rooms

How to use

The one that you will interact with will be the room and the room manager. A server will at least have a single room manager. Before being able to run a group video call, you need to create a room with the room manager, then use that room to add clients to it. You can use any protocol as your signaling controller.

SFU <---> Room <---> REST/WebSocket/gRPC <---> browser/app
 |
 |
 \/
 Clients

See the example folder to see how to write a group video call app with this SFU package. The following section will explain in detail how to use this package.

Connect to the SFU

On the first connection to SFU, a client will do a WebRTC negotiation, exchanging Session Description Protocol(SDP) and ice candidates. The client will send an offer SDP to initiate the negotiation. The SFU will respond with an answer SDP. Then both will exchange the ice candidates.

The steps will be like this to initiate the negotiation with the client.

  1. Add a client first to the SFU by calling AddClient(clientID, sfu.DefaultClientOptions()) on the current room instance. The clientID must be a unique string. The client options can be customized, but the default should be enough.
  2. Once added, now we can access the relay client from the SFU through currentRoom.SFU.GetClient(clientID). The relay client is a WebRTC client on the SFU side that will be used to negotiate with the client.
  3. To start the negotiation you can pass the offer SDP from the client to client.Negotiate(offer) method. The method will return an answer SDP that you need to pass back to the client. As part of the negotiation, you need to pass the ice candidates from the client to client.AddICECandidate(candidate) method.
  4. You also need to pass the ice candidates from the relay client to the client. You can get the ice candidates from the relay client by listening to the client.OnICECandidate event. The event will be triggered when the relay client has a new ice candidate. You only need to send this ice candidate to the client.

Once you complete the five steps above, the connection should be established. See the example of how the connection is established here

Publish and subscribe tracks

When a client wants to publish a track to the SFU, usually the flow is like this:

  1. Get the media tracks from either the camera or the screen sharing. You can use getUserMedia to get the media tracks.
  2. Add the media tracks to the WebRTC PeerConnection instance. You can use addTrack to add the media tracks to the PeerConnection instance.
  3. This will add the tracks to SFU but won't be published until the client sets the track source type either media or screen.
  4. When the track is added to SFU, the client will trigger client.OnTrackAdded([]*tracks). We need to use this event to set the track source type by calling client.SetTracksSourceType(map[trackTypes string]TrackType)`
  5. When successful, this will trigger client.OnTrackAvailable([]*tracks) event on other clients.
  6. Use this event to subscribe to the tracks by calling client.SubscribeTracks(tracksReq []SubscribeTrackRequest) method. When successful, it will trigger peerConnection.OnTrack() event that we can use to attach the track to the HTML video element. Or instead of manually subscribing to each track, you can also use client.SubscribeAllTracks() method to subscribe to all available tracks. You only need to call it once, and all available tracks will automatically added to the PeerConnection instance.

See the client_test.go for more details on how to publish and subscribe to tracks.

Renegotiation

Usually, renegotiation will be needed when a new track is added or removed. The renegotiation can be initiated by the SFU if a new client joins the room and publishes a new track to the room. To broadcast the track to all clients, the SFU must renegotiate with each client to inform them about the new tracks.

Renegotiation from the SFU

To wait for the renegotiation process from SFU to make sure you receive a new track once published in a room, you can do this:

  1. When creating a new client after calling currentRoom.addClient() method, you can listen to the client.OnRenegotiation event. The event will be triggered when the SFU is trying to renegotiate with the client.
  2. Once you receive an offer from the SFU, you can pass the offer SDP to the client and get the answer to pass back to the SFU.
  3. The event client.OnRenegotiation must return an answer SDP received from the client, and it suggested having a timeout when waiting for the answer from the client. If the timeout is reached, you can return an empty SDP to the SFU.

See the example of how the renegotiation is handled here

Renegotiation from the client

The renegotiation also can be initiated by the client if the client is adding a new track, for example, by doing a screen sharing. Because both sides can initiate the renegotiation, there is a possibility that both sides are trying to renegotiate at the same time.

To prevent this, we need to check with the SFU relay client if it is allowed to do renegotiation from the client side by checking the client.IsAllowNegotiation() method. If it returns true, then we can start the renegotiation by calling client.Negotiate(offer) method. The same method that we used for the first negotiation. The SFU will respond with an SDP answer to the client. Make sure after you call client.IsAllowNegotiation() method, you also call client.Negotiate(offer) method to make sure the in-renegotiation state is processed by the SFU. If you do not call the following method, the SFU will think that the client is doing a renegotiation and won't initiate the renegotiation.

If the client.IsAllowNegotiation() is returned false, it means the SFU is currently trying to renegotiate with the client. So the client should wait until the SFU is done renegotiating with the client. In the browser, you can listen to the onnegotiationneeded event to know when the SFU is done renegotiating with the client.

Leave the room

If a client wants to leave a room or disconnect from the SFU. You can close the PeerConnection instance on the client side. The SFU will detect that the client is closed and will remove the client from the room.

Logging

This library is using Glog for logging. You can set the log level by setting the -flag stderrthreshold=warning when running an application from the command line. You can control it also from your app using the flag package. The default log level is info. The available log levels are info, warning, error, fatal, and panic. You can read more about Glog here

To set the log printing to stderr instead of a file, you can set the -flag logtostderr=true flag variable to true. The default is false.

Licence

MIT License - see LICENSE for the full text

sfu's People

Contributors

ryanbekhen avatar tyohan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

sfu's Issues

Improving track subscription flow in a room to support multiple sources of track

When a participant publishes a track to a room, they will call peerConnection.AddTrack(), and this will trigger thepeerConnection.OnTrack() event on the SFU side. We face an issue once more than one type of video is published in a room, for example, once a screensharing stream is published in a room. How do we know which track is from the camera and which is from screensharing? So far, I haven't found a spec from WebRTC that specifies how to tell the difference between video sources. The media track has label property that can be used for the source, but I don't think it's available to use in Pion.

To make it easier to identify the tracks in the room, the user it belong, or the input device of that track, we need to change how we publish and subscribe to the media tracks. This is what I have that I think can help us with that need:

When a participant adds a track:

  1. By default, when a participant joins the room, they're not subscribed to any tracks in the room.
  2. The participant can subscribe to all available tracks in the room by calling client.SubscribeAllTracks()
  3. They can also call room.GetTracks() to get a list of available tracks to subscribe to and subscribe to each track that they like by calling client.SubscribeTrack(MSID) or client.SubscribeTracksByClient(clientid)
  4. When a participant adds a track to the peerConnection, the SFU will trigger the peerConnection.OnTrack() event on the pair peerConnection on the SFU side. That event then will make the pair client trigger OnTrackAdded(ssrc), which is only received by the participant who previously added it.
  5. Once the publisher receives the OnTrackAdded() event, the publisher must set the metadata of that track to include the source of that track. This can be done by calling client.SetTrackMetadata(SSRC, metadata)
  6. Once the track metadata is set, the SFU will publish the track to the room and let the other participants know that a new track is available to subscribe to.

When a participant subscribes to a track

  1. The participant will call room.GetTracks() to know what tracks to subscribe to. Each track will contains MSID, clientID, source type(camera, screen, or microphone), and added timestamp
  2. The participant then calls client.SubscribeTracks([]MSID) to subscribe to each track they like.
  3. The SFU will publish those tracks to the participant
  4. If a new track is published by another participant in a room, the client.OnTrackAvailable(trackDetail) will trigger, and the participant can subscribe by calling the same client.SubscribeTracks([]MSID) if they want to.
  5. Track detail will have information about clientid, msid, ssrc, kind, source type, codec of original track.

By doing this way, we can manage the publishing and subscription to tracks easier. Either a participant joins as a publisher or only as a subscriber, or as a passive participant.

Track is not remove when internet connection lost

When a client connected to the room then suddenly their internet is drop or disconnected, their connection will be failed after a timeout but the other clients in the room is not receiving track removed event and it make the client video still exists even the client already disconnected.

To reproduce:

  1. Join into a room with other clients.
  2. After connected, try to turn off wifi or or any internet connection.
  3. The client will disconnect
  4. Other clients in the room still see the video but in freezing state.

Perfect negotiation seems not fully working

Just found out that when a race condition of renegotiation happens between the client and SFU after adding new tracks, we always let the SFU do the renegotiation first and not continue the renegotiation on the client side. This makes the new tracks added to the client not trigger the ontrack event on the SFU side. Ideally, when the SFU finish the renegotiation, the SFU let the client knows that they can continue the previous renegotiation.

I will try to create a test to verify this.

Another idea for perfect renegotiation based on conversations on the Pion Slack channel is to separate the publisher and subscriber connection. This will prevent a race condition when both sides add a new track because it will trigger the renegotiation on different connections.

Potential issue with Firefox and Chrome Simulcast

There is a reported issue that the onTrack event won't be triggered when the remote client is using Firefox and sending simulcast track with H264. Similar issue also exist on Chrome mobile.

Based on this conversation the issue may be because:

  • The Firefox is not support simulcast with H264, with this we must allow to configure the room with specific codec to use.
  • Chrome won't send RID track with lower than 180 resolution, when this happen the SFU will never publish the track because it's waiting the low track to receive before publish it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.