mirror of
https://github.com/jech/galene.git
synced 2024-11-22 16:45:58 +01:00
Restrict negotiation of downstream codecs, allow multiple profiles.
We now restrict the allowable codecs in the downstream direction, which leads to a clean failure instead of a silent track. We also allow multiple profiles for a single codec.
This commit is contained in:
parent
845e798467
commit
816b7a54df
3 changed files with 192 additions and 93 deletions
235
group/group.go
235
group/group.go
|
@ -142,71 +142,49 @@ func (g *Group) API() (*webrtc.API, error) {
|
||||||
return APIFromNames(codecs)
|
return APIFromNames(codecs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func codecFromName(name string) (webrtc.RTPCodecCapability, error) {
|
func fmtpValue(fmtp, key string) string {
|
||||||
switch name {
|
fields := strings.Split(fmtp, ";")
|
||||||
case "vp8":
|
for _, f := range fields {
|
||||||
return webrtc.RTPCodecCapability{
|
kv := strings.SplitN(f, "=", 2)
|
||||||
"video/VP8", 90000, 0,
|
if len(kv) != 2 {
|
||||||
"",
|
continue
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "vp9":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"video/VP9", 90000, 0,
|
|
||||||
"profile-id=0",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "av1":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"video/AV1X", 90000, 0,
|
|
||||||
"",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "h264":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"video/H264", 90000, 0,
|
|
||||||
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "opus":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"audio/opus", 48000, 2,
|
|
||||||
"minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "g722":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"audio/G722", 8000, 1,
|
|
||||||
"",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "pcmu":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"audio/PCMU", 8000, 1,
|
|
||||||
"",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
case "pcma":
|
|
||||||
return webrtc.RTPCodecCapability{
|
|
||||||
"audio/PCMA", 8000, 1,
|
|
||||||
"",
|
|
||||||
nil,
|
|
||||||
}, nil
|
|
||||||
default:
|
|
||||||
return webrtc.RTPCodecCapability{}, errors.New("unknown codec")
|
|
||||||
}
|
}
|
||||||
|
if kv[0] == key {
|
||||||
|
return kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func payloadType(codec webrtc.RTPCodecCapability) (webrtc.PayloadType, error) {
|
func CodecPayloadType(codec webrtc.RTPCodecCapability) (webrtc.PayloadType, error) {
|
||||||
switch strings.ToLower(codec.MimeType) {
|
switch strings.ToLower(codec.MimeType) {
|
||||||
case "video/vp8":
|
case "video/vp8":
|
||||||
return 96, nil
|
return 96, nil
|
||||||
case "video/vp9":
|
case "video/vp9":
|
||||||
|
profile := fmtpValue(codec.SDPFmtpLine, "profile-id")
|
||||||
|
switch(profile) {
|
||||||
|
case "0":
|
||||||
return 98, nil
|
return 98, nil
|
||||||
|
case "2":
|
||||||
|
return 100, nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("unknown VP9 profile")
|
||||||
|
}
|
||||||
case "video/av1x":
|
case "video/av1x":
|
||||||
return 104, nil
|
return 35, nil
|
||||||
case "video/h264":
|
case "video/h264":
|
||||||
|
profile := fmtpValue(codec.SDPFmtpLine, "profile-level-id")
|
||||||
|
if len(profile) < 4 {
|
||||||
|
return 0, errors.New("malforned H.264 profile")
|
||||||
|
}
|
||||||
|
switch(strings.ToLower(profile[:4])) {
|
||||||
|
case "4200":
|
||||||
return 102, nil
|
return 102, nil
|
||||||
|
case "42e0":
|
||||||
|
return 108, nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("unknown H.264 profile")
|
||||||
|
}
|
||||||
case "audio/opus":
|
case "audio/opus":
|
||||||
return 111, nil
|
return 111, nil
|
||||||
case "audio/g722":
|
case "audio/g722":
|
||||||
|
@ -220,7 +198,112 @@ func payloadType(codec webrtc.RTPCodecCapability) (webrtc.PayloadType, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func APIFromCodecs(codecs []webrtc.RTPCodecCapability) (*webrtc.API, error) {
|
func codecsFromName(name string) ([]webrtc.RTPCodecParameters, error) {
|
||||||
|
fb := []webrtc.RTCPFeedback{
|
||||||
|
{"goog-remb", ""},
|
||||||
|
{"nack", ""},
|
||||||
|
{"nack", "pli"},
|
||||||
|
{"ccm", "fir"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var codecs []webrtc.RTPCodecCapability
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case "vp8":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"video/VP8", 90000, 0,
|
||||||
|
"",
|
||||||
|
fb,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "vp9":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"video/VP9", 90000, 0,
|
||||||
|
"profile-id=0",
|
||||||
|
fb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"video/VP9", 90000, 0,
|
||||||
|
"profile-id=2",
|
||||||
|
fb,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "av1":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"video/AV1X", 90000, 0,
|
||||||
|
"",
|
||||||
|
fb,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "h264":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"video/H264", 90000, 0,
|
||||||
|
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f",
|
||||||
|
fb,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"video/H264", 90000, 0,
|
||||||
|
"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
|
||||||
|
fb,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "opus":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"audio/opus", 48000, 2,
|
||||||
|
"minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "g722":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"audio/G722", 8000, 1,
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "pcmu":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"audio/PCMU", 8000, 1,
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case "pcma":
|
||||||
|
codecs = []webrtc.RTPCodecCapability{
|
||||||
|
{
|
||||||
|
"audio/PCMU", 8000, 1,
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown codec")
|
||||||
|
}
|
||||||
|
|
||||||
|
parms := make([]webrtc.RTPCodecParameters, 0, len(codecs))
|
||||||
|
for _, c := range codecs {
|
||||||
|
ptype, err := CodecPayloadType(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Couldn't determine ptype for codec %v: %v",
|
||||||
|
c.MimeType, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parms = append(parms, webrtc.RTPCodecParameters{
|
||||||
|
RTPCodecCapability: c,
|
||||||
|
PayloadType: ptype,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return parms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func APIFromCodecs(codecs []webrtc.RTPCodecParameters) (*webrtc.API, error) {
|
||||||
s := webrtc.SettingEngine{}
|
s := webrtc.SettingEngine{}
|
||||||
s.SetSRTPReplayProtectionWindow(512)
|
s.SetSRTPReplayProtectionWindow(512)
|
||||||
if !UseMDNS {
|
if !UseMDNS {
|
||||||
|
@ -229,41 +312,11 @@ func APIFromCodecs(codecs []webrtc.RTPCodecCapability) (*webrtc.API, error) {
|
||||||
m := webrtc.MediaEngine{}
|
m := webrtc.MediaEngine{}
|
||||||
|
|
||||||
for _, codec := range codecs {
|
for _, codec := range codecs {
|
||||||
var tpe webrtc.RTPCodecType
|
tpe := webrtc.RTPCodecTypeVideo
|
||||||
var fb []webrtc.RTCPFeedback
|
if strings.HasPrefix(strings.ToLower(codec.MimeType), "audio/") {
|
||||||
if strings.HasPrefix(strings.ToLower(codec.MimeType), "video/") {
|
|
||||||
tpe = webrtc.RTPCodecTypeVideo
|
|
||||||
fb = []webrtc.RTCPFeedback{
|
|
||||||
{"goog-remb", ""},
|
|
||||||
{"nack", ""},
|
|
||||||
{"nack", "pli"},
|
|
||||||
{"ccm", "fir"},
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(strings.ToLower(codec.MimeType), "audio/") {
|
|
||||||
tpe = webrtc.RTPCodecTypeAudio
|
tpe = webrtc.RTPCodecTypeAudio
|
||||||
fb = []webrtc.RTCPFeedback{}
|
|
||||||
} else {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
err := m.RegisterCodec(codec, tpe)
|
||||||
ptpe, err := payloadType(codec)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("%v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = m.RegisterCodec(
|
|
||||||
webrtc.RTPCodecParameters{
|
|
||||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
|
||||||
MimeType: codec.MimeType,
|
|
||||||
ClockRate: codec.ClockRate,
|
|
||||||
Channels: codec.Channels,
|
|
||||||
SDPFmtpLine: codec.SDPFmtpLine,
|
|
||||||
RTCPFeedback: fb,
|
|
||||||
},
|
|
||||||
PayloadType: ptpe,
|
|
||||||
},
|
|
||||||
tpe,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%v", err)
|
log.Printf("%v", err)
|
||||||
continue
|
continue
|
||||||
|
@ -290,14 +343,14 @@ func APIFromNames(names []string) (*webrtc.API, error) {
|
||||||
if len(names) == 0 {
|
if len(names) == 0 {
|
||||||
names = []string{"vp8", "opus"}
|
names = []string{"vp8", "opus"}
|
||||||
}
|
}
|
||||||
codecs := make([]webrtc.RTPCodecCapability, 0, len(names))
|
var codecs []webrtc.RTPCodecParameters
|
||||||
for _, n := range names {
|
for _, n := range names {
|
||||||
codec, err := codecFromName(n)
|
cs, err := codecsFromName(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Codec %v: %v", n, err)
|
log.Printf("Codec %v: %v", n, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
codecs = append(codecs, codec)
|
codecs = append(codecs, cs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return APIFromCodecs(codecs)
|
return APIFromCodecs(codecs)
|
||||||
|
|
|
@ -237,3 +237,29 @@ func TestPermissions(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFmtpValue(t *testing.T) {
|
||||||
|
type fmtpTest struct {
|
||||||
|
fmtp string
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
fmtpTests := []fmtpTest{
|
||||||
|
{"", "foo", ""},
|
||||||
|
{"profile-id=0", "profile-id", "0"},
|
||||||
|
{"profile-id=0", "foo", ""},
|
||||||
|
{"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", "profile-level-id", "42001f"},
|
||||||
|
{"foo=1;bar=2;quux=3", "foo", "1"},
|
||||||
|
{"foo=1;bar=2;quux=3", "bar", "2"},
|
||||||
|
{"foo=1;bar=2;quux=3", "fu", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range fmtpTests {
|
||||||
|
v := fmtpValue(test.fmtp, test.key)
|
||||||
|
if v != test.value {
|
||||||
|
t.Errorf("fmtpValue(%v, %v) = %v, expected %v",
|
||||||
|
test.fmtp, test.key, v, test.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -389,6 +389,26 @@ func addDownTrackUnlocked(conn *rtpDownConnection, remoteTrack *rtpUpTrack, remo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
codec := local.Codec()
|
||||||
|
ptype, err := group.CodecPayloadType(local.Codec())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Couldn't determine ptype for codec %v: %v",
|
||||||
|
codec.MimeType, err)
|
||||||
|
} else {
|
||||||
|
err := transceiver.SetCodecPreferences(
|
||||||
|
[]webrtc.RTPCodecParameters{
|
||||||
|
{
|
||||||
|
RTPCodecCapability: codec,
|
||||||
|
PayloadType: ptype,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Couldn't set ptype for codec %v: %v",
|
||||||
|
codec.MimeType, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parms := transceiver.Sender().GetParameters()
|
parms := transceiver.Sender().GetParameters()
|
||||||
if len(parms.Encodings) != 1 {
|
if len(parms.Encodings) != 1 {
|
||||||
return errors.New("got multiple encodings")
|
return errors.New("got multiple encodings")
|
||||||
|
|
Loading…
Reference in a new issue