Factor out an API object

Relates to #333
This commit is contained in:
Woodrow Douglass
2019-01-02 12:44:43 -05:00
committed by Michiel De Backker
parent bd7c6ffc25
commit e906728df3
9 changed files with 103 additions and 67 deletions

19
apiobject.go Normal file
View File

@@ -0,0 +1,19 @@
package webrtc
// API is a repository for semi-global settings to WebRTC objects
// In the simplest case, the DefaultAPI object should be used
// rather then constructing a new API object.
type API struct {
settingEngine settingEngine
mediaEngine MediaEngine
}
var defaultAPI = NewAPI()
// NewAPI Creates a new API object for keeping semi-global settings to WebRTC objects
func NewAPI() *API {
a := new(API)
initSettingEngine(&a.settingEngine)
InitMediaEngine(&a.mediaEngine)
return a
}

View File

@@ -11,7 +11,7 @@ import (
// RegisterCodec is used to register a codec with the DefaultMediaEngine
func RegisterCodec(codec *RTCRtpCodec) {
DefaultMediaEngine.RegisterCodec(codec)
defaultAPI.mediaEngine.RegisterCodec(codec)
}
// TODO: Phase out DefaultPayloadTypes in favor or dynamic assignment in 96-127 range
@@ -26,20 +26,22 @@ const (
)
// RegisterDefaultCodecs is a helper that registers the default codecs supported by pions-webrtc
func RegisterDefaultCodecs() {
RegisterCodec(NewRTCRtpOpusCodec(DefaultPayloadTypeOpus, 48000, 2))
RegisterCodec(NewRTCRtpG722Codec(DefaultPayloadTypeG722, 8000))
RegisterCodec(NewRTCRtpVP8Codec(DefaultPayloadTypeVP8, 90000))
RegisterCodec(NewRTCRtpH264Codec(DefaultPayloadTypeH264, 90000))
RegisterCodec(NewRTCRtpVP9Codec(DefaultPayloadTypeVP9, 90000))
func (api *API) RegisterDefaultCodecs() {
api.mediaEngine.RegisterCodec(NewRTCRtpOpusCodec(DefaultPayloadTypeOpus, 48000, 2))
api.mediaEngine.RegisterCodec(NewRTCRtpG722Codec(DefaultPayloadTypeG722, 8000))
api.mediaEngine.RegisterCodec(NewRTCRtpVP8Codec(DefaultPayloadTypeVP8, 90000))
api.mediaEngine.RegisterCodec(NewRTCRtpH264Codec(DefaultPayloadTypeH264, 90000))
api.mediaEngine.RegisterCodec(NewRTCRtpVP9Codec(DefaultPayloadTypeVP9, 90000))
}
// DefaultMediaEngine is the default MediaEngine used by RTCPeerConnections
var DefaultMediaEngine = NewMediaEngine()
// RegisterDefaultCodecs calls the above on the default api object.
func RegisterDefaultCodecs() {
defaultAPI.RegisterDefaultCodecs()
}
// NewMediaEngine creates a new MediaEngine
func NewMediaEngine() *MediaEngine {
return &MediaEngine{}
// InitMediaEngine initializes an empty media engine object.
func InitMediaEngine(m *MediaEngine) {
*m = MediaEngine{}
}
// MediaEngine defines the codecs supported by a RTCPeerConnection

View File

@@ -102,7 +102,14 @@ type RTCDataChannel struct {
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
func NewRTCDataChannel(transport *RTCSctpTransport, params *RTCDataChannelParameters) (*RTCDataChannel, error) {
d, err := newRTCDataChannel(params, defaultSettingEngine)
return defaultAPI.NewRTCDataChannel(transport, params)
}
// NewRTCDataChannel creates a new RTCDataChannel.
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
func (api *API) NewRTCDataChannel(transport *RTCSctpTransport, params *RTCDataChannelParameters) (*RTCDataChannel, error) {
d, err := api.newRTCDataChannel(params)
if err != nil {
return nil, err
}
@@ -117,7 +124,7 @@ func NewRTCDataChannel(transport *RTCSctpTransport, params *RTCDataChannelParame
// newRTCDataChannel is an internal constructor for the data channel used to
// create the RTCDataChannel object before the networking is set up.
func newRTCDataChannel(params *RTCDataChannelParameters, settingEngine *settingEngine) (*RTCDataChannel, error) {
func (api *API) newRTCDataChannel(params *RTCDataChannelParameters) (*RTCDataChannel, error) {
// https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #5)
if len(params.Label) > 65535 {
return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit}
@@ -127,7 +134,7 @@ func newRTCDataChannel(params *RTCDataChannelParameters, settingEngine *settingE
Label: params.Label,
ID: &params.ID,
ReadyState: RTCDataChannelStateConnecting,
settingEngine: settingEngine,
settingEngine: &api.settingEngine,
}
return d, nil

View File

@@ -17,16 +17,16 @@ func TestGenerateDataChannelID(t *testing.T) {
c *RTCPeerConnection
result uint16
}{
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{}, settingEngine: defaultSettingEngine}, 0},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil}, settingEngine: defaultSettingEngine}, 0},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil}, settingEngine: defaultSettingEngine}, 2},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil, 2: nil}, settingEngine: defaultSettingEngine}, 4},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil, 4: nil}, settingEngine: defaultSettingEngine}, 2},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{}, settingEngine: defaultSettingEngine}, 1},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil}, settingEngine: defaultSettingEngine}, 1},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil}, settingEngine: defaultSettingEngine}, 3},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil, 3: nil}, settingEngine: defaultSettingEngine}, 5},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil, 5: nil}, settingEngine: defaultSettingEngine}, 3},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{}, api: defaultAPI}, 0},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil}, api: defaultAPI}, 0},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil}, api: defaultAPI}, 2},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil, 2: nil}, api: defaultAPI}, 4},
{true, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil, 4: nil}, api: defaultAPI}, 2},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{}, api: defaultAPI}, 1},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{0: nil}, api: defaultAPI}, 1},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil}, api: defaultAPI}, 3},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil, 3: nil}, api: defaultAPI}, 5},
{false, &RTCPeerConnection{sctpTransport: NewRTCSctpTransport(nil), dataChannels: map[uint16]*RTCDataChannel{1: nil, 5: nil}, api: defaultAPI}, 3},
}
for _, testCase := range testCases {
@@ -42,7 +42,7 @@ func TestGenerateDataChannelID(t *testing.T) {
}
func TestRTCDataChannel_EventHandlers(t *testing.T) {
dc := &RTCDataChannel{settingEngine: defaultSettingEngine}
dc := &RTCDataChannel{settingEngine: &defaultAPI.settingEngine}
onOpenCalled := make(chan bool)
onMessageCalled := make(chan bool)
@@ -84,7 +84,7 @@ func TestRTCDataChannel_EventHandlers(t *testing.T) {
}
func TestRTCDataChannel_MessagesAreOrdered(t *testing.T) {
dc := &RTCDataChannel{settingEngine: defaultSettingEngine}
dc := &RTCDataChannel{settingEngine: &defaultAPI.settingEngine}
max := 512
out := make(chan int)

View File

@@ -25,7 +25,7 @@ type RTCIceGatherer struct {
// NewRTCIceGatherer creates a new NewRTCIceGatherer.
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
func NewRTCIceGatherer(opts RTCIceGatherOptions) (*RTCIceGatherer, error) {
func (api *API) NewRTCIceGatherer(opts RTCIceGatherOptions) (*RTCIceGatherer, error) {
validatedServers := []*ice.URL{}
if len(opts.ICEServers) > 0 {
for _, server := range opts.ICEServers {
@@ -40,10 +40,15 @@ func NewRTCIceGatherer(opts RTCIceGatherOptions) (*RTCIceGatherer, error) {
return &RTCIceGatherer{
state: RTCIceGathererStateNew,
validatedServers: validatedServers,
settingEngine: defaultSettingEngine,
settingEngine: &api.settingEngine,
}, nil
}
// NewRTCIceGatherer does the same as above, except with the default API object
func NewRTCIceGatherer(opts RTCIceGatherOptions) (*RTCIceGatherer, error) {
return defaultAPI.NewRTCIceGatherer(opts)
}
// State indicates the current state of the ICE gatherer.
func (g *RTCIceGatherer) State() RTCIceGathererState {
g.lock.RLock()

View File

@@ -96,8 +96,6 @@ type RTCPeerConnection struct {
lastOffer string
lastAnswer string
// Media
mediaEngine *MediaEngine
rtpTransceivers []*RTCRtpTransceiver
// DataChannels
@@ -126,11 +124,12 @@ type RTCPeerConnection struct {
srtcpSession *srtp.SessionSRTCP
srtcpEndpoint *mux.Endpoint
// A reference to the associated setting engine used by this peerconnection
settingEngine *settingEngine
// A reference to the associated API state used by this connection
api *API
}
func newPC(settings *settingEngine, configuration RTCConfiguration) (*RTCPeerConnection, error) {
// New creates a new RTCPeerConfiguration with the provided configuration against the received API object
func (api *API) New(configuration RTCConfiguration) (*RTCPeerConnection, error) {
// https://w3c.github.io/webrtc-pc/#constructor (Step #2)
// Some variables defined explicitly despite their implicit zero values to
// allow better readability to understand what is happening.
@@ -152,11 +151,12 @@ func newPC(settings *settingEngine, configuration RTCConfiguration) (*RTCPeerCon
IceConnectionState: ice.ConnectionStateNew, // FIXME REMOVE
IceGatheringState: RTCIceGatheringStateNew,
ConnectionState: RTCPeerConnectionStateNew,
mediaEngine: DefaultMediaEngine,
dataChannels: make(map[uint16]*RTCDataChannel),
srtpSession: srtp.CreateSessionSRTP(),
srtcpSession: srtp.CreateSessionSRTCP(),
settingEngine: settings,
api: api,
}
var err error
@@ -182,7 +182,7 @@ func newPC(settings *settingEngine, configuration RTCConfiguration) (*RTCPeerCon
// New creates a new RTCPeerConfiguration with the provided configuration
func New(configuration RTCConfiguration) (*RTCPeerConnection, error) {
return newPC(defaultSettingEngine, configuration)
return defaultAPI.New(configuration)
}
// initConfiguration defines validation of the specified RTCConfiguration and
@@ -1282,7 +1282,7 @@ func (pc *RTCPeerConnection) CreateDataChannel(label string, options *RTCDataCha
}
*/
d, err := newRTCDataChannel(params, pc.settingEngine)
d, err := pc.api.newRTCDataChannel(params)
if err != nil {
return nil, err
}
@@ -1321,12 +1321,6 @@ func (pc *RTCPeerConnection) generateDataChannelID(client bool) (uint16, error)
return 0, &rtcerr.OperationError{Err: ErrMaxDataChannelID}
}
// SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
// This enables RTCPeerConnection with support for different codecs
func (pc *RTCPeerConnection) SetMediaEngine(m *MediaEngine) {
pc.mediaEngine = m
}
// SetIdentityProvider is used to configure an identity provider to generate identity assertions
func (pc *RTCPeerConnection) SetIdentityProvider(provider string) error {
return errors.Errorf("TODO SetIdentityProvider")
@@ -1448,7 +1442,7 @@ func (pc *RTCPeerConnection) generateChannel(h *rtp.Header) (chan *rtp.Packet, c
return nil, nil, fmt.Errorf("no codec could be found in RemoteDescription for payloadType %d", h.PayloadType)
}
codec, err := pc.mediaEngine.getCodecSDP(sdpCodec)
codec, err := pc.api.mediaEngine.getCodecSDP(sdpCodec)
if err != nil {
return nil, nil, fmt.Errorf("codec %s in not registered", sdpCodec)
}
@@ -1501,7 +1495,7 @@ func (pc *RTCPeerConnection) addFingerprint(d *sdp.SessionDescription) {
}
func (pc *RTCPeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codecType RTCRtpCodecType, midValue string, iceParams RTCIceParameters, peerDirection RTCRtpTransceiverDirection, candidates []RTCIceCandidate, dtlsRole sdp.ConnectionRole) bool {
if codecs := pc.mediaEngine.getCodecsByKind(codecType); len(codecs) == 0 {
if codecs := pc.api.mediaEngine.getCodecsByKind(codecType); len(codecs) == 0 {
return false
}
media := sdp.NewJSEPMediaDescription(codecType.String(), []string{}).
@@ -1511,7 +1505,7 @@ func (pc *RTCPeerConnection) addRTPMediaSection(d *sdp.SessionDescription, codec
WithPropertyAttribute(sdp.AttrKeyRtcpMux). // TODO: support RTCP fallback
WithPropertyAttribute(sdp.AttrKeyRtcpRsize) // TODO: Support Reduced-Size RTCP?
for _, codec := range pc.mediaEngine.getCodecsByKind(codecType) {
for _, codec := range pc.api.mediaEngine.getCodecsByKind(codecType) {
media.WithCodec(codec.PayloadType, codec.Name, codec.ClockRate, codec.Channels, codec.SdpFmtpLine)
}
@@ -1590,7 +1584,7 @@ func (pc *RTCPeerConnection) sendRTP(packet *rtp.Packet) {
}
func (pc *RTCPeerConnection) newRTCTrack(payloadType uint8, ssrc uint32, id, label string) (*RTCTrack, error) {
codec, err := pc.mediaEngine.getCodec(payloadType)
codec, err := pc.api.mediaEngine.getCodec(payloadType)
if err != nil {
return nil, err
} else if codec.Payloader == nil {

View File

@@ -57,6 +57,7 @@ func signalPair(pcOffer *RTCPeerConnection, pcAnswer *RTCPeerConnection) error {
}
func TestNew(t *testing.T) {
api := NewAPI()
t.Run("Success", func(t *testing.T) {
secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
@@ -64,7 +65,7 @@ func TestNew(t *testing.T) {
certificate, err := GenerateCertificate(secretKey)
assert.Nil(t, err)
pc, err := New(RTCConfiguration{
pc, err := api.New(RTCConfiguration{
IceServers: []RTCIceServer{
{
URLs: []string{
@@ -106,12 +107,12 @@ func TestNew(t *testing.T) {
})
assert.Nil(t, err)
return New(RTCConfiguration{
return api.New(RTCConfiguration{
Certificates: []RTCCertificate{*certificate},
})
}, &rtcerr.InvalidAccessError{Err: ErrCertificateExpired}},
{func() (*RTCPeerConnection, error) {
return New(RTCConfiguration{
return api.New(RTCConfiguration{
IceServers: []RTCIceServer{
{
URLs: []string{
@@ -135,6 +136,7 @@ func TestNew(t *testing.T) {
}
func TestRTCPeerConnection_SetConfiguration(t *testing.T) {
api := NewAPI()
t.Run("Success", func(t *testing.T) {
secretKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
@@ -142,7 +144,7 @@ func TestRTCPeerConnection_SetConfiguration(t *testing.T) {
certificate, err := GenerateCertificate(secretKey)
assert.Nil(t, err)
pc, err := New(RTCConfiguration{
pc, err := api.New(RTCConfiguration{
PeerIdentity: "unittest",
Certificates: []RTCCertificate{*certificate},
IceCandidatePoolSize: 5,
@@ -180,7 +182,7 @@ func TestRTCPeerConnection_SetConfiguration(t *testing.T) {
expectedErr error
}{
{func() (*RTCPeerConnection, error) {
pc, err := New(RTCConfiguration{})
pc, err := api.New(RTCConfiguration{})
assert.Nil(t, err)
err = pc.Close()
@@ -190,7 +192,7 @@ func TestRTCPeerConnection_SetConfiguration(t *testing.T) {
return RTCConfiguration{}
}, &rtcerr.InvalidStateError{Err: ErrConnectionClosed}},
{func() (*RTCPeerConnection, error) {
return New(RTCConfiguration{})
return api.New(RTCConfiguration{})
}, func() RTCConfiguration {
return RTCConfiguration{
PeerIdentity: "unittest",
@@ -411,7 +413,8 @@ func TestRTCPeerConnection_NewRTCSampleTrack(t *testing.T) {
}
func TestRTCPeerConnection_EventHandlers(t *testing.T) {
pc, err := New(RTCConfiguration{})
api := NewAPI()
pc, err := api.New(RTCConfiguration{})
assert.Nil(t, err)
onTrackCalled := make(chan bool)
@@ -441,7 +444,7 @@ func TestRTCPeerConnection_EventHandlers(t *testing.T) {
// Verify that the set handlers are called
assert.NotPanics(t, func() { pc.onTrack(&RTCTrack{}) })
assert.NotPanics(t, func() { pc.onICEConnectionStateChange(ice.ConnectionStateNew) })
assert.NotPanics(t, func() { go pc.onDataChannelHandler(&RTCDataChannel{settingEngine: defaultSettingEngine}) })
assert.NotPanics(t, func() { go pc.onDataChannelHandler(&RTCDataChannel{settingEngine: &api.settingEngine}) })
allTrue := func(vals []bool) bool {
for _, val := range vals {

View File

@@ -35,16 +35,19 @@ type RTCSctpTransport struct {
association *sctp.Association
onDataChannelHandler func(*RTCDataChannel)
settingEngine *settingEngine
}
// NewRTCSctpTransport creates a new RTCSctpTransport.
// This constructor is part of the ORTC API. It is not
// meant to be used together with the basic WebRTC API.
func NewRTCSctpTransport(dtls *RTCDtlsTransport) *RTCSctpTransport {
func (api *API) NewRTCSctpTransport(dtls *RTCDtlsTransport) *RTCSctpTransport {
res := &RTCSctpTransport{
dtlsTransport: dtls,
State: RTCSctpTransportStateConnecting,
port: 5000, // TODO
settingEngine: &api.settingEngine,
}
res.updateMessageSize()
@@ -53,6 +56,11 @@ func NewRTCSctpTransport(dtls *RTCDtlsTransport) *RTCSctpTransport {
return res
}
// NewRTCSctpTransport does the same as above, except with the default API object
func NewRTCSctpTransport(dtls *RTCDtlsTransport) *RTCSctpTransport {
return defaultAPI.NewRTCSctpTransport(dtls)
}
// Transport returns the RTCDtlsTransport instance the RTCSctpTransport is sending over.
func (r *RTCSctpTransport) Transport() *RTCDtlsTransport {
r.lock.RLock()
@@ -137,7 +145,7 @@ func (r *RTCSctpTransport) acceptDataChannels() {
ID: &sid,
Label: dc.Config.Label,
ReadyState: RTCDataChannelStateOpen,
settingEngine: defaultSettingEngine,
settingEngine: r.settingEngine,
}
<-r.onDataChannel(rtcDC)

View File

@@ -6,26 +6,24 @@ import (
"github.com/pions/webrtc/pkg/ice"
)
var defaultSettingEngine = newSettingEngine()
// SetEphemeralUDPPortRange limits the pool of ephemeral ports that
// ICE UDP connections can allocate from. This setting currently only
// affects host candidates, not server reflexive candidates.
func SetEphemeralUDPPortRange(portMin, portMax uint16) error {
return defaultSettingEngine.SetEphemeralUDPPortRange(portMin, portMax)
return defaultAPI.settingEngine.SetEphemeralUDPPortRange(portMin, portMax)
}
// DetachDataChannels enables detaching data channels. When enabled
// data channels have to be detached in the OnOpen callback using the
// RTCDataChannel.Detach method.
func DetachDataChannels() {
defaultSettingEngine.DetachDataChannels()
defaultAPI.settingEngine.DetachDataChannels()
}
// SetConnectionTimeout sets the amount of silence needed on a given candidate pair
// before the ICE agent considers the pair timed out.
func SetConnectionTimeout(connectionTimeout, keepAlive time.Duration) {
defaultSettingEngine.SetConnectionTimeout(connectionTimeout, keepAlive)
defaultAPI.settingEngine.SetConnectionTimeout(connectionTimeout, keepAlive)
}
// settingEngine allows influencing behavior in ways that are not
@@ -72,6 +70,6 @@ func (e *settingEngine) SetEphemeralUDPPortRange(portMin, portMax uint16) error
return nil
}
func newSettingEngine() *settingEngine {
return new(settingEngine)
func initSettingEngine(s *settingEngine) {
*s = settingEngine{}
}