api: support a custom media engine

This commit is contained in:
backkem
2018-07-16 22:25:54 +02:00
committed by Sean DuBois
parent f7c5ecd57f
commit ab6910899c
10 changed files with 78 additions and 73 deletions

View File

@@ -67,7 +67,7 @@ var (
ErrModPeerIdentity = errors.New("peer identity cannot be modified") ErrModPeerIdentity = errors.New("peer identity cannot be modified")
ErrModCertificates = errors.New("certificates cannot be modified") ErrModCertificates = errors.New("certificates cannot be modified")
ErrModRtcpMuxPolicy = errors.New("rtcp mux policy cannot be modified") ErrModRtcpMuxPolicy = errors.New("rtcp mux policy cannot be modified")
ErrModIceCandidatePoolSize = errors.New("ice candidate pool size cannot be modified") ErrModICECandidatePoolSize = errors.New("ice candidate pool size cannot be modified")
) )
// InvalidModificationError indicates the object can not be modified in this way. // InvalidModificationError indicates the object can not be modified in this way.

View File

@@ -45,7 +45,7 @@ func main() {
} }
// Create a audio track // Create a audio track
opusTrack, err := peerConnection.NewRTCTrack(webrtc.PayloadTypeOpus, "audio", "pion1") opusTrack, err := peerConnection.NewRTCTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -55,7 +55,7 @@ func main() {
} }
// Create a video track // Create a video track
vp8Track, err := peerConnection.NewRTCTrack(webrtc.PayloadTypeVP8, "video", "pion2") vp8Track, err := peerConnection.NewRTCTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -28,8 +28,8 @@ func main() {
// Setup the codecs you want to use. // Setup the codecs you want to use.
// We'll use a VP8 codec but you can also define your own // We'll use a VP8 codec but you can also define your own
webrtc.RegisterCodec(webrtc.NewRTCRtpOpusCodec(webrtc.PayloadTypeOpus, 48000, 2)) webrtc.RegisterCodec(webrtc.NewRTCRtpOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000, 2))
webrtc.RegisterCodec(webrtc.NewRTCRtpVP8Codec(webrtc.PayloadTypeVP8, 90000)) webrtc.RegisterCodec(webrtc.NewRTCRtpVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000))
// Create a new RTCPeerConnection // Create a new RTCPeerConnection
peerConnection, err := webrtc.New(webrtc.RTCConfiguration{}) peerConnection, err := webrtc.New(webrtc.RTCConfiguration{})

View File

@@ -121,10 +121,10 @@ func (d *MediaDescription) WithCodec(payloadType uint8, name string, clockrate u
// WithMediaSource adds media source information to the media description // WithMediaSource adds media source information to the media description
func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription { func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription {
return d. return d.
WithValueAttribute("ssrc", fmt.Sprintf("%d cname:%s", ssrc, cname)). // Deprecated but not pased out? WithValueAttribute("ssrc", fmt.Sprintf("%d cname:%s", ssrc, cname)). // Deprecated but not phased out?
WithValueAttribute("ssrc", fmt.Sprintf("%d msid:%s %s", ssrc, streamLabel, label)). WithValueAttribute("ssrc", fmt.Sprintf("%d msid:%s %s", ssrc, streamLabel, label)).
WithValueAttribute("ssrc", fmt.Sprintf("%d mslabel:%s", ssrc, streamLabel)). // Deprecated but not pased out? WithValueAttribute("ssrc", fmt.Sprintf("%d mslabel:%s", ssrc, streamLabel)). // Deprecated but not phased out?
WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label)) // Deprecated but not pased out? WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label)) // Deprecated but not phased out?
} }
// WithCandidate adds an ICE candidate to the media description // WithCandidate adds an ICE candidate to the media description

View File

@@ -8,21 +8,6 @@ import (
"strings" "strings"
) )
// SessionBuilderTrack represents a single track in a SessionBuilder
type SessionBuilderTrack struct {
SSRC uint32
IsAudio bool
}
// SessionBuilder provides an easy way to build an SDP for an RTCPeerConnection
type SessionBuilder struct {
IceUsername, IcePassword, Fingerprint string
Candidates []string
Tracks []*SessionBuilderTrack
}
// ConnectionRole indicates which of the end points should initiate the connection establishment // ConnectionRole indicates which of the end points should initiate the connection establishment
type ConnectionRole int type ConnectionRole int

View File

