diff --git a/bitratecontroller.go b/bitratecontroller.go
index d5265ca..e67b8a2 100644
--- a/bitratecontroller.go
+++ b/bitratecontroller.go
@@ -7,7 +7,7 @@ import (
"time"
"github.com/pion/interceptor/pkg/cc"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
)
var (
diff --git a/client.go b/client.go
index dbea128..70a2351 100644
--- a/client.go
+++ b/client.go
@@ -23,7 +23,7 @@ import (
"github.com/pion/interceptor/pkg/stats"
"github.com/pion/logging"
"github.com/pion/rtcp"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
)
type ClientState int
@@ -60,26 +60,28 @@ var (
)
type ClientOptions struct {
- IdleTimeout time.Duration
- Type string
- EnableVoiceDetection bool
- EnablePlayoutDelay bool
+ IdleTimeout time.Duration `json:"idle_timeout"`
+ Type string `json:"type"`
+ EnableVoiceDetection bool `json:"enable_voice_detection"`
+ EnablePlayoutDelay bool `json:"enable_playout_delay"`
+ EnableOpusDTX bool `json:"enable_opus_dtx"`
+ EnableOpusInbandFEC bool `json:"enable_opus_inband_fec"`
// Configure the minimum playout delay that will be used by the client
// Recommendation:
// 0 ms: Certain gaming scenarios (likely without audio) where we will want to play the frame as soon as possible. Also, for remote desktop without audio where rendering a frame asap makes sense
// 100/150/200 ms: These could be the max target latency for interactive streaming use cases depending on the actual application (gaming, remoting with audio, interactive scenarios)
// 400 ms: Application that want to ensure a network glitch has very little chance of causing a freeze can start with a minimum delay target that is high enough to deal with network issues. Video streaming is one example.
- MinPlayoutDelay uint16
+ MinPlayoutDelay uint16 `json:"min_playout_delay"`
// Configure the minimum playout delay that will be used by the client
// Recommendation:
// 0 ms: Certain gaming scenarios (likely without audio) where we will want to play the frame as soon as possible. Also, for remote desktop without audio where rendering a frame asap makes sense
// 100/150/200 ms: These could be the max target latency for interactive streaming use cases depending on the actual application (gaming, remoting with audio, interactive scenarios)
// 400 ms: Application that want to ensure a network glitch has very little chance of causing a freeze can start with a minimum delay target that is high enough to deal with network issues. Video streaming is one example.
- MaxPlayoutDelay uint16
- JitterBufferMinWait time.Duration
- JitterBufferMaxWait time.Duration
+ MaxPlayoutDelay uint16 `json:"max_playout_delay"`
+ JitterBufferMinWait time.Duration `json:"jitter_buffer_min_wait"`
+ JitterBufferMaxWait time.Duration `json:"jitter_buffer_max_wait"`
// On unstable network, the packets can be arrived unordered which may affected the nack and packet loss counts, set this to true to allow the SFU to handle reordered packet
- ReorderPackets bool
+ ReorderPackets bool `json:"reorder_packets"`
Log logging.LeveledLogger
}
@@ -184,6 +186,8 @@ func DefaultClientOptions() ClientOptions {
Type: ClientTypePeer,
EnableVoiceDetection: true,
EnablePlayoutDelay: true,
+ EnableOpusDTX: true,
+ EnableOpusInbandFEC: true,
MinPlayoutDelay: 100,
MaxPlayoutDelay: 200,
JitterBufferMinWait: 20 * time.Millisecond,
@@ -416,7 +420,7 @@ func NewClient(s *SFU, id string, name string, peerConnectionConfig webrtc.Confi
case webrtc.PeerConnectionStateNew:
// do nothing
client.startIdleTimeout(opts.IdleTimeout)
- case webrtc.PeerConnectionState(webrtc.Unknown):
+ case webrtc.PeerConnectionState(webrtc.PeerConnectionStateUnknown):
// clean up
client.afterClosed()
}
@@ -742,7 +746,65 @@ func (c *Client) negotiateQueuOp(offer webrtc.SessionDescription) (*webrtc.Sessi
c.pendingRemoteCandidates = nil
- return c.peerConnection.PC().LocalDescription(), nil
+ sdp := c.setOpusSDP(*c.peerConnection.PC().LocalDescription())
+
+ return &sdp, nil
+}
+
+func (c *Client) setOpusSDP(sdp webrtc.SessionDescription) webrtc.SessionDescription {
+ if c.options.EnableOpusDTX {
+ var regex, err = regexp.Compile(`a=rtpmap:(\d+) opus\/(\d+)\/(\d+)`)
+ if err != nil {
+ c.log.Errorf("client: error on compile regex ", err)
+ return sdp
+ }
+ var opusLine = regex.FindString(sdp.SDP)
+
+ if opusLine == "" {
+ c.log.Errorf("client: error opus line not found")
+ return sdp
+ }
+
+ regex, err = regexp.Compile(`(\d+)`)
+ if err != nil {
+ c.log.Errorf("client: error on compile regex ", err)
+ return sdp
+ }
+
+ var opusNo = regex.FindString(opusLine)
+ if opusNo == "" {
+ c.log.Errorf("client: error opus no not found")
+ return sdp
+ }
+
+ fmtpRegex, err := regexp.Compile(`a=fmtp:` + opusNo + ` .+`)
+ if err != nil {
+ c.log.Errorf("client: error on compile regex ", err)
+ return sdp
+ }
+
+ var fmtpLine = fmtpRegex.FindString(sdp.SDP)
+ if fmtpLine == "" {
+ c.log.Errorf("client: error fmtp line not found")
+ return sdp
+ }
+
+ var newFmtpLine = ""
+
+ if c.options.EnableOpusDTX && !strings.Contains(fmtpLine, "usedtx=1") {
+ newFmtpLine += ";usedtx=1"
+ }
+
+ if c.options.EnableOpusInbandFEC && !strings.Contains(fmtpLine, "useinbandfec=1") {
+ newFmtpLine += ";useinbandfec=1"
+ }
+
+ if newFmtpLine != "" {
+ sdp.SDP = strings.Replace(sdp.SDP, fmtpLine, fmtpLine+newFmtpLine, -1)
+ }
+ }
+
+ return sdp
}
func (c *Client) renegotiate() {
diff --git a/client_test.go b/client_test.go
index f7b0ba9..735d625 100644
--- a/client_test.go
+++ b/client_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
"github.com/stretchr/testify/require"
)
diff --git a/clienttrack.go b/clienttrack.go
index 8a45c7e..8c1acc7 100644
--- a/clienttrack.go
+++ b/clienttrack.go
@@ -5,7 +5,7 @@ import (
"sync"
"github.com/pion/rtp"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
)
type iClientTrack interface {
diff --git a/clienttrackred.go b/clienttrackred.go
index 72c01ca..7773f37 100644
--- a/clienttrackred.go
+++ b/clienttrackred.go
@@ -6,7 +6,7 @@ import (
"github.com/inlivedev/sfu/pkg/rtppool"
"github.com/pion/rtp"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
)
var (
diff --git a/clienttracksimulcast.go b/clienttracksimulcast.go
index dbc72d3..8f13001 100644
--- a/clienttracksimulcast.go
+++ b/clienttracksimulcast.go
@@ -7,7 +7,7 @@ import (
"github.com/inlivedev/sfu/pkg/packetmap"
"github.com/pion/rtp"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
)
type simulcastClientTrack struct {
diff --git a/codec.go b/codec.go
index 56636bd..7daa5a2 100644
--- a/codec.go
+++ b/codec.go
@@ -6,8 +6,8 @@ import (
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
- "github.com/pion/webrtc/v3"
- "github.com/pion/webrtc/v3/pkg/media"
+ "github.com/pion/webrtc/v4"
+ "github.com/pion/webrtc/v4/pkg/media"
"golang.org/x/exp/slices"
)
diff --git a/datachannel.go b/datachannel.go
index c39919c..9bc1f38 100644
--- a/datachannel.go
+++ b/datachannel.go
@@ -6,7 +6,7 @@ import (
"sync"
"time"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
)
var (
diff --git a/datachannel_test.go b/datachannel_test.go
index a3ec0db..60e9072 100644
--- a/datachannel_test.go
+++ b/datachannel_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"time"
- "github.com/pion/webrtc/v3"
+ "github.com/pion/webrtc/v4"
"github.com/stretchr/testify/require"
)
diff --git a/examples/http-websocket/index.html b/examples/http-websocket/index.html
index bd2124f..3e00b4b 100644
--- a/examples/http-websocket/index.html
+++ b/examples/http-websocket/index.html
@@ -153,6 +153,30 @@
const maxBw = 2500*1000
+ let mutedMic = false
+
+ let mutedCam = false
+
+ const toggleMic = async (e) => {
+ peerConnection.getSenders().forEach(sender => {
+ if (sender.track.kind === 'audio') {
+ sender.track.enabled = !mutedMic
+ mutedMic = !mutedMic
+ e.target.innerText = !sender.track.enabled?'Unmute Mic':'Mute Mic'
+ }
+ })
+ }
+
+ const toggleCam = async (e) => {
+ peerConnection.getSenders().forEach(sender => {
+ if (sender.track.kind === 'video') {
+ sender.track.enabled = !mutedCam
+ mutedCam = !mutedCam
+ e.target.innerText = !sender.track.enabled?'Unmute Cam':'Mute Cam'
+ }
+ })
+ }
+
const startWs =async ()=>{
let debug = false
const urlParams = new URLSearchParams(window.location.search);
@@ -273,6 +297,49 @@
start('vp9')
}
+ function setOpusSDP(sdp){
+ let fmtlineOpus = ''
+
+ let fmtNo = null
+ // find fmtline for opus from SDP
+ const opusFmtLines = sdp.match(/a=rtpmap:(\d+) opus\/(\d+)\/(\d+)/)
+ if (opusFmtLines) {
+ // extract only the first digit
+ const matchesNo = opusFmtLines[0].match(/(\d+)/)
+ if (matchesNo) {
+ fmtNo = matchesNo[0]
+ }
+ }
+
+ console.log('fmtNo', fmtNo)
+
+ // find fmtp line for opus from SDP
+ const opusFmtpLines = sdp.match(new RegExp(`a=fmtp:${fmtNo} .+`))
+ if (opusFmtpLines) {
+ fmtlineOpus = opusFmtpLines[0]
+ }
+
+ // check if DTX is enabled and if not, add it to the SDP
+ if (!fmtlineOpus.includes('usedtx=1')) {
+ fmtlineOpus += ';usedtx=1'
+ }
+
+ // check if inbandfec is enabled and if not, add it to the SDP
+ if (!fmtlineOpus.includes('useinbandfec=1')) {
+ fmtlineOpus += ';useinbandfec=1'
+ }
+
+ console.log('fmtlineOpus', fmtlineOpus)
+
+ // replace the old fmtp line with the new one
+ sdp = sdp.replace(
+ new RegExp(`a=fmtp:${fmtNo} .+`),
+ `a=fmtp:${fmtNo} ${fmtlineOpus}`
+ )
+
+ return sdp
+ }
+
async function start(codec) {
await startWs()
document.getElementById("btnStart").disabled = true;
@@ -553,6 +620,8 @@
}
const offer=await peerConnection.createOffer()
+
+ // offer.sdp = setOpusSDP(offer.sdp)
await peerConnection.setLocalDescription(offer)
@@ -957,6 +1026,8 @@
Low
document.addEventListener("DOMContentLoaded", function(event) {
document.getElementById("btnStart").onclick = startH264;
document.getElementById("btnStartVP9").onclick = startVP9;
+ document.getElementById("btnToggleMic").onclick = toggleMic;
+ document.getElementById("btnToggleCam").onclick = toggleCam;
document.getElementById("btnShareScreen").onclick = shareScreen;
document.getElementById("btnStats").onclick = toggleStats;
document.getElementById("selectQuality").onchange = switchQuality;
@@ -979,6 +1050,8 @@ Outbound Video