Skip to content

Commit

Permalink
add docs for public methods and adjust some public method's name
Browse files Browse the repository at this point in the history
  • Loading branch information
Yohan Totting committed Jan 5, 2024
1 parent 0b8c53f commit 7ade7fc
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 27 deletions.
45 changes: 41 additions & 4 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,6 @@ func NewClient(s *SFU, id string, name string, peerConnectionConfig webrtc.Confi
return
}

glog.Info("client: send pli ", remoteTrack.ID())
if err := client.peerConnection.PC().WriteRTCP([]rtcp.Packet{
&rtcp.PictureLossIndication{MediaSSRC: uint32(remoteTrack.SSRC())},
}); err != nil {
Expand Down Expand Up @@ -454,11 +453,14 @@ func (c *Client) Context() context.Context {
return c.context
}

func (c *Client) OnTracksAdded(f func(addedTracks []ITrack)) {
// OnTrackAdded event is to confirmed the source type of the pending published tracks.
// If the event is not listened, the pending published tracks will be ignored and not published to other clients.
// Once received, respond with `client.SetTracksSourceType()“ to confirm the source type of the pending published tracks
func (c *Client) OnTracksAdded(callback func(addedTracks []ITrack)) {
c.mu.Lock()
defer c.mu.Unlock()

c.onTracksAdded = f
c.onTracksAdded = callback
}

// Init and Complete negotiation is used for bridging the room between servers
Expand Down Expand Up @@ -585,13 +587,19 @@ func (c *Client) renegotiate() {
})
}

// OnBeforeRenegotiation event is called before the SFU is trying to renegotiate with the client.
// The client must be listen for this event and set the callback to return true if the client is ready to renegotiate
// and no current negotiation is in progress from the client.
func (c *Client) OnBeforeRenegotiation(callback func(context.Context) bool) {
c.mu.Lock()
defer c.mu.Unlock()

c.onBeforeRenegotiation = callback
}

// OnRenegotiation event is called when the SFU is trying to renegotiate with the client.
// The callback will receive the SDP offer from the SFU that must be use to create the SDP answer from the client.
// The SDP answer then can be passed back to the SFU using `client.CompleteNegotiation()` method.
func (c *Client) OnRenegotiation(callback func(context.Context, webrtc.SessionDescription) (webrtc.SessionDescription, error)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down Expand Up @@ -693,6 +701,9 @@ func (c *Client) allowRemoteRenegotiation() {
})
}

