mirror of
https://github.com/aler9/gortsplib
synced 2025-09-27 03:25:52 +08:00
245 lines
5.5 KiB
Go
245 lines
5.5 KiB
Go
// Package format contains RTP format definitions, decoders and encoders.
|
|
package format
|
|
|
|
import (
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pion/rtp"
|
|
psdp "github.com/pion/sdp/v3"
|
|
)
|
|
|
|
var (
|
|
smartPayloadTypeRegexp = regexp.MustCompile("^smart/[0-9]/[0-9]+$")
|
|
smartRtpmapRegexp = regexp.MustCompile("^([0-9]+) (.+)/[0-9]+$")
|
|
)
|
|
|
|
func replaceSmartPayloadType(payloadType string, attributes []psdp.Attribute) string {
|
|
re1 := smartPayloadTypeRegexp.FindStringSubmatch(payloadType)
|
|
if re1 != nil {
|
|
for _, attr := range attributes {
|
|
if attr.Key == "rtpmap" {
|
|
re2 := smartRtpmapRegexp.FindStringSubmatch(attr.Value)
|
|
if re2 != nil {
|
|
return re2[1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return payloadType
|
|
}
|
|
|
|
func getFormatAttribute(attributes []psdp.Attribute, payloadType uint8, key string) string {
|
|
for _, attr := range attributes {
|
|
if attr.Key == key {
|
|
v := strings.TrimSpace(attr.Value)
|
|
if parts := strings.SplitN(v, " ", 2); len(parts) == 2 {
|
|
if tmp, err := strconv.ParseUint(parts[0], 10, 8); err == nil && uint8(tmp) == payloadType {
|
|
return parts[1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getCodecAndClock(rtpMap string) (string, string) {
|
|
parts2 := strings.SplitN(rtpMap, "/", 2)
|
|
if len(parts2) != 2 {
|
|
return "", ""
|
|
}
|
|
|
|
return strings.ToLower(parts2[0]), parts2[1]
|
|
}
|
|
|
|
func decodeFMTP(enc string) map[string]string {
|
|
if enc == "" {
|
|
return nil
|
|
}
|
|
|
|
ret := make(map[string]string)
|
|
|
|
for _, kv := range strings.Split(enc, ";") {
|
|
kv = strings.Trim(kv, " ")
|
|
|
|
if len(kv) == 0 {
|
|
continue
|
|
}
|
|
|
|
tmp := strings.SplitN(kv, "=", 2)
|
|
if len(tmp) != 2 {
|
|
continue
|
|
}
|
|
|
|
ret[strings.ToLower(tmp[0])] = tmp[1]
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
type unmarshalContext struct {
|
|
mediaType string
|
|
payloadType uint8
|
|
clock string
|
|
codec string
|
|
rtpMap string
|
|
fmtp map[string]string
|
|
}
|
|
|
|
// Format is a media format.
|
|
// It defines the payload type of RTP packets and how to encode/decode them.
|
|
type Format interface {
|
|
unmarshal(ctx *unmarshalContext) error
|
|
|
|
// Codec returns the codec name.
|
|
Codec() string
|
|
|
|
// ClockRate returns the clock rate.
|
|
ClockRate() int
|
|
|
|
// PayloadType returns the payload type.
|
|
PayloadType() uint8
|
|
|
|
// RTPMap returns the rtpmap attribute.
|
|
RTPMap() string
|
|
|
|
// FMTP returns the fmtp attribute.
|
|
FMTP() map[string]string
|
|
|
|
// PTSEqualsDTS checks whether PTS is equal to DTS in RTP packets.
|
|
PTSEqualsDTS(*rtp.Packet) bool
|
|
}
|
|
|
|
// Unmarshal decodes a format from a media description.
|
|
func Unmarshal(md *psdp.MediaDescription, payloadTypeStr string) (Format, error) {
|
|
mediaType := md.MediaName.Media
|
|
payloadTypeStr = replaceSmartPayloadType(payloadTypeStr, md.Attributes)
|
|
|
|
tmp, err := strconv.ParseUint(payloadTypeStr, 10, 8)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
payloadType := uint8(tmp)
|
|
|
|
rtpMap := getFormatAttribute(md.Attributes, payloadType, "rtpmap")
|
|
fmtp := decodeFMTP(getFormatAttribute(md.Attributes, payloadType, "fmtp"))
|
|
codec, clock := getCodecAndClock(rtpMap)
|
|
|
|
format := func() Format {
|
|
switch {
|
|
/*
|
|
* dynamic payload types
|
|
**/
|
|
|
|
// video
|
|
|
|
case codec == "av1" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
|
return &AV1{}
|
|
|
|
case codec == "vp9" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
|
return &VP9{}
|
|
|
|
case codec == "vp8" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
|
return &VP8{}
|
|
|
|
case codec == "h265" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
|
return &H265{}
|
|
|
|
case codec == "h264" && clock == "90000" && ((payloadType >= 96 && payloadType <= 127) || payloadType == 35):
|
|
return &H264{}
|
|
|
|
case codec == "mp4v-es" && clock == "90000" && payloadType >= 96 && payloadType <= 127:
|
|
return &MPEG4Video{}
|
|
|
|
// audio
|
|
|
|
case codec == "opus", codec == "multiopus" && payloadType >= 96 && payloadType <= 127:
|
|
return &Opus{}
|
|
|
|
case codec == "vorbis" && payloadType >= 96 && payloadType <= 127:
|
|
return &Vorbis{}
|
|
|
|
case codec == "mpeg4-generic" && payloadType >= 96 && payloadType <= 127:
|
|
return &MPEG4Audio{}
|
|
|
|
case codec == "mp4a-latm" && payloadType >= 96 && payloadType <= 127:
|
|
return &MPEG4AudioLATM{}
|
|
|
|
case codec == "ac3" && payloadType >= 96 && payloadType <= 127:
|
|
return &AC3{}
|
|
|
|
case codec == "speex" && payloadType >= 96 && payloadType <= 127:
|
|
return &Speex{}
|
|
|
|
case (codec == "g726-16" ||
|
|
codec == "g726-24" ||
|
|
codec == "g726-32" ||
|
|
codec == "g726-40" ||
|
|
codec == "aal2-g726-16" ||
|
|
codec == "aal2-g726-24" ||
|
|
codec == "aal2-g726-32" ||
|
|
codec == "aal2-g726-40") && clock == "8000" && payloadType >= 96 && payloadType <= 127:
|
|
return &G726{}
|
|
|
|
case codec == "pcma", codec == "pcmu" && payloadType >= 96 && payloadType <= 127:
|
|
return &G711{}
|
|
|
|
case codec == "l8", codec == "l16", codec == "l24" && payloadType >= 96 && payloadType <= 127:
|
|
return &LPCM{}
|
|
|
|
// other
|
|
|
|
case codec == "smtpe336m" && payloadType >= 96 && payloadType <= 127:
|
|
return &KLV{}
|
|
|
|
/*
|
|
* static payload types
|
|
**/
|
|
|
|
// video
|
|
|
|
case payloadType == 32:
|
|
return &MPEG1Video{}
|
|
|
|
case payloadType == 26:
|
|
return &MJPEG{}
|
|
|
|
// audio
|
|
|
|
case payloadType == 14:
|
|
return &MPEG1Audio{}
|
|
|
|
case payloadType == 9:
|
|
return &G722{}
|
|
|
|
case payloadType == 0, payloadType == 8:
|
|
return &G711{}
|
|
|
|
case payloadType == 10, payloadType == 11:
|
|
return &LPCM{}
|
|
|
|
// other
|
|
|
|
case payloadType == 33:
|
|
return &MPEGTS{}
|
|
}
|
|
|
|
return &Generic{}
|
|
}()
|
|
|
|
err = format.unmarshal(&unmarshalContext{
|
|
mediaType: mediaType,
|
|
payloadType: payloadType,
|
|
clock: clock,
|
|
codec: codec,
|
|
rtpMap: rtpMap,
|
|
fmtp: fmtp,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return format, nil
|
|
}
|