mirror of
https://github.com/pion/webrtc.git
synced 2025-12-24 11:51:03 +08:00
Implement SetCodecPreferences in RTPTransceiver
This allows to set supported codecs per transceiver. Resolves #1847
This commit is contained in:
@@ -202,6 +202,7 @@ var (
|
||||
|
||||
errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil")
|
||||
errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending")
|
||||
errRTPTransceiverCodecUnsupported = errors.New("unsupported codec type by this transceiver")
|
||||
|
||||
errSCTPTransportDTLS = errors.New("DTLS not established")
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
@@ -57,6 +58,8 @@ type MediaEngine struct {
|
||||
|
||||
headerExtensions []mediaEngineHeaderExtension
|
||||
negotiatedHeaderExtensions map[int]mediaEngineHeaderExtension
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// RegisterDefaultCodecs registers the default codecs supported by Pion WebRTC.
|
||||
@@ -196,6 +199,9 @@ func (m *MediaEngine) addCodec(codecs []RTPCodecParameters, codec RTPCodecParame
|
||||
// These are the list of codecs supported by this PeerConnection.
|
||||
// RegisterCodec is not safe for concurrent use.
|
||||
func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
codec.statsID = fmt.Sprintf("RTPCodec-%d", time.Now().UnixNano())
|
||||
switch typ {
|
||||
case RTPCodecTypeAudio:
|
||||
@@ -211,6 +217,9 @@ func (m *MediaEngine) RegisterCodec(codec RTPCodecParameters, typ RTPCodecType)
|
||||
// RegisterHeaderExtension adds a header extension to the MediaEngine
|
||||
// To determine the negotiated value use `GetHeaderExtensionID` after signaling is complete
|
||||
func (m *MediaEngine) RegisterHeaderExtension(extension RTPHeaderExtensionCapability, typ RTPCodecType, allowedDirections ...RTPTransceiverDirection) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.negotiatedHeaderExtensions == nil {
|
||||
m.negotiatedHeaderExtensions = map[int]mediaEngineHeaderExtension{}
|
||||
}
|
||||
@@ -251,6 +260,9 @@ func (m *MediaEngine) RegisterHeaderExtension(extension RTPHeaderExtensionCapabi
|
||||
|
||||
// RegisterFeedback adds feedback mechanism to already registered codecs.
|
||||
func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPCodecType) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
switch typ {
|
||||
case RTPCodecTypeVideo:
|
||||
for i, v := range m.videoCodecs {
|
||||
@@ -268,6 +280,9 @@ func (m *MediaEngine) RegisterFeedback(feedback RTCPFeedback, typ RTPCodecType)
|
||||
// getHeaderExtensionID returns the negotiated ID for a header extension.
|
||||
// If the Header Extension isn't enabled ok will be false
|
||||
func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapability) (val int, audioNegotiated, videoNegotiated bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if m.negotiatedHeaderExtensions == nil {
|
||||
return 0, false, false
|
||||
}
|
||||
@@ -284,6 +299,8 @@ func (m *MediaEngine) getHeaderExtensionID(extension RTPHeaderExtensionCapabilit
|
||||
// copy copies any user modifiable state of the MediaEngine
|
||||
// all internal state is reset
|
||||
func (m *MediaEngine) copy() *MediaEngine {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
cloned := &MediaEngine{
|
||||
videoCodecs: append([]RTPCodecParameters{}, m.videoCodecs...),
|
||||
audioCodecs: append([]RTPCodecParameters{}, m.audioCodecs...),
|
||||
@@ -296,12 +313,24 @@ func (m *MediaEngine) copy() *MediaEngine {
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParameters, RTPCodecType, error) {
|
||||
for _, codec := range m.negotiatedVideoCodecs {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
codecs := m.negotiatedVideoCodecs
|
||||
if !m.negotiatedVideo {
|
||||
codecs = m.videoCodecs
|
||||
}
|
||||
for _, codec := range codecs {
|
||||
if codec.PayloadType == payloadType {
|
||||
return codec, RTPCodecTypeVideo, nil
|
||||
}
|
||||
}
|
||||
for _, codec := range m.negotiatedAudioCodecs {
|
||||
|
||||
codecs = m.negotiatedAudioCodecs
|
||||
if !m.negotiatedAudio {
|
||||
codecs = m.audioCodecs
|
||||
}
|
||||
for _, codec := range codecs {
|
||||
if codec.PayloadType == payloadType {
|
||||
return codec, RTPCodecTypeAudio, nil
|
||||
}
|
||||
@@ -311,6 +340,9 @@ func (m *MediaEngine) getCodecByPayload(payloadType PayloadType) (RTPCodecParame
|
||||
}
|
||||
|
||||
func (m *MediaEngine) collectStats(collector *statsReportCollector) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
statsLoop := func(codecs []RTPCodecParameters) {
|
||||
for _, codec := range codecs {
|
||||
collector.Collecting()
|
||||
@@ -418,6 +450,9 @@ func (m *MediaEngine) pushCodecs(codecs []RTPCodecParameters, typ RTPCodecType)
|
||||
|
||||
// Update the MediaEngine from a remote description
|
||||
func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
for _, media := range desc.MediaDescriptions {
|
||||
var typ RTPCodecType
|
||||
switch {
|
||||
@@ -478,6 +513,9 @@ func (m *MediaEngine) updateFromRemoteDescription(desc sdp.SessionDescription) e
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
if typ == RTPCodecTypeVideo {
|
||||
if m.negotiatedVideo {
|
||||
return m.negotiatedVideoCodecs
|
||||
@@ -496,6 +534,9 @@ func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters {
|
||||
}
|
||||
|
||||
func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
headerExtensions := make([]RTPHeaderExtensionParameter, 0)
|
||||
|
||||
if m.negotiatedVideo && typ == RTPCodecTypeVideo ||
|
||||
@@ -525,6 +566,8 @@ func (m *MediaEngine) getRTPParametersByPayloadType(payloadType PayloadType) (RT
|
||||
return RTPParameters{}, err
|
||||
}
|
||||
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
headerExtensions := make([]RTPHeaderExtensionParameter, 0)
|
||||
for id, e := range m.negotiatedHeaderExtensions {
|
||||
if e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo {
|
||||
|
||||
@@ -1052,7 +1052,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error {
|
||||
localDirection = RTPTransceiverDirectionSendonly
|
||||
}
|
||||
|
||||
t = newRTPTransceiver(receiver, nil, localDirection, kind)
|
||||
t = newRTPTransceiver(receiver, nil, localDirection, kind, pc.api)
|
||||
pc.mu.Lock()
|
||||
pc.addRTPTransceiver(t)
|
||||
pc.mu.Unlock()
|
||||
@@ -1634,7 +1634,7 @@ func (pc *PeerConnection) newTransceiverFromTrack(direction RTPTransceiverDirect
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return newRTPTransceiver(r, s, direction, track.Kind()), nil
|
||||
return newRTPTransceiver(r, s, direction, track.Kind(), pc.api), nil
|
||||
}
|
||||
|
||||
// AddTransceiverFromKind Create a new RtpTransceiver and adds it to the set of transceivers.
|
||||
@@ -1668,7 +1668,7 @@ func (pc *PeerConnection) AddTransceiverFromKind(kind RTPCodecType, init ...RTPT
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t = newRTPTransceiver(receiver, nil, RTPTransceiverDirectionRecvonly, kind)
|
||||
t = newRTPTransceiver(receiver, nil, RTPTransceiverDirectionRecvonly, kind, pc.api)
|
||||
default:
|
||||
return nil, errPeerConnAddTransceiverFromKindSupport
|
||||
}
|
||||
@@ -2208,7 +2208,7 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use
|
||||
t, localTransceivers = satisfyTypeAndDirection(kind, direction, localTransceivers)
|
||||
if t == nil {
|
||||
if len(mediaTransceivers) == 0 {
|
||||
t = &RTPTransceiver{kind: kind}
|
||||
t = &RTPTransceiver{kind: kind, api: pc.api, codecs: pc.api.mediaEngine.getCodecsByKind(kind)}
|
||||
t.setDirection(RTPTransceiverDirectionInactive)
|
||||
mediaTransceivers = append(mediaTransceivers, t)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ type RTPReceiver struct {
|
||||
closed, received chan interface{}
|
||||
mu sync.RWMutex
|
||||
|
||||
tr *RTPTransceiver
|
||||
|
||||
// A reference to the associated api object
|
||||
api *API
|
||||
}
|
||||
@@ -60,6 +62,12 @@ func (api *API) NewRTPReceiver(kind RTPCodecType, transport *DTLSTransport) (*RT
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *RTPReceiver) setRTPTransceiver(tr *RTPTransceiver) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.tr = tr
|
||||
}
|
||||
|
||||
// Transport returns the currently-configured *DTLSTransport or nil
|
||||
// if one has not yet been configured
|
||||
func (r *RTPReceiver) Transport() *DTLSTransport {
|
||||
@@ -68,10 +76,18 @@ func (r *RTPReceiver) Transport() *DTLSTransport {
|
||||
return r.transport
|
||||
}
|
||||
|
||||
func (r *RTPReceiver) getParameters() RTPParameters {
|
||||
parameters := r.api.mediaEngine.getRTPParametersByKind(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
|
||||
parameters.Codecs = r.tr.getCodecs()
|
||||
return parameters
|
||||
}
|
||||
|
||||
// GetParameters describes the current configuration for the encoding and
|
||||
// transmission of media on the receiver's track.
|
||||
func (r *RTPReceiver) GetParameters() RTPParameters {
|
||||
return r.api.mediaEngine.getRTPParametersByKind(r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly})
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.getParameters()
|
||||
}
|
||||
|
||||
// Track returns the RtpTransceiver TrackRemote
|
||||
@@ -119,7 +135,7 @@ func (r *RTPReceiver) Receive(parameters RTPReceiveParameters) error {
|
||||
),
|
||||
}
|
||||
|
||||
globalParams := r.GetParameters()
|
||||
globalParams := r.getParameters()
|
||||
codec := RTPCodecCapability{}
|
||||
if len(globalParams.Codecs) != 0 {
|
||||
codec = globalParams.Codecs[0].RTPCodecCapability
|
||||
|
||||
12
rtpsender.go
12
rtpsender.go
@@ -38,6 +38,8 @@ type RTPSender struct {
|
||||
api *API
|
||||
id string
|
||||
|
||||
tr *RTPTransceiver
|
||||
|
||||
mu sync.RWMutex
|
||||
sendCalled, stopCalled chan struct{}
|
||||
}
|
||||
@@ -88,6 +90,12 @@ func (r *RTPSender) setNegotiated() {
|
||||
r.negotiated = true
|
||||
}
|
||||
|
||||
func (r *RTPSender) setRTPTransceiver(tr *RTPTransceiver) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.tr = tr
|
||||
}
|
||||
|
||||
// Transport returns the currently-configured *DTLSTransport or nil
|
||||
// if one has not yet been configured
|
||||
func (r *RTPSender) Transport() *DTLSTransport {
|
||||
@@ -99,7 +107,7 @@ func (r *RTPSender) Transport() *DTLSTransport {
|
||||
// GetParameters describes the current configuration for the encoding and
|
||||
// transmission of media on the sender's track.
|
||||
func (r *RTPSender) GetParameters() RTPSendParameters {
|
||||
return RTPSendParameters{
|
||||
sendParameters := RTPSendParameters{
|
||||
RTPParameters: r.api.mediaEngine.getRTPParametersByKind(
|
||||
r.track.Kind(),
|
||||
[]RTPTransceiverDirection{RTPTransceiverDirectionSendonly},
|
||||
@@ -113,6 +121,8 @@ func (r *RTPSender) GetParameters() RTPSendParameters {
|
||||
},
|
||||
},
|
||||
}
|
||||
sendParameters.Codecs = r.tr.getCodecs()
|
||||
return sendParameters
|
||||
}
|
||||
|
||||
// Track returns the RTCRtpTransceiver track, or nil
|
||||
|
||||
@@ -4,6 +4,7 @@ package webrtc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/pion/rtp"
|
||||
@@ -16,8 +17,13 @@ type RTPTransceiver struct {
|
||||
receiver atomic.Value // *RTPReceiver
|
||||
direction atomic.Value // RTPTransceiverDirection
|
||||
|
||||
codecs []RTPCodecParameters // User provided codecs via SetCodecPreferences
|
||||
|
||||
stopped bool
|
||||
kind RTPCodecType
|
||||
|
||||
api *API
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func newRTPTransceiver(
|
||||
@@ -25,14 +31,51 @@ func newRTPTransceiver(
|
||||
sender *RTPSender,
|
||||
direction RTPTransceiverDirection,
|
||||
kind RTPCodecType,
|
||||
api *API,
|
||||
) *RTPTransceiver {
|
||||
t := &RTPTransceiver{kind: kind}
|
||||
t := &RTPTransceiver{kind: kind, api: api}
|
||||
t.setReceiver(receiver)
|
||||
t.setSender(sender)
|
||||
t.setDirection(direction)
|
||||
return t
|
||||
}
|
||||
|
||||
// SetCodecPreferences sets preferred list of supported codecs
|
||||
// if codecs is empty or nil we reset to default from MediaEngine
|
||||
func (t *RTPTransceiver) SetCodecPreferences(codecs []RTPCodecParameters) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
for _, codec := range codecs {
|
||||
if _, matchType := codecParametersFuzzySearch(codec, t.api.mediaEngine.getCodecsByKind(t.kind)); matchType == codecMatchNone {
|
||||
return fmt.Errorf("%w %s", errRTPTransceiverCodecUnsupported, codec.MimeType)
|
||||
}
|
||||
}
|
||||
|
||||
t.codecs = codecs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Codecs returns list of supported codecs
|
||||
func (t *RTPTransceiver) getCodecs() []RTPCodecParameters {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
|
||||
mediaEngineCodecs := t.api.mediaEngine.getCodecsByKind(t.kind)
|
||||
if len(t.codecs) == 0 {
|
||||
return mediaEngineCodecs
|
||||
}
|
||||
|
||||
filteredCodecs := []RTPCodecParameters{}
|
||||
for _, codec := range t.codecs {
|
||||
if c, matchType := codecParametersFuzzySearch(codec, mediaEngineCodecs); matchType != codecMatchNone {
|
||||
filteredCodecs = append(filteredCodecs, c)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredCodecs
|
||||
}
|
||||
|
||||
// Sender returns the RTPTransceiver's RTPSender if it has one
|
||||
func (t *RTPTransceiver) Sender() *RTPSender {
|
||||
if v := t.sender.Load(); v != nil {
|
||||
@@ -49,6 +92,14 @@ func (t *RTPTransceiver) SetSender(s *RTPSender, track TrackLocal) error {
|
||||
}
|
||||
|
||||
func (t *RTPTransceiver) setSender(s *RTPSender) {
|
||||
if s != nil {
|
||||
s.setRTPTransceiver(t)
|
||||
}
|
||||
|
||||
if prevSender := t.Sender(); prevSender != nil {
|
||||
prevSender.setRTPTransceiver(nil)
|
||||
}
|
||||
|
||||
t.sender.Store(s)
|
||||
}
|
||||
|
||||
@@ -106,6 +157,14 @@ func (t *RTPTransceiver) Stop() error {
|
||||
}
|
||||
|
||||
func (t *RTPTransceiver) setReceiver(r *RTPReceiver) {
|
||||
if r != nil {
|
||||
r.setRTPTransceiver(t)
|
||||
}
|
||||
|
||||
if prevReceiver := t.Receiver(); prevReceiver != nil {
|
||||
prevReceiver.setRTPTransceiver(nil)
|
||||
}
|
||||
|
||||
t.receiver.Store(r)
|
||||
}
|
||||
|
||||
|
||||
133
rtptransceiver_test.go
Normal file
133
rtptransceiver_test.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// +build !js
|
||||
|
||||
package webrtc
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_RTPTransceiver_SetCodecPreferences(t *testing.T) {
|
||||
me := &MediaEngine{}
|
||||
api := NewAPI(WithMediaEngine(me))
|
||||
assert.NoError(t, me.RegisterDefaultCodecs())
|
||||
|
||||
me.pushCodecs(me.videoCodecs, RTPCodecTypeVideo)
|
||||
me.pushCodecs(me.audioCodecs, RTPCodecTypeAudio)
|
||||
|
||||
tr := RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: me.videoCodecs}
|
||||
assert.EqualValues(t, me.videoCodecs, tr.getCodecs())
|
||||
|
||||
failTestCases := [][]RTPCodecParameters{
|
||||
{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil},
|
||||
PayloadType: 111,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
|
||||
PayloadType: 96,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeOpus, 48000, 2, "minptime=10;useinbandfec=1", nil},
|
||||
PayloadType: 111,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range failTestCases {
|
||||
assert.Error(t, tr.SetCodecPreferences(testCase), errRTPTransceiverCodecUnsupported)
|
||||
}
|
||||
|
||||
successTestCases := [][]RTPCodecParameters{
|
||||
{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
|
||||
PayloadType: 96,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
|
||||
PayloadType: 96,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil},
|
||||
PayloadType: 97,
|
||||
},
|
||||
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP9, 90000, 0, "profile-id=0", nil},
|
||||
PayloadType: 98,
|
||||
},
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil},
|
||||
PayloadType: 99,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range successTestCases {
|
||||
assert.NoError(t, tr.SetCodecPreferences(testCase))
|
||||
}
|
||||
|
||||
assert.NoError(t, tr.SetCodecPreferences(nil))
|
||||
assert.NotEqual(t, 0, len(tr.getCodecs()))
|
||||
|
||||
assert.NoError(t, tr.SetCodecPreferences([]RTPCodecParameters{}))
|
||||
assert.NotEqual(t, 0, len(tr.getCodecs()))
|
||||
}
|
||||
|
||||
// Assert that SetCodecPreferences properly filters codecs and PayloadTypes are respected
|
||||
func Test_RTPTransceiver_SetCodecPreferences_PayloadType(t *testing.T) {
|
||||
testCodec := RTPCodecParameters{
|
||||
RTPCodecCapability: RTPCodecCapability{"video/testCodec", 90000, 0, "", nil},
|
||||
PayloadType: 50,
|
||||
}
|
||||
|
||||
m := &MediaEngine{}
|
||||
assert.NoError(t, m.RegisterDefaultCodecs())
|
||||
|
||||
offerPC, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, m.RegisterCodec(testCodec, RTPCodecTypeVideo))
|
||||
|
||||
answerPC, err := NewAPI(WithMediaEngine(m)).NewPeerConnection(Configuration{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = offerPC.AddTransceiverFromKind(RTPCodecTypeVideo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
answerTransceiver, err := answerPC.AddTransceiverFromKind(RTPCodecTypeVideo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, answerTransceiver.SetCodecPreferences([]RTPCodecParameters{
|
||||
testCodec,
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
|
||||
PayloadType: 51,
|
||||
},
|
||||
}))
|
||||
|
||||
offer, err := offerPC.CreateOffer(nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, offerPC.SetLocalDescription(offer))
|
||||
assert.NoError(t, answerPC.SetRemoteDescription(offer))
|
||||
|
||||
answer, err := answerPC.CreateAnswer(nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// VP8 with proper PayloadType
|
||||
assert.NotEqual(t, -1, strings.Index(answer.SDP, "a=rtpmap:96 VP8/90000"))
|
||||
|
||||
// testCodec is ignored since offerer doesn't support
|
||||
assert.Equal(t, -1, strings.Index(answer.SDP, "testCodec"))
|
||||
|
||||
closePairNow(t, offerPC, answerPC)
|
||||
}
|
||||
2
sdp.go
2
sdp.go
@@ -292,7 +292,7 @@ func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates b
|
||||
WithPropertyAttribute(sdp.AttrKeyRTCPMux).
|
||||
WithPropertyAttribute(sdp.AttrKeyRTCPRsize)
|
||||
|
||||
codecs := mediaEngine.getCodecsByKind(t.kind)
|
||||
codecs := t.getCodecs()
|
||||
for _, codec := range codecs {
|
||||
name := strings.TrimPrefix(codec.MimeType, "audio/")
|
||||
name = strings.TrimPrefix(name, "video/")
|
||||
|
||||
69
sdp_test.go
69
sdp_test.go
@@ -307,6 +307,8 @@ func TestMediaDescriptionFingerprints(t *testing.T) {
|
||||
engine := &MediaEngine{}
|
||||
assert.NoError(t, engine.RegisterDefaultCodecs())
|
||||
|
||||
api := NewAPI(WithMediaEngine(engine))
|
||||
|
||||
sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -317,13 +319,17 @@ func TestMediaDescriptionFingerprints(t *testing.T) {
|
||||
{
|
||||
id: "video",
|
||||
transceivers: []*RTPTransceiver{{
|
||||
kind: RTPCodecTypeVideo,
|
||||
kind: RTPCodecTypeVideo,
|
||||
api: api,
|
||||
codecs: engine.getCodecsByKind(RTPCodecTypeVideo),
|
||||
}},
|
||||
},
|
||||
{
|
||||
id: "audio",
|
||||
transceivers: []*RTPTransceiver{{
|
||||
kind: RTPCodecTypeAudio,
|
||||
kind: RTPCodecTypeAudio,
|
||||
api: api,
|
||||
codecs: engine.getCodecsByKind(RTPCodecTypeAudio),
|
||||
}},
|
||||
},
|
||||
{
|
||||
@@ -363,21 +369,22 @@ func TestMediaDescriptionFingerprints(t *testing.T) {
|
||||
|
||||
func TestPopulateSDP(t *testing.T) {
|
||||
t.Run("Rid", func(t *testing.T) {
|
||||
tr := &RTPTransceiver{kind: RTPCodecTypeVideo}
|
||||
se := SettingEngine{}
|
||||
|
||||
me := &MediaEngine{}
|
||||
assert.NoError(t, me.RegisterDefaultCodecs())
|
||||
api := NewAPI(WithMediaEngine(me))
|
||||
|
||||
tr := &RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: me.videoCodecs}
|
||||
tr.setDirection(RTPTransceiverDirectionRecvonly)
|
||||
ridMap := map[string]string{
|
||||
"ridkey": "some",
|
||||
}
|
||||
mediaSections := []mediaSection{{id: "video", transceivers: []*RTPTransceiver{tr}, ridMap: ridMap}}
|
||||
|
||||
se := SettingEngine{}
|
||||
|
||||
m := MediaEngine{}
|
||||
assert.NoError(t, m.RegisterDefaultCodecs())
|
||||
|
||||
d := &sdp.SessionDescription{}
|
||||
|
||||
offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, &m, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete)
|
||||
offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Test contains rid map keys
|
||||
@@ -397,6 +404,50 @@ func TestPopulateSDP(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, true, found, "Rid key should be present")
|
||||
})
|
||||
t.Run("SetCodecPreferences", func(t *testing.T) {
|
||||
se := SettingEngine{}
|
||||
|
||||
me := &MediaEngine{}
|
||||
assert.NoError(t, me.RegisterDefaultCodecs())
|
||||
api := NewAPI(WithMediaEngine(me))
|
||||
me.pushCodecs(me.videoCodecs, RTPCodecTypeVideo)
|
||||
me.pushCodecs(me.audioCodecs, RTPCodecTypeAudio)
|
||||
|
||||
tr := &RTPTransceiver{kind: RTPCodecTypeVideo, api: api, codecs: me.videoCodecs}
|
||||
tr.setDirection(RTPTransceiverDirectionRecvonly)
|
||||
codecErr := tr.SetCodecPreferences([]RTPCodecParameters{
|
||||
{
|
||||
RTPCodecCapability: RTPCodecCapability{MimeTypeVP8, 90000, 0, "", nil},
|
||||
PayloadType: 96,
|
||||
},
|
||||
})
|
||||
assert.NoError(t, codecErr)
|
||||
|
||||
mediaSections := []mediaSection{{id: "video", transceivers: []*RTPTransceiver{tr}}}
|
||||
|
||||
d := &sdp.SessionDescription{}
|
||||
|
||||
offerSdp, err := populateSDP(d, false, []DTLSFingerprint{}, se.sdpMediaLevelFingerprints, se.candidates.ICELite, me, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), []ICECandidate{}, ICEParameters{}, mediaSections, ICEGatheringStateComplete)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Test codecs
|
||||
foundVP8 := false
|
||||
for _, desc := range offerSdp.MediaDescriptions {
|
||||
if desc.MediaName.Media != "video" {
|
||||
continue
|
||||
}
|
||||
for _, a := range desc.Attributes {
|
||||
if strings.Contains(a.Key, "rtpmap") {
|
||||
if a.Value == "98 VP9/90000" {
|
||||
t.Fatal("vp9 should not be present in sdp")
|
||||
} else if a.Value == "96 VP8/90000" {
|
||||
foundVP8 = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundVP8, "vp8 should be present in sdp")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRIDs(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user