@@ -133,7 +133,7 @@ type RTCTrack struct {
// NewRTCTrack is used to create a new RTCTrack // NewRTCTrack is used to create a new RTCTrack
func (r *RTCPeerConnection) NewRTCTrack(payloadType uint8, id, label string) (*RTCTrack, error) { func (r *RTCPeerConnection) NewRTCTrack(payloadType uint8, id, label string) (*RTCTrack, error) {
codec, err := rtcMediaEngine.getCodec(payloadType) codec, err := r.mediaEngine.getCodec(payloadType)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -9,48 +9,50 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// RegisterCodec is used to register a codec with the DefaultMediaEngine
func RegisterCodec(codec *RTCRtpCodec) {
DefaultMediaEngine.RegisterCodec(codec)
}
// TODO: Phase out DefaultPayloadTypes in favor or dynamic assignment in 96-127 range
// PayloadTypes for the default codecs // PayloadTypes for the default codecs
const ( const (
PayloadTypeOpus = 111 DefaultPayloadTypeOpus = 111
PayloadTypeVP8 = 96 DefaultPayloadTypeVP8 = 96
PayloadTypeVP9 = 98 DefaultPayloadTypeVP9 = 98
PayloadTypeH264 = 100 DefaultPayloadTypeH264 = 100
) )
// Names for the default codecs
const (
Opus = "opus"
VP8 = "VP8"
VP9 = "VP9"
H264 = "H264"
)
var rtcMediaEngine = &mediaEngine{}
// RegisterDefaultCodecs is a helper that registers the default codecs supported by pions-webrtc // RegisterDefaultCodecs is a helper that registers the default codecs supported by pions-webrtc
func RegisterDefaultCodecs() { func RegisterDefaultCodecs() {
RegisterCodec(NewRTCRtpOpusCodec(PayloadTypeOpus, 48000, 2)) RegisterCodec(NewRTCRtpOpusCodec(DefaultPayloadTypeOpus, 48000, 2))
RegisterCodec(NewRTCRtpVP8Codec(PayloadTypeVP8, 90000)) RegisterCodec(NewRTCRtpVP8Codec(DefaultPayloadTypeVP8, 90000))
RegisterCodec(NewRTCRtpH264Codec(PayloadTypeH264, 90000)) RegisterCodec(NewRTCRtpH264Codec(DefaultPayloadTypeH264, 90000))
RegisterCodec(NewRTCRtpVP9Codec(PayloadTypeVP9, 90000)) RegisterCodec(NewRTCRtpVP9Codec(DefaultPayloadTypeVP9, 90000))
} }
// RegisterCodec is used to register a codec // DefaultMediaEngine is the default MediaEngine used by RTCPeerConnections
func RegisterCodec(codec *RTCRtpCodec) { var DefaultMediaEngine = NewMediaEngine()
rtcMediaEngine.RegisterCodec(codec)
// NewMediaEngine creates a new MediaEngine
func NewMediaEngine() *MediaEngine {
return &MediaEngine{}
} }
type mediaEngine struct { // MediaEngine defines the codecs supported by a RTCPeerConnection
type MediaEngine struct {
codecs []*RTCRtpCodec codecs []*RTCRtpCodec
} }
func (m *mediaEngine) RegisterCodec(codec *RTCRtpCodec) uint8 { // RegisterCodec registers a codec to a media engine
func (m *MediaEngine) RegisterCodec(codec *RTCRtpCodec) uint8 {
// TODO: generate PayloadType if not set // TODO: generate PayloadType if not set
m.codecs = append(m.codecs, codec) m.codecs = append(m.codecs, codec)
return codec.PayloadType return codec.PayloadType
} }
func (m *mediaEngine) getCodec(payloadType uint8) (*RTCRtpCodec, error) { func (m *MediaEngine) getCodec(payloadType uint8) (*RTCRtpCodec, error) {
for _, codec := range m.codecs { for _, codec := range m.codecs {
if codec.PayloadType == payloadType { if codec.PayloadType == payloadType {
return codec, nil return codec, nil
@@ -59,7 +61,7 @@ func (m *mediaEngine) getCodec(payloadType uint8) (*RTCRtpCodec, error) {
return nil, errors.New("Codec not found") return nil, errors.New("Codec not found")
} }
func (m *mediaEngine) getCodecSDP(sdpCodec sdp.Codec) (*RTCRtpCodec, error) { func (m *MediaEngine) getCodecSDP(sdpCodec sdp.Codec) (*RTCRtpCodec, error) {
for _, codec := range m.codecs { for _, codec := range m.codecs {
if codec.Name == sdpCodec.Name && if codec.Name == sdpCodec.Name &&
codec.ClockRate == sdpCodec.ClockRate && codec.ClockRate == sdpCodec.ClockRate &&
@@ -72,9 +74,9 @@ func (m *mediaEngine) getCodecSDP(sdpCodec sdp.Codec) (*RTCRtpCodec, error) {
return nil, errors.New("Codec not found") return nil, errors.New("Codec not found")
} }
func (m *mediaEngine) getCodecsByKind(kind RTCRtpCodecType) []*RTCRtpCodec { func (m *MediaEngine) getCodecsByKind(kind RTCRtpCodecType) []*RTCRtpCodec {
var codecs []*RTCRtpCodec var codecs []*RTCRtpCodec
for _, codec := range rtcMediaEngine.codecs { for _, codec := range m.codecs {
if codec.Type == kind { if codec.Type == kind {
codecs = append(codecs, codec) codecs = append(codecs, codec)
} }
@@ -82,6 +84,14 @@ func (m *mediaEngine) getCodecsByKind(kind RTCRtpCodecType) []*RTCRtpCodec {
return codecs return codecs
} }
// Names for the default codecs supported by pions-webrtc
const (
Opus = "opus"
VP8 = "VP8"
VP9 = "VP9"
H264 = "H264"
)
// NewRTCRtpOpusCodec is a helper to create an Opus codec // NewRTCRtpOpusCodec is a helper to create an Opus codec
func NewRTCRtpOpusCodec(payloadType uint8, clockrate uint32, channels uint16) *RTCRtpCodec { func NewRTCRtpOpusCodec(payloadType uint8, clockrate uint32, channels uint16) *RTCRtpCodec {
c := NewRTCRtpCodec(RTCRtpCodecTypeAudio, c := NewRTCRtpCodec(RTCRtpCodecTypeAudio,

View File

@@ -231,7 +231,7 @@ func (r *RTCPeerConnection) validateICECandidatePoolSize(config RTCConfiguration
current := r.config current := r.config
if r.LocalDescription != nil && if r.LocalDescription != nil &&
config.ICECandidatePoolSize != current.ICECandidatePoolSize { config.ICECandidatePoolSize != current.ICECandidatePoolSize {
return &InvalidModificationError{Err: ErrModIceCandidatePoolSize} return &InvalidModificationError{Err: ErrModICECandidatePoolSize}
} }
return nil return nil
} }
@@ -265,22 +265,27 @@ func parseICEServer(server RTCICEServer, rawURL string) (ice.URL, error) {
return iceurl, &SyntaxError{Err: err} return iceurl, &SyntaxError{Err: err}
} }
_, isPass := server.Credential.(string)
_, isOauth := server.Credential.(RTCOAuthCredential)
noPass := !isPass && !isOauth
if iceurl.Type == ice.ServerTypeTURN { if iceurl.Type == ice.ServerTypeTURN {
if server.Username == "" || if server.Username == "" {
noPass {
return iceurl, &InvalidAccessError{Err: ErrNoTurnCred} return iceurl, &InvalidAccessError{Err: ErrNoTurnCred}
} }
if server.CredentialType == RTCICECredentialTypePassword &&
!isPass { switch t := server.Credential.(type) {
return iceurl, &InvalidAccessError{Err: ErrTurnCred} case string:
} if t == "" {
if server.CredentialType == RTCICECredentialTypeOauth && return iceurl, &InvalidAccessError{Err: ErrNoTurnCred}
!isOauth { } else if server.CredentialType != RTCICECredentialTypePassword {
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
}
case RTCOAuthCredential:
if server.CredentialType != RTCICECredentialTypeOauth {
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
}
default:
return iceurl, &InvalidAccessError{Err: ErrTurnCred} return iceurl, &InvalidAccessError{Err: ErrTurnCred}
} }
} }
return iceurl, nil return iceurl, nil

View File

@@ -100,13 +100,15 @@ type RTCPeerConnection struct {
connectionState RTCPeerConnectionState connectionState RTCPeerConnectionState
// Media // Media
mediaEngine *MediaEngine
rtpTransceivers []*RTCRtpTransceiver rtpTransceivers []*RTCRtpTransceiver
Ontrack func(*RTCTrack) Ontrack func(*RTCTrack)
} }
// Public
// New creates a new RTCPeerConfiguration with the provided configuration // New creates a new RTCPeerConfiguration with the provided configuration
func New(config RTCConfiguration) (*RTCPeerConnection, error) { func New(config RTCConfiguration) (*RTCPeerConnection, error) {
r := &RTCPeerConnection{ r := &RTCPeerConnection{
config: config, config: config,
signalingState: RTCSignalingStateStable, signalingState: RTCSignalingStateStable,
@@ -114,6 +116,7 @@ func New(config RTCConfiguration) (*RTCPeerConnection, error) {
iceGatheringState: ice.GatheringStateNew, iceGatheringState: ice.GatheringStateNew,
iceConnectionState: ice.ConnectionStateNew, iceConnectionState: ice.ConnectionStateNew,
connectionState: RTCPeerConnectionStateNew, connectionState: RTCPeerConnectionStateNew,
mediaEngine: DefaultMediaEngine,
} }
err := r.SetConfiguration(config) err := r.SetConfiguration(config)
if err != nil { if err != nil {
@@ -122,12 +125,14 @@ func New(config RTCConfiguration) (*RTCPeerConnection, error) {
r.tlscfg = dtls.NewTLSCfg() r.tlscfg = dtls.NewTLSCfg()
// TODO: Initialize ICE Agent
return r, nil return r, nil
} }
// Public // SetMediaEngine allows overwriting the default media engine used by the RTCPeerConnection
// This enables RTCPeerConnection with support for different codecs
func (r *RTCPeerConnection) SetMediaEngine(m *MediaEngine) {
r.mediaEngine = m
}
// SetIdentityProvider is used to configure an identity provider to generate identity assertions // SetIdentityProvider is used to configure an identity provider to generate identity assertions
func (r *RTCPeerConnection) SetIdentityProvider(provider string) error { func (r *RTCPeerConnection) SetIdentityProvider(provider string) error {
@@ -161,7 +166,7 @@ func (r *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buf
return nil return nil
} }
codec, err := rtcMediaEngine.getCodecSDP(sdpCodec) codec, err := r.mediaEngine.getCodecSDP(sdpCodec)
if err != nil { if err != nil {
fmt.Printf("Codec %s in not registered\n", sdpCodec) fmt.Printf("Codec %s in not registered\n", sdpCodec)
} }

View File

@@ -151,7 +151,7 @@ func (r *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) err
// RTCOfferOptions describes the options used to control the offer creation process // RTCOfferOptions describes the options used to control the offer creation process
type RTCOfferOptions struct { type RTCOfferOptions struct {
VoiceActivityDetection bool VoiceActivityDetection bool
IceRestart bool ICERestart bool
} }
// CreateOffer starts the RTCPeerConnection and generates the localDescription // CreateOffer starts the RTCPeerConnection and generates the localDescription
@@ -181,7 +181,7 @@ func (r *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDes
track := tranceiver.Sender.Track track := tranceiver.Sender.Track
cname := "pion" // TODO: Support RTP streams synchronisation cname := "pion" // TODO: Support RTP streams synchronisation
steamlabel := "pion" // TODO: Support steam labels steamlabel := "pion" // TODO: Support steam labels
codec, err := rtcMediaEngine.getCodec(track.PayloadType) codec, err := r.mediaEngine.getCodec(track.PayloadType)
if err != nil { if err != nil {
return RTCSessionDescription{}, err return RTCSessionDescription{}, err
} }
@@ -273,7 +273,7 @@ func (r *RTCPeerConnection) addAnswerMedia(d *sdp.SessionDescription, codecType
track := tranceiver.Sender.Track track := tranceiver.Sender.Track
cname := track.Label // TODO: Support RTP streams synchronisation cname := track.Label // TODO: Support RTP streams synchronisation
steamlabel := track.Label // TODO: Support steam labels steamlabel := track.Label // TODO: Support steam labels
codec, err := rtcMediaEngine.getCodec(track.PayloadType) codec, err := r.mediaEngine.getCodec(track.PayloadType)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -314,7 +314,7 @@ func (r *RTCPeerConnection) addAnswerMedia(d *sdp.SessionDescription, codecType
WithPropertyAttribute(sdp.AttrKeyRtcpMux). // TODO: support RTCP fallback WithPropertyAttribute(sdp.AttrKeyRtcpMux). // TODO: support RTCP fallback
WithPropertyAttribute(sdp.AttrKeyRtcpRsize) // TODO: Support Reduced-Size RTCP? WithPropertyAttribute(sdp.AttrKeyRtcpRsize) // TODO: Support Reduced-Size RTCP?
for _, codec := range rtcMediaEngine.getCodecsByKind(codecType) { for _, codec := range r.mediaEngine.getCodecsByKind(codecType) {
media.WithCodec( media.WithCodec(
codec.PayloadType, codec.PayloadType,
codec.Name, codec.Name,