You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/pkg/sdp/sdp.go

321 lines
6.4 KiB
Go

// Copyright 2020, Chef. All rights reserved.
// https://github.com/q191201771/lal
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package sdp
import (
"encoding/base64"
"errors"
"strconv"
"strings"
"github.com/q191201771/lal/pkg/base"
)
// rfc4566
var ErrSDP = errors.New("lal.sdp: fxxk")
const (
ARTPMapEncodingName = "H265"
)
type LogicContext struct {
AudioClockRate int
VideoClockRate int
AudioPayloadType base.AVPacketPT
VideoPayloadType base.AVPacketPT
AudioAControl string
VideoAControl string
ASC []byte
VPS []byte
SPS []byte
PPS []byte
}
type RawContext struct {
ARTPMapList []ARTPMap
AFmtPBaseList []AFmtPBase
AControlList []AControl
}
type ARTPMap struct {
PayloadType int
EncodingName string
ClockRate int
EncodingParameters string
}
type AFmtPBase struct {
Format int // same as PayloadType
Parameters map[string]string // name -> value
}
type AControl struct {
Value string
}
func ParseSDP2LogicContext(b []byte) (LogicContext, error) {
var ret LogicContext
c, err := ParseSDP2RawContext(b)
if err != nil {
return ret, err
}
for i, item := range c.ARTPMapList {
switch item.PayloadType {
case base.RTPPacketTypeAVCOrHEVC:
ret.VideoClockRate = item.ClockRate
if item.EncodingName == ARTPMapEncodingName {
ret.VideoPayloadType = base.AVPacketPTHEVC
} else {
ret.VideoPayloadType = base.AVPacketPTAVC
}
if i < len(c.AControlList) {
ret.VideoAControl = c.AControlList[i].Value
}
case base.RTPPacketTypeAAC:
ret.AudioClockRate = item.ClockRate
ret.AudioPayloadType = base.AVPacketPTAAC
if i < len(c.AControlList) {
ret.AudioAControl = c.AControlList[i].Value
}
default:
return ret, ErrSDP
}
}
for _, item := range c.AFmtPBaseList {
switch item.Format {
case base.RTPPacketTypeAVCOrHEVC:
if ret.VideoPayloadType == base.AVPacketPTHEVC {
ret.VPS, ret.SPS, ret.PPS, err = ParseVPSSPSPPS(item)
} else {
ret.SPS, ret.PPS, err = ParseSPSPPS(item)
}
if err != nil {
return ret, err
}
case base.RTPPacketTypeAAC:
ret.ASC, err = ParseASC(item)
if err != nil {
return ret, err
}
default:
return ret, ErrSDP
}
}
return ret, nil
}
// 例子见单元测试
func ParseSDP2RawContext(b []byte) (RawContext, error) {
var sdpCtx RawContext
s := string(b)
lines := strings.Split(s, "\r\n")
for _, line := range lines {
if strings.HasPrefix(line, "a=rtpmap") {
aRTPMap, err := ParseARTPMap(line)
if err != nil {
return sdpCtx, err
}
sdpCtx.ARTPMapList = append(sdpCtx.ARTPMapList, aRTPMap)
}
if strings.HasPrefix(line, "a=fmtp") {
aFmtPBase, err := ParseAFmtPBase(line)
if err != nil {
return sdpCtx, err
}
sdpCtx.AFmtPBaseList = append(sdpCtx.AFmtPBaseList, aFmtPBase)
}
if strings.HasPrefix(line, "a=control") {
aControl, err := ParseAControl(line)
if err != nil {
return sdpCtx, err
}
sdpCtx.AControlList = append(sdpCtx.AControlList, aControl)
}
}
return sdpCtx, nil
}
// 例子见单元测试
func ParseARTPMap(s string) (ret ARTPMap, err error) {
// rfc 3640 3.3.1. General
// rfc 3640 3.3.6. High Bit-rate AAC
//
// a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>]
//
items := strings.SplitN(s, ":", 2)
if len(items) != 2 {
err = ErrSDP
return
}
items = strings.SplitN(items[1], " ", 2)
if len(items) != 2 {
err = ErrSDP
return
}
ret.PayloadType, err = strconv.Atoi(items[0])
if err != nil {
return
}
items = strings.SplitN(items[1], "/", 3)
switch len(items) {
case 3:
ret.EncodingParameters = items[2]
fallthrough
case 2:
ret.EncodingName = items[0]
ret.ClockRate, err = strconv.Atoi(items[1])
if err != nil {
return
}
default:
err = ErrSDP
}
return
}
// 例子见单元测试
func ParseAFmtPBase(s string) (ret AFmtPBase, err error) {
// rfc 3640 4.4.1. The a=fmtp Keyword
//
// a=fmtp:<format> <parameter name>=<value>[; <parameter name>=<value>]
//
ret.Parameters = make(map[string]string)
items := strings.SplitN(s, ":", 2)
if len(items) != 2 {
err = ErrSDP
return
}
items = strings.SplitN(items[1], " ", 2)
if len(items) != 2 {
err = ErrSDP
return
}
ret.Format, err = strconv.Atoi(items[0])
if err != nil {
return
}
items = strings.Split(items[1], ";")
for _, pp := range items {
pp = strings.TrimSpace(pp)
kv := strings.SplitN(pp, "=", 2)
if len(kv) != 2 {
err = ErrSDP
return
}
ret.Parameters[kv[0]] = kv[1]
}
return
}
func ParseAControl(s string) (ret AControl, err error) {
if !strings.HasPrefix(s, "a=control:") {
err = ErrSDP
return
}
ret.Value = strings.TrimPrefix(s, "a=control:")
return
}
func ParseASC(a AFmtPBase) ([]byte, error) {
if a.Format != base.RTPPacketTypeAAC {
return nil, ErrSDP
}
v, ok := a.Parameters["config"]
if !ok {
return nil, ErrSDP
}
if len(v) < 4 || (len(v)%2) != 0 {
return nil, ErrSDP
}
l := len(v) / 2
r := make([]byte, l)
for i := 0; i < l; i++ {
b, err := strconv.ParseInt(v[i*2:i*2+2], 16, 0)
if err != nil {
return nil, ErrSDP
}
r[i] = uint8(b)
}
return r, nil
}
func ParseVPSSPSPPS(a AFmtPBase) (vps, sps, pps []byte, err error) {
if a.Format != base.RTPPacketTypeAVCOrHEVC {
return nil, nil, nil, ErrSDP
}
v, ok := a.Parameters["sprop-vps"]
if !ok {
return nil, nil, nil, ErrSDP
}
if vps, err = base64.StdEncoding.DecodeString(v); err != nil {
return nil, nil, nil, err
}
v, ok = a.Parameters["sprop-sps"]
if !ok {
return nil, nil, nil, ErrSDP
}
if sps, err = base64.StdEncoding.DecodeString(v); err != nil {
return nil, nil, nil, err
}
v, ok = a.Parameters["sprop-pps"]
if !ok {
return nil, nil, nil, ErrSDP
}
if pps, err = base64.StdEncoding.DecodeString(v); err != nil {
return nil, nil, nil, err
}
return
}
// 解析AVC/H264的spspps
// 例子见单元测试
func ParseSPSPPS(a AFmtPBase) (sps, pps []byte, err error) {
if a.Format != base.RTPPacketTypeAVCOrHEVC {
return nil, nil, ErrSDP
}
v, ok := a.Parameters["sprop-parameter-sets"]
if !ok {
return nil, nil, ErrSDP
}
items := strings.SplitN(v, ",", 2)
if len(items) != 2 {
return nil, nil, ErrSDP
}
sps, err = base64.StdEncoding.DecodeString(items[0])
if err != nil {
return nil, nil, ErrSDP
}
pps, err = base64.StdEncoding.DecodeString(items[1])
return
}