mirror of
https://github.com/pion/webrtc.git
synced 2025-11-02 21:44:02 +08:00
api: support a custom media engine
This commit is contained in:
@@ -67,7 +67,7 @@ var (
|
||||
ErrModPeerIdentity = errors.New("peer identity cannot be modified")
|
||||
ErrModCertificates = errors.New("certificates 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.
|
||||
|
||||
@@ -45,7 +45,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Create a audio track
|
||||
opusTrack, err := peerConnection.NewRTCTrack(webrtc.PayloadTypeOpus, "audio", "pion1")
|
||||
opusTrack, err := peerConnection.NewRTCTrack(webrtc.DefaultPayloadTypeOpus, "audio", "pion1")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Create a video track
|
||||
vp8Track, err := peerConnection.NewRTCTrack(webrtc.PayloadTypeVP8, "video", "pion2")
|
||||
vp8Track, err := peerConnection.NewRTCTrack(webrtc.DefaultPayloadTypeVP8, "video", "pion2")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ func main() {
|
||||
|
||||
// Setup the codecs you want to use.
|
||||
// We'll use a VP8 codec but you can also define your own
|
||||
webrtc.RegisterCodec(webrtc.NewRTCRtpOpusCodec(webrtc.PayloadTypeOpus, 48000, 2))
|
||||
webrtc.RegisterCodec(webrtc.NewRTCRtpVP8Codec(webrtc.PayloadTypeVP8, 90000))
|
||||
webrtc.RegisterCodec(webrtc.NewRTCRtpOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000, 2))
|
||||
webrtc.RegisterCodec(webrtc.NewRTCRtpVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000))
|
||||
|
||||
// Create a new RTCPeerConnection
|
||||
peerConnection, err := webrtc.New(webrtc.RTCConfiguration{})
|
||||
|
||||
@@ -121,10 +121,10 @@ func (d *MediaDescription) WithCodec(payloadType uint8, name string, clockrate u
|
||||
// WithMediaSource adds media source information to the media description
|
||||
func (d *MediaDescription) WithMediaSource(ssrc uint32, cname, streamLabel, label string) *MediaDescription {
|
||||
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 mslabel:%s", ssrc, streamLabel)). // Deprecated but not pased out?
|
||||
WithValueAttribute("ssrc", fmt.Sprintf("%d label:%s", ssrc, label)) // 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 phased out?
|
||||
}
|
||||
|
||||
// WithCandidate adds an ICE candidate to the media description
|
||||
|
||||
@@ -8,21 +8,6 @@ import (
|
||||
"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
|
||||
type ConnectionRole int
|
||||
|
||||
|
||||
2
media.go
2
media.go
@@ -133,7 +133,7 @@ type RTCTrack struct {
|
||||
|
||||
// NewRTCTrack is used to create a new RTCTrack
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -9,48 +9,50 @@ import (
|
||||
"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
|
||||
const (
|
||||
PayloadTypeOpus = 111
|
||||
PayloadTypeVP8 = 96
|
||||
PayloadTypeVP9 = 98
|
||||
PayloadTypeH264 = 100
|
||||
DefaultPayloadTypeOpus = 111
|
||||
DefaultPayloadTypeVP8 = 96
|
||||
DefaultPayloadTypeVP9 = 98
|
||||
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
|
||||
func RegisterDefaultCodecs() {
|
||||
RegisterCodec(NewRTCRtpOpusCodec(PayloadTypeOpus, 48000, 2))
|
||||
RegisterCodec(NewRTCRtpVP8Codec(PayloadTypeVP8, 90000))
|
||||
RegisterCodec(NewRTCRtpH264Codec(PayloadTypeH264, 90000))
|
||||
RegisterCodec(NewRTCRtpVP9Codec(PayloadTypeVP9, 90000))
|
||||
RegisterCodec(NewRTCRtpOpusCodec(DefaultPayloadTypeOpus, 48000, 2))
|
||||
RegisterCodec(NewRTCRtpVP8Codec(DefaultPayloadTypeVP8, 90000))
|
||||
RegisterCodec(NewRTCRtpH264Codec(DefaultPayloadTypeH264, 90000))
|
||||
RegisterCodec(NewRTCRtpVP9Codec(DefaultPayloadTypeVP9, 90000))
|
||||
}
|
||||
|
||||
// RegisterCodec is used to register a codec
|
||||
func RegisterCodec(codec *RTCRtpCodec) {
|
||||
rtcMediaEngine.RegisterCodec(codec)
|
||||
// DefaultMediaEngine is the default MediaEngine used by RTCPeerConnections
|
||||
var DefaultMediaEngine = NewMediaEngine()
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
m.codecs = append(m.codecs, codec)
|
||||
return codec.PayloadType
|
||||
}
|
||||
|
||||
func (m *mediaEngine) getCodec(payloadType uint8) (*RTCRtpCodec, error) {
|
||||
func (m *MediaEngine) getCodec(payloadType uint8) (*RTCRtpCodec, error) {
|
||||
for _, codec := range m.codecs {
|
||||
if codec.PayloadType == payloadType {
|
||||
return codec, nil
|
||||
@@ -59,7 +61,7 @@ func (m *mediaEngine) getCodec(payloadType uint8) (*RTCRtpCodec, error) {
|
||||
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 {
|
||||
if codec.Name == sdpCodec.Name &&
|
||||
codec.ClockRate == sdpCodec.ClockRate &&
|
||||
@@ -72,9 +74,9 @@ func (m *mediaEngine) getCodecSDP(sdpCodec sdp.Codec) (*RTCRtpCodec, error) {
|
||||
return nil, errors.New("Codec not found")
|
||||
}
|
||||
|
||||
func (m *mediaEngine) getCodecsByKind(kind RTCRtpCodecType) []*RTCRtpCodec {
|
||||
func (m *MediaEngine) getCodecsByKind(kind RTCRtpCodecType) []*RTCRtpCodec {
|
||||
var codecs []*RTCRtpCodec
|
||||
for _, codec := range rtcMediaEngine.codecs {
|
||||
for _, codec := range m.codecs {
|
||||
if codec.Type == kind {
|
||||
codecs = append(codecs, codec)
|
||||
}
|
||||
@@ -82,6 +84,14 @@ func (m *mediaEngine) getCodecsByKind(kind RTCRtpCodecType) []*RTCRtpCodec {
|
||||
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
|
||||
func NewRTCRtpOpusCodec(payloadType uint8, clockrate uint32, channels uint16) *RTCRtpCodec {
|
||||
c := NewRTCRtpCodec(RTCRtpCodecTypeAudio,
|
||||
|
||||
@@ -231,7 +231,7 @@ func (r *RTCPeerConnection) validateICECandidatePoolSize(config RTCConfiguration
|
||||
current := r.config
|
||||
if r.LocalDescription != nil &&
|
||||
config.ICECandidatePoolSize != current.ICECandidatePoolSize {
|
||||
return &InvalidModificationError{Err: ErrModIceCandidatePoolSize}
|
||||
return &InvalidModificationError{Err: ErrModICECandidatePoolSize}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -265,23 +265,28 @@ func parseICEServer(server RTCICEServer, rawURL string) (ice.URL, error) {
|
||||
return iceurl, &SyntaxError{Err: err}
|
||||
}
|
||||
|
||||
_, isPass := server.Credential.(string)
|
||||
_, isOauth := server.Credential.(RTCOAuthCredential)
|
||||
noPass := !isPass && !isOauth
|
||||
|
||||
if iceurl.Type == ice.ServerTypeTURN {
|
||||
if server.Username == "" ||
|
||||
noPass {
|
||||
if server.Username == "" {
|
||||
return iceurl, &InvalidAccessError{Err: ErrNoTurnCred}
|
||||
}
|
||||
if server.CredentialType == RTCICECredentialTypePassword &&
|
||||
!isPass {
|
||||
|
||||
switch t := server.Credential.(type) {
|
||||
case string:
|
||||
if t == "" {
|
||||
return iceurl, &InvalidAccessError{Err: ErrNoTurnCred}
|
||||
} else if server.CredentialType != RTCICECredentialTypePassword {
|
||||
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
|
||||
}
|
||||
if server.CredentialType == RTCICECredentialTypeOauth &&
|
||||
!isOauth {
|
||||
|
||||
case RTCOAuthCredential:
|
||||
if server.CredentialType != RTCICECredentialTypeOauth {
|
||||
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
|
||||
}
|
||||
|
||||
default:
|
||||
return iceurl, &InvalidAccessError{Err: ErrTurnCred}
|
||||
|
||||
}
|
||||
}
|
||||
return iceurl, nil
|
||||
}
|
||||
|
||||
@@ -100,13 +100,15 @@ type RTCPeerConnection struct {
|
||||
connectionState RTCPeerConnectionState
|
||||
|
||||
// Media
|
||||
mediaEngine *MediaEngine
|
||||
rtpTransceivers []*RTCRtpTransceiver
|
||||
Ontrack func(*RTCTrack)
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
// New creates a new RTCPeerConfiguration with the provided configuration
|
||||
func New(config RTCConfiguration) (*RTCPeerConnection, error) {
|
||||
|
||||
r := &RTCPeerConnection{
|
||||
config: config,
|
||||
signalingState: RTCSignalingStateStable,
|
||||
@@ -114,6 +116,7 @@ func New(config RTCConfiguration) (*RTCPeerConnection, error) {
|
||||
iceGatheringState: ice.GatheringStateNew,
|
||||
iceConnectionState: ice.ConnectionStateNew,
|
||||
connectionState: RTCPeerConnectionStateNew,
|
||||
mediaEngine: DefaultMediaEngine,
|
||||
}
|
||||
err := r.SetConfiguration(config)
|
||||
if err != nil {
|
||||
@@ -122,12 +125,14 @@ func New(config RTCConfiguration) (*RTCPeerConnection, error) {
|
||||
|
||||
r.tlscfg = dtls.NewTLSCfg()
|
||||
|
||||
// TODO: Initialize ICE Agent
|
||||
|
||||
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
|
||||
func (r *RTCPeerConnection) SetIdentityProvider(provider string) error {
|
||||
@@ -161,7 +166,7 @@ func (r *RTCPeerConnection) generateChannel(ssrc uint32, payloadType uint8) (buf
|
||||
return nil
|
||||
}
|
||||
|
||||
codec, err := rtcMediaEngine.getCodecSDP(sdpCodec)
|
||||
codec, err := r.mediaEngine.getCodecSDP(sdpCodec)
|
||||
if err != nil {
|
||||
fmt.Printf("Codec %s in not registered\n", sdpCodec)
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ func (r *RTCPeerConnection) SetRemoteDescription(desc RTCSessionDescription) err
|
||||
// RTCOfferOptions describes the options used to control the offer creation process
|
||||
type RTCOfferOptions struct {
|
||||
VoiceActivityDetection bool
|
||||
IceRestart bool
|
||||
ICERestart bool
|
||||
}
|
||||
|
||||
// CreateOffer starts the RTCPeerConnection and generates the localDescription
|
||||
@@ -181,7 +181,7 @@ func (r *RTCPeerConnection) CreateOffer(options *RTCOfferOptions) (RTCSessionDes
|
||||
track := tranceiver.Sender.Track
|
||||
cname := "pion" // TODO: Support RTP streams synchronisation
|
||||
steamlabel := "pion" // TODO: Support steam labels
|
||||
codec, err := rtcMediaEngine.getCodec(track.PayloadType)
|
||||
codec, err := r.mediaEngine.getCodec(track.PayloadType)
|
||||
if err != nil {
|
||||
return RTCSessionDescription{}, err
|
||||
}
|
||||
@@ -273,7 +273,7 @@ func (r *RTCPeerConnection) addAnswerMedia(d *sdp.SessionDescription, codecType
|
||||
track := tranceiver.Sender.Track
|
||||
cname := track.Label // TODO: Support RTP streams synchronisation
|
||||
steamlabel := track.Label // TODO: Support steam labels
|
||||
codec, err := rtcMediaEngine.getCodec(track.PayloadType)
|
||||
codec, err := r.mediaEngine.getCodec(track.PayloadType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -314,7 +314,7 @@ func (r *RTCPeerConnection) addAnswerMedia(d *sdp.SessionDescription, codecType
|
||||
WithPropertyAttribute(sdp.AttrKeyRtcpMux). // TODO: support RTCP fallback
|
||||
WithPropertyAttribute(sdp.AttrKeyRtcpRsize) // TODO: Support Reduced-Size RTCP?
|
||||
|
||||
for _, codec := range rtcMediaEngine.getCodecsByKind(codecType) {
|
||||
for _, codec := range r.mediaEngine.getCodecsByKind(codecType) {
|
||||
media.WithCodec(
|
||||
codec.PayloadType,
|
||||
codec.Name,
|
||||
|
||||
Reference in New Issue
Block a user