// OnAllowedRemoteRenegotiation event is called when the SFU is done with the renegotiation
// and ready to receive the renegotiation from the client.
// Use this event to trigger the client to do renegotiation if needed.
func (c *Client) OnAllowedRemoteRenegotiation(callback func()) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down Expand Up @@ -790,7 +801,6 @@ func (c *Client) enableReportAndStats(rtpSender *webrtc.RTPSender, track iClient
for _, p := range rtcpPackets {
switch p.(type) {
case *rtcp.PictureLossIndication:
glog.Info("client: got pli ", track.ID())
track.RequestPLI()
case *rtcp.FullIntraRequest:
track.RequestPLI()
Expand Down Expand Up @@ -872,6 +882,9 @@ func (c *Client) AddICECandidate(candidate webrtc.ICECandidateInit) error {
return nil
}

// OnTracksAvailable event is called when the SFU has ice candidate that need to pass to the client.
// This event will triggered during negotiation process to exchanges ice candidates between SFU and client.
// The client can also pass the ice candidate to the SFU using `client.AddICECandidate()` method.
func (c *Client) OnIceCandidate(callback func(context.Context, *webrtc.ICECandidate)) {
c.onIceCandidate = callback
}
Expand All @@ -896,6 +909,8 @@ func (c *Client) sendPendingLocalCandidates() {
c.pendingLocalCandidates = nil
}

// OnConnectionStateChanged event is called when the SFU connection state is changed.
// The callback will receive the connection state as the new state.
func (c *Client) OnConnectionStateChanged(callback func(webrtc.PeerConnectionState)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand All @@ -915,10 +930,15 @@ func (c *Client) onJoined() {
}
}

// OnJoined event is called when the client is joined to the room.
// This doesn't mean that the client's tracks are already published to the room.
// This event can be use to track number of clients in the room.
func (c *Client) OnJoined(callback func()) {
c.onJoinedCallbacks = append(c.onJoinedCallbacks, callback)
}

// OnLeft event is called when the client is left from the room.
// This event can be use to track number of clients in the room.
func (c *Client) OnLeft(callback func()) {
c.onLeftCallbacks = append(c.onLeftCallbacks, callback)
}
Expand All @@ -929,6 +949,8 @@ func (c *Client) onLeft() {
}
}

// OnTrackRemoved event is called when the client's track is removed from the room.
// Usually this triggered when the client is disconnected from the room or a track is unpublished from the client.
func (c *Client) OnTrackRemoved(callback func(sourceType string, track *webrtc.TrackLocalStaticRTP)) {
c.onTrackRemovedCallbacks = append(c.onTrackRemovedCallbacks, callback)
}
Expand Down Expand Up @@ -991,6 +1013,11 @@ func (c *Client) updateSenderStats(sender *webrtc.RTPSender) {
}
}

// SetTracksSourceType set the source type of the pending published tracks.
// This function must be called after receiving OnTracksAdded event.
// The source type can be "media" or "screen"
// Calling this method will trigger `client.OnTracksAvailable` event to other clients.
// The other clients then can subscribe the tracks using `client.SubscribeTracks()` method.
func (c *Client) SetTracksSourceType(trackTypes map[string]TrackType) {
availableTracks := make([]ITrack, 0)
removeTrackIDs := make([]string, 0)
Expand All @@ -1011,6 +1038,10 @@ func (c *Client) SetTracksSourceType(trackTypes map[string]TrackType) {
}
}

// SubscribeTracks subscribe tracks from other clients that are published to this client
// The client must listen for `client.OnTracksAvailable` to know if a new track is available to subscribe.
// Calling subscribe tracks will trigger the SFU renegotiation with the client.
// Make sure you already listen for `client.OnRenegotiation()` before calling this method to track subscription can complete.
func (c *Client) SubscribeTracks(req []SubscribeTrackRequest) error {
if c.peerConnection.PC().ConnectionState() != webrtc.PeerConnectionStateConnected {
c.pendingReceivedTracks = append(c.pendingReceivedTracks, req...)
Expand Down Expand Up @@ -1086,6 +1117,8 @@ func (c *Client) SubscribeAllTracks() {
}
}

// SetQuality method is to set the maximum quality of the video that will be sent to the client.
// This is for bandwidth efficiency purpose and use when the video is rendered in smaller size than the original size.
func (c *Client) SetQuality(quality QualityLevel) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down Expand Up @@ -1305,6 +1338,8 @@ func (c *Client) SFU() *SFU {
return c.sfu
}

// OnTracksAvailable event is called when the SFU is trying to publish new tracks to the client.
// The client then can subscribe to the tracks by calling `client.SubscribeTracks()` method.
func (c *Client) OnTracksAvailable(callback func([]ITrack)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand All @@ -1317,6 +1352,8 @@ func (c *Client) onTracksAvailable(tracks []ITrack) {
}
}

// OnVoiceDetected event is called when the SFU is detecting voice activity in the room.
// The callback will receive the voice activity data that can be use for visual indicator of current speaker.
func (c *Client) OnVoiceDetected(callback func(activity voiceactivedetector.VoiceActivity)) {
c.mu.Lock()
defer c.mu.Unlock()
Expand Down
20 changes: 15 additions & 5 deletions codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,24 +131,34 @@ var (
func RegisterCodecs(m *webrtc.MediaEngine, codecs []string) error {
errors := []error{}

if !slices.Contains(codecs, "video/rtx") {
codecs = append(codecs, "video/rtx")
}

for _, codec := range audioCodecs {
if slices.Contains(codecs, codec.MimeType) {
if err := m.RegisterCodec(codec, webrtc.RTPCodecTypeAudio); err != nil {
errors = append(errors, err)
}
}

}

registeredVideoCodecs := make([]webrtc.RTPCodecParameters, 0)

for _, codec := range videoCodecs {

if slices.Contains(codecs, codec.MimeType) {
if err := m.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil {
errors = append(errors, err)
}

registeredVideoCodecs = append(registeredVideoCodecs, codec)
}
}

for _, codec := range registeredVideoCodecs {
for _, videoCodec := range videoCodecs {
if videoCodec.RTPCodecCapability.MimeType == "video/rtx" && videoCodec.RTPCodecCapability.SDPFmtpLine == "apt="+string(codec.PayloadType) {
if err := m.RegisterCodec(videoCodec, webrtc.RTPCodecTypeVideo); err != nil {
errors = append(errors, err)
}
}
}
}

Expand Down
13 changes: 7 additions & 6 deletions manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ func (m *Manager) getRoom(id string) (*Room, error) {
return room, nil
}

func (m *Manager) EndRoom(id string) error {
// CloseRoom will stop all clients in the room and close it.
// This is a shortcut to find a room with id and close it.
func (m *Manager) CloseRoom(id string) error {
m.mutex.Lock()
defer m.mutex.Unlock()

Expand All @@ -180,16 +182,15 @@ func (m *Manager) EndRoom(id string) error {
return ErrRoomNotFound
}

room.StopAllClients()

return nil
return room.Close()
}

func (m *Manager) Stop() {
// Close will close all room and canceling the context
func (m *Manager) Close() {
defer m.cancel()

for _, room := range m.rooms {
room.StopAllClients()
room.Close()
}
}

Expand Down
27 changes: 15 additions & 12 deletions room.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func DefaultRoomOptions() RoomOptions {
return RoomOptions{
Bitrates: DefaultBitrates(),
QualityPreset: DefaultQualityPreset(),
Codecs: []string{"video/flexfec-03", webrtc.MimeTypeVP9, webrtc.MimeTypeH264, "audio/red", webrtc.MimeTypeOpus},
Codecs: []string{webrtc.MimeTypeVP9, webrtc.MimeTypeH264, "audio/red", webrtc.MimeTypeOpus},
ClientTimeout: 10 * time.Minute,
PLIInterval: 0,
}
Expand Down Expand Up @@ -160,16 +160,14 @@ func (r *Room) AddExtension(extension IExtension) {
r.extensions = append(r.extensions, extension)
}

// room can only closed once it's empty or it will return error
// Close the room and stop all clients. All connected clients will stopped and removed from the room.
// All clients will get `connectionstateevent` with `closed` state.
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event
func (r *Room) Close() error {
if r.state == StateRoomClosed {
return ErrRoomIsClosed
}

if r.sfu.clients.Length() > 0 {
return ErrRoomIsNotEmpty
}

r.cancel()

r.sfu.Stop()
Expand Down Expand Up @@ -200,12 +198,6 @@ func (r *Room) StopClient(id string) error {
return client.stop()
}

func (r *Room) StopAllClients() {
for _, client := range r.sfu.clients.GetClients() {
client.PeerConnection().Close()
}
}

func (r *Room) AddClient(id, name string, opts ClientOptions) (*Client, error) {
r.mutex.Lock()
defer r.mutex.Unlock()
Expand Down Expand Up @@ -375,10 +367,21 @@ func (r *Room) CreateDataChannel(label string, opts DataChannelOptions) error {
return r.sfu.CreateDataChannel(label, opts)
}

// BitratesConfig return the current bitrate configuration that used in bitrate controller
// Client should use this to configure the bitrate when publishing media tracks
// Inconsistent bitrate configuration between client and server will result missed bitrate calculation and
// could affecting packet loss and media quality
func (r *Room) BitratesConfig() BitratesConfig {
return r.sfu.bitratesConfig
}

// CodecsPreference return the current codecs preference that used in SFU
// Client should use this to configure the used codecs when publishing media tracks
// Inconsistent codecs preference between client and server can make the SFU cannot handle the codec properly
func (r *Room) CodecsPreference() []string {
return r.sfu.codecs
}

func (r *Room) Context() context.Context {
return r.context
}
Expand Down
4 changes: 4 additions & 0 deletions sfu.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import (
"golang.org/x/exp/slices"
)

// BitratesConfig is the configuration for the bitrate that will be used for adaptive bitrates controller
// The paramenter is in bps (bit per second) for non pixels parameters.
// For pixels parameters, it is total pixels (width * height) of the video.
// High, Mid, and Low are the references for bitrate controller to decide the max bitrate to send to the client.
type BitratesConfig struct {
AudioRed uint32 `json:"audio_red,omitempty" yaml:"audio_red,omitempty" mapstructure:"audio_red,omitempty"`
Audio uint32 `json:"audio,omitempty" yaml:"audio,omitempty" mapstructure:"audio,omitempty"`
Expand Down

0 comments on commit 7ade7fc

Please sign in to comment.