package codecs
import (
"strings"
"github.com/livekit/protocol/codecs/mime"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/pion/webrtc/v4"
)
var (
OpusCodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeOpus.String(),
ClockRate: 48000,
Channels: 2,
SDPFmtpLine: "minptime=10;useinbandfec=1",
},
PayloadType: 111,
}
RedCodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeRED.String(),
ClockRate: 48000,
Channels: 2,
SDPFmtpLine: "111/111",
},
PayloadType: 63,
}
PCMUCodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypePCMU.String(),
ClockRate: 8000,
},
PayloadType: 0,
}
PCMACodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypePCMA.String(),
ClockRate: 8000,
},
PayloadType: 8,
}
VideoRTXCodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeRTX.String(),
ClockRate: 90000,
},
}
VP8CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeVP8.String(),
ClockRate: 90000,
},
PayloadType: 96,
}
VP9ProfileId0CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeVP9.String(),
ClockRate: 90000,
SDPFmtpLine: "profile-id=0",
},
PayloadType: 98,
}
VP9ProfileId1CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeVP9.String(),
ClockRate: 90000,
SDPFmtpLine: "profile-id=1",
},
PayloadType: 100,
}
H264ProfileLevelId42e01fPacketizationMode0CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH264.String(),
ClockRate: 90000,
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f",
},
PayloadType: 125,
}
H264ProfileLevelId42e01fPacketizationMode1CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH264.String(),
ClockRate: 90000,
SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",
},
PayloadType: 108,
}
H264HighProfileFmtp = "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032"
H264HighProfileCodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH264.String(),
ClockRate: 90000,
SDPFmtpLine: H264HighProfileFmtp,
},
PayloadType: 123,
}
AV1CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeAV1.String(),
ClockRate: 90000,
},
PayloadType: 35,
}
H265CodecParameters = webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mime.MimeTypeH265.String(),
ClockRate: 90000,
},
PayloadType: 116,
}
VideoCodecsParameters = []webrtc.RTPCodecParameters{
VP8CodecParameters,
VP9ProfileId0CodecParameters,
VP9ProfileId1CodecParameters,
H264ProfileLevelId42e01fPacketizationMode0CodecParameters,
H264ProfileLevelId42e01fPacketizationMode1CodecParameters,
H264HighProfileCodecParameters,
AV1CodecParameters,
H265CodecParameters,
}
)
func areFmtpLinesEqual(a, b string) bool {
if a == b {
return true
}
paramsA := make(map[string]string)
for _, param := range strings.Split(a, ";") {
if key, value, found := strings.Cut(param, "="); found {
paramsA[key] = value
}
}
count := 0
for _, param := range strings.Split(b, ";") {
if key, value, found := strings.Cut(param, "="); found {
if paramsA[key] != value {
return false
}
count++
}
}
return count == len(paramsA)
}
func ToWebrtcCodecParameters(codec *livekit.Codec) webrtc.RTPCodecParameters {
fmtp := codec.GetFmtpLine()
var params webrtc.RTPCodecParameters
switch mime.NormalizeMimeType(codec.GetMime()) {
case mime.MimeTypeOpus:
params = OpusCodecParameters
case mime.MimeTypeRED:
params = RedCodecParameters
case mime.MimeTypePCMU:
params = PCMUCodecParameters
case mime.MimeTypePCMA:
params = PCMACodecParameters
case mime.MimeTypeRTX:
params = VideoRTXCodecParameters
case mime.MimeTypeVP8:
params = VP8CodecParameters
case mime.MimeTypeVP9:
if strings.Contains(fmtp, "profile-id=1") {
params = VP9ProfileId1CodecParameters
} else {
params = VP9ProfileId0CodecParameters
}
case mime.MimeTypeAV1:
params = AV1CodecParameters
case mime.MimeTypeH264:
if strings.Contains(fmtp, "profile-level-id=640032") {
params = H264HighProfileCodecParameters
} else if strings.Contains(fmtp, "packetization-mode=0") {
params = H264ProfileLevelId42e01fPacketizationMode0CodecParameters
} else {
params = H264ProfileLevelId42e01fPacketizationMode1CodecParameters
}
case mime.MimeTypeH265:
params = H265CodecParameters
default:
return webrtc.RTPCodecParameters{}
}
if fmtp != "" && !areFmtpLinesEqual(fmtp, params.SDPFmtpLine) {
logger.Warnw("non-standard fmtp, may not be supported",
nil,
"mime", codec.GetMime(),
"fmtp", fmtp,
"supportedFmtp", params.SDPFmtpLine,
)
params.SDPFmtpLine = fmtp
}
return params
}