|
- package aoiprxkit
-
- import (
- "fmt"
- "net"
- "strconv"
- "strings"
- "time"
- )
-
- type SDPInfo struct {
- SessionName string
- Origin string
- MulticastGroup string
- Port int
- PayloadType uint8
- Encoding string
- SampleRateHz int
- Channels int
- PacketTimeMS int
- }
-
- // ParseMinimalSDP extracts the multicast address, port and one rtpmap line.
- // It is deliberately small and not a full SDP parser.
- func ParseMinimalSDP(s string) (SDPInfo, error) {
- var out SDPInfo
- lines := strings.Split(strings.ReplaceAll(s, "\r\n", "\n"), "\n")
- for _, raw := range lines {
- line := strings.TrimSpace(raw)
- switch {
- case strings.HasPrefix(line, "s="):
- out.SessionName = strings.TrimPrefix(line, "s=")
-
- case strings.HasPrefix(line, "o="):
- out.Origin = strings.TrimPrefix(line, "o=")
-
- case strings.HasPrefix(line, "c=IN IP4 "):
- rest := strings.TrimPrefix(line, "c=IN IP4 ")
- host := strings.Split(rest, "/")[0]
- if net.ParseIP(host) == nil {
- return out, fmt.Errorf("invalid multicast host in c=: %q", host)
- }
- out.MulticastGroup = host
-
- case strings.HasPrefix(line, "m=audio "):
- fields := strings.Fields(line)
- if len(fields) < 4 {
- return out, fmt.Errorf("invalid m=audio line")
- }
- port, err := strconv.Atoi(fields[1])
- if err != nil {
- return out, fmt.Errorf("invalid audio port: %w", err)
- }
- pt, err := strconv.Atoi(fields[3])
- if err != nil {
- return out, fmt.Errorf("invalid payload type: %w", err)
- }
- out.Port = port
- out.PayloadType = uint8(pt)
-
- case strings.HasPrefix(line, "a=rtpmap:"):
- rest := strings.TrimPrefix(line, "a=rtpmap:")
- parts := strings.Fields(rest)
- if len(parts) != 2 {
- return out, fmt.Errorf("invalid rtpmap line")
- }
- pt, err := strconv.Atoi(parts[0])
- if err != nil {
- return out, fmt.Errorf("invalid rtpmap payload type: %w", err)
- }
- codecParts := strings.Split(parts[1], "/")
- if len(codecParts) < 2 {
- return out, fmt.Errorf("invalid rtpmap codec tuple")
- }
- sr, err := strconv.Atoi(codecParts[1])
- if err != nil {
- return out, fmt.Errorf("invalid rtpmap sample rate: %w", err)
- }
- ch := 1
- if len(codecParts) >= 3 {
- ch, err = strconv.Atoi(codecParts[2])
- if err != nil {
- return out, fmt.Errorf("invalid rtpmap channel count: %w", err)
- }
- }
- out.PayloadType = uint8(pt)
- out.Encoding = codecParts[0]
- out.SampleRateHz = sr
- out.Channels = ch
-
- case strings.HasPrefix(line, "a=ptime:"):
- ms, err := strconv.Atoi(strings.TrimPrefix(line, "a=ptime:"))
- if err == nil {
- out.PacketTimeMS = ms
- }
- }
- }
- if out.MulticastGroup == "" || out.Port == 0 || out.Encoding == "" || out.SampleRateHz == 0 {
- return out, fmt.Errorf("incomplete SDP: %+v", out)
- }
- return out, nil
- }
-
- func ConfigFromSDP(base Config, info SDPInfo) (Config, error) {
- cfg := base
- cfg.MulticastGroup = info.MulticastGroup
- cfg.Port = info.Port
- cfg.PayloadType = info.PayloadType
- cfg.SampleRateHz = info.SampleRateHz
- cfg.Channels = info.Channels
- cfg.Encoding = info.Encoding
- if info.PacketTimeMS > 0 {
- cfg.PacketTime = time.Duration(info.PacketTimeMS) * time.Millisecond
- }
- return cfg, cfg.Validate()
- }
|