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/parse_raw.go

246 lines
4.9 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 (
"strconv"
"strings"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/naza/pkg/nazaerrors"
)
type RawContext struct {
MediaDescList []MediaDesc
}
type MediaDesc struct {
M M
ARtpMap ARtpMap
AFmtPBase *AFmtPBase
AControl AControl
}
type M struct {
Media string
}
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
}
// ParseSdp2RawContext 例子见单元测试
//
func ParseSdp2RawContext(b []byte) (RawContext, error) {
lines := strings.Split(string(b), "\r\n")
ctx, err := parseSdp2RawContext(lines)
if err == nil {
return ctx, nil
}
// TestCase13再尝试抢救一下
var newlines []string
i := 0
for i < len(lines) {
if strings.HasPrefix(lines[i], "a=fmtp") {
newline := lines[i]
j := i + 1
// TODO(chef): 如果换行的数据刚好是`m=`或`a=`开头呢?
for ; j < len(lines); j++ {
if !strings.HasPrefix(lines[j], "m=") &&
!strings.HasPrefix(lines[j], "a=") {
newline += lines[j]
} else {
break
}
}
newlines = append(newlines, newline)
i = j
} else {
newlines = append(newlines, lines[i])
i++
}
}
return parseSdp2RawContext(newlines)
}
func ParseM(s string) (ret M, err error) {
ss := strings.TrimPrefix(s, "m=")
items := strings.Split(ss, " ")
if len(items) < 1 {
return ret, nazaerrors.Wrap(base.ErrSdp)
}
ret.Media = items[0]
return
}
// ParseARtpMap 例子见单元测试
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 = nazaerrors.Wrap(base.ErrSdp)
return
}
items = strings.SplitN(items[1], " ", 2)
if len(items) != 2 {
err = nazaerrors.Wrap(base.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 = nazaerrors.Wrap(base.ErrSdp)
}
return
}
// ParseAFmtPBase 例子见单元测试
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 = nazaerrors.Wrap(base.ErrSdp)
return
}
items = strings.SplitN(items[1], " ", 2)
if len(items) != 2 {
err = nazaerrors.Wrap(base.ErrSdp)
return
}
ret.Format, err = strconv.Atoi(items[0])
if err != nil {
return
}
// 见TestCase11
items[1] = strings.TrimLeft(items[1], ";")
// 见TestCase12
items[1] = strings.TrimRight(items[1], ";")
items = strings.Split(items[1], ";")
for _, pp := range items {
pp = strings.TrimSpace(pp)
kv := strings.SplitN(pp, "=", 2)
if len(kv) != 2 {
err = nazaerrors.Wrap(base.ErrSdp)
return
}
ret.Parameters[kv[0]] = kv[1]
}
return
}
func ParseAControl(s string) (ret AControl, err error) {
if !strings.HasPrefix(s, "a=control:") {
err = nazaerrors.Wrap(base.ErrSdp)
return
}
ret.Value = strings.TrimPrefix(s, "a=control:")
return
}
// ---------------------------------------------------------------------------------------------------------------------
func parseSdp2RawContext(lines []string) (RawContext, error) {
var (
sdpCtx RawContext
md *MediaDesc
)
for _, line := range lines {
if strings.HasPrefix(line, "m=") {
m, err := ParseM(line)
if err != nil {
return sdpCtx, err
}
if md != nil {
sdpCtx.MediaDescList = append(sdpCtx.MediaDescList, *md)
}
md = &MediaDesc{
M: m,
}
}
if strings.HasPrefix(line, "a=rtpmap") {
aRtpMap, err := ParseARtpMap(line)
if err != nil {
return sdpCtx, err
}
if md == nil {
continue
}
md.ARtpMap = aRtpMap
}
if strings.HasPrefix(line, "a=fmtp") {
aFmtPBase, err := ParseAFmtPBase(line)
if err != nil {
return sdpCtx, err
}
if md == nil {
continue
}
md.AFmtPBase = &aFmtPBase
}
if strings.HasPrefix(line, "a=control") {
aControl, err := ParseAControl(line)
if err != nil {
return sdpCtx, err
}
if md == nil {
continue
}
md.AControl = aControl
}
}
if md != nil {
sdpCtx.MediaDescList = append(sdpCtx.MediaDescList, *md)
}
return sdpCtx, nil
}