Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update to v4 and add dtx for opus #16

Merged
merged 2 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bitratecontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"time"

"github.com/pion/interceptor/pkg/cc"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
)

var (
Expand Down
86 changes: 74 additions & 12 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"testing"
"time"

"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
"github.com/stretchr/testify/require"
)

Expand Down
2 changes: 1 addition & 1 deletion clienttrack.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"sync"

"github.com/pion/rtp"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
)

type iClientTrack interface {
Expand Down
2 changes: 1 addition & 1 deletion clienttrackred.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
2 changes: 1 addition & 1 deletion clienttracksimulcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down
2 changes: 1 addition & 1 deletion datachannel.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"sync"
"time"

"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
)

var (
Expand Down
2 changes: 1 addition & 1 deletion datachannel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"
"time"

"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
"github.com/stretchr/testify/require"
)

Expand Down
73 changes: 73 additions & 0 deletions examples/http-websocket/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -553,6 +620,8 @@
}

const offer=await peerConnection.createOffer()

// offer.sdp = setOpusSDP(offer.sdp)

await peerConnection.setLocalDescription(offer)

Expand Down Expand Up @@ -957,6 +1026,8 @@ <h3>Low</h3>
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;
Expand All @@ -979,6 +1050,8 @@ <h3>Outbound Video</h3>
<footer>
<button id="btnStart">Start H264</button>
<button id="btnStartVP9">Start with VP9</button>
<button id="btnToggleMic">Mute Mic</button>
<button id="btnToggleCam">Mute Cam</button>
<span><input id="simulcast" type="checkbox" value="simulcast">Simulcast</span>
<span><input id="svc" type="checkbox" value="svc" checked>SVC</span>
<button id="btnShareScreen">Share Screen</button>
Expand Down
2 changes: 1 addition & 1 deletion examples/http-websocket/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/inlivedev/sfu/pkg/interceptors/voiceactivedetector"
"github.com/inlivedev/sfu/pkg/networkmonitor"
"github.com/pion/logging"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
"golang.org/x/net/websocket"
)

Expand Down
40 changes: 23 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,45 @@ go 1.21

require (
github.com/pion/ice/v2 v2.3.13
github.com/pion/interceptor v0.1.25
github.com/pion/rtcp v1.2.12
github.com/pion/webrtc/v3 v3.2.32
github.com/pion/interceptor v0.1.29
github.com/pion/rtcp v1.2.14
github.com/stretchr/testify v1.9.0
)

require (
github.com/golang/glog v1.1.2
github.com/jaevor/go-nanoid v1.3.0
github.com/pion/webrtc/v4 v4.0.0-beta.21
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/text v0.14.0
golang.org/x/text v0.15.0
)

require gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
require (
github.com/pion/ice/v3 v3.0.7 // indirect
github.com/pion/mdns/v2 v2.0.7 // indirect
github.com/pion/srtp/v3 v3.0.1 // indirect
github.com/pion/stun/v2 v2.0.0 // indirect
github.com/pion/transport/v3 v3.0.2 // indirect
github.com/pion/turn/v3 v3.0.3 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.3.1
github.com/pion/datachannel v1.5.5 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/google/uuid v1.6.0
github.com/pion/datachannel v1.5.6 // indirect
github.com/pion/dtls/v2 v2.2.11 // indirect
github.com/pion/logging v0.2.2
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtp v1.8.5
github.com/pion/sctp v1.8.13 // indirect
github.com/pion/rtp v1.8.6
github.com/pion/sctp v1.8.16 // indirect
github.com/pion/sdp/v3 v3.0.9
github.com/pion/srtp/v2 v2.0.18 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/transport/v2 v2.2.3 // indirect
github.com/pion/transport/v2 v2.2.4 // indirect
github.com/pion/turn/v2 v2.1.3
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/net v0.20.0 // direct
golang.org/x/sys v0.16.0
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // direct
golang.org/x/sys v0.20.0
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading