golang实现会话描述协议(SDP)解析与生成的插件库sdp的使用

Golang实现会话描述协议(SDP)解析与生成的插件库sdp的使用

简介

Package sdp实现了SDP: 会话描述协议 [RFC4566]。作为核心包,它遵循gortc原则。

![Master status] ![codecov] ![GoDoc]

示例

SDP示例

v=0
o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
s=SDP Seminar
i=A Seminar on the session description protocol
u=http://www.example.com/seminars/sdp.pdf
e=j.doe@example.com (Jane Doe)
p=12345
c=IN IP4 224.2.17.12/127
b=CT:154798
t=2873397496 2873404696
r=7d 1h 0 25h
k=clear:ab8c4df8b8f4as8v8iuy8re
a=recvonly
m=audio 49170 RTP/AVP 0
m=video 51372 RTP/AVP 99
b=AS:66781
k=prompt
a=rtpmap:99 h263-1998/90000

编码示例

package main

import (
	"fmt"
	"net"
	"time"
	
	"gortc.io/sdp"
)

func main()  {
	var (
		s sdp.Session
		b []byte
	)
	// 定义媒体
	audio := sdp.Media{
		Description: sdp.MediaDescription{
			Type:     "audio",
			Port:     49170,
			Formats:   []string{"0"},
			Protocol: "RTP/AVP",
		},
	}
	video := sdp.Media{
		Description: sdp.MediaDescription{
			Type:     "video",
			Port:     51372,
			Formats:   []string{"99"},
			Protocol: "RTP/AVP",
		},
		Bandwidths: sdp.Bandwidths{
			sdp.B bandWidthApplicationSpecific: 66781,
		},
		Encryption: sdp.Encryption{
			Method: "prompt",
		},
	}
	video.AddAttribute("rtpmap", "99", "h263-1998/90000")

	// 定义消息
	m := &sdp.Message{
		Origin: sdp.Origin{
			Username:       "jdoe",
			SessionID:      2890844526,
			SessionVersion: 2890842807,
			Address:        "10.47.16.5",
		},
		Name:  "SDP Seminar",
		Info:  "A Seminar on the session description protocol",
		URI:   "http://www.example.com/seminars/sdp.pdf",
		Email: "j.doe@example.com (Jane Doe)",
		Phone: "12345",
		Connection: sdp.ConnectionData{
			IP:  net.ParseIP("224.2.17.12"),
			TTL: 127,
		},
		Bandwidths: sdp.Bandwidths{
			sdp.BandwidthConferenceTotal: 154798,
		},
		Timing: []sdp.Timing{
			{
				Start:  sdp.NTPToTime(2873397496),
				End:    sdp.NTPToTime(2873404696),
				Repeat: 7 * time.Hour * 24,
				Active: 3600 * time.Second,
				Offsets: []time.Duration{
					0,
					25 * time.Hour,
				},
			},
		},
		Encryption: sdp.Encryption{
			Method: "clear",
			Key: "ab8c4df8b8f4as8v8iuy8re",
		},
		Medias: []sdp.Media{audio, video},
	}
	m.AddFlag("recvonly")

	// 将消息添加到会话
	s = m.Append(s)

	// 将会话添加到字节缓冲区
	b = s.AppendTo(b)
	fmt.Println(string(b))
}

解码示例

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	"gortc.io/sdp"
)

func main() {
	name := "example.sdp"
	if len(os.Args) > 1 {
		name = os.Args[1]
	}
	var (
		s   sdp.Session
		b   []byte
		err error
		f   io.ReadCloser
	)
	fmt.Println("sdp file:", name)
	if f, err = os.Open(name); err != nil {
		log.Fatal("err:", err)
	}
	defer f.Close()
	if b, err = ioutil.ReadAll(f); err != nil {
		log.Fatal("err:", err)
	}
	if s, err = sdp.DecodeSession(b, s); err != nil {
		log.Fatal("err:", err)
	}
	for k, v := range s {
		fmt.Println(k, v)
	}
	d := sdp.NewDecoder(s)
	m := new(sdp.Message)
	if err = d.Decode(m); err != nil {
		log.Fatal("err:", err)
	}
	fmt.Println("Decoded session", m.Name)
	fmt.Println("Info:", m.Info)
	fmt.Println("Origin:", m.Origin)
}

直接使用Session结构

package main

import (
	"fmt"

	"gortc.io/sdp"
)

func main() {
	var (
		s sdp.Session
		b []byte
	)
	b = s.AddVersion(0).
		AddMediaDescription(sdp.MediaDescription{
			Type:     "video",
			Port:     51372,
			Formats:   []string{"99"},
			Protocol: "RTP/AVP",
		}).
		AddAttribute("rtpmap", "99", "h263-1998/90000").
		AddLine(sdp.TypeEmail, "test@test.com").
		AddRaw('ü', "vαlue").
		AppendTo(b)
	// 等等
	fmt.Println(string(b))
	// 输出:
	//	v=0
	//	m=video 51372 RTP/AVP 99
	//	a=rtpmap:99 h263-1998/90000
	//	e=test@test.com
	//	ü=vαlue
}

支持的参数

  • [x] v (协议版本)
  • [x] o (发起者和会话标识符)
  • [x] s (会话名称)
  • [x] i (会话信息)
  • [x] u (描述URI)
  • [x] e (电子邮件地址)
  • [x] p (电话号码)
  • [x] c (连接信息)
  • [x] b (零个或多个带宽信息行)
  • [x] t (时间)
  • [x] r (重复)
  • [x] z (时区调整)
  • [x] k (加密密钥)
  • [x] a (零个或多个会话属性行)
  • [x] m (媒体名称和传输地址)

性能基准

goos: linux
goarch: amd64
pkg: github.com/gortc/sdp
PASS
benchmark                                    iter       time/iter   bytes alloc         allocs
---------                                    ----       ---------   -----------         ------
BenchmarkDecoder_Decode-12                 300000   4884.00 ns/op     3166 B/op   93 allocs/op
BenchmarkEncode-12                        1000000   1577.00 ns/op        0 B/op    0 allocs/op
BenchmarkSession_AddConnectionData-12    20000000    114.00 ns/op        0 B/op    0 allocs/op
BenchmarkAppendIP-12                     50000000     37.90 ns/op        0 B/op    0 allocs/op
BenchmarkAppendByte-12                  100000000     11.00 ns/op        0 B/op    0 allocs/op
BenchmarkAppendInt-12                   100000000     11.90 ns/op        0 B/op    0 allocs/op
BenchmarkSession_EX1-12                   3000000    578.00 ns/op       16 B/op    1 allocs/op
BenchmarkAppendRune-12                  200000000      6.70 ns/op        0 B/op    0 allocs/op
BenchmarkDecode-12                      100000000     13.10 ns/op        0 B/op    0 allocs/op
BenchmarkDecodeSession-12                 5000000    234.00 ns/op        0 B/op    0 allocs/op
ok  	github.com/gortc/sdp	16.820s

更多关于golang实现会话描述协议(SDP)解析与生成的插件库sdp的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现会话描述协议(SDP)解析与生成的插件库sdp的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang SDP 解析与生成库使用指南

会话描述协议(SDP)是多媒体会话描述的标准格式,常用于WebRTC等实时通信场景。在Go语言中,github.com/pion/sdp是一个优秀的SDP解析与生成库。

安装

go get github.com/pion/sdp/v3

基本用法

解析SDP

package main

import (
	"fmt"
	"github.com/pion/sdp/v3"
)

func main() {
	rawSDP := `v=0
o=- 0 0 IN IP4 127.0.0.1
s=-
c=IN IP4 127.0.0.1
t=0 0
m=audio 4000 RTP/AVP 96
a=rtpmap:96 opus/48000/2
`

	s := sdp.SessionDescription{}
	if err := s.Unmarshal([]byte(rawSDP)); err != nil {
		fmt.Printf("解析SDP失败: %v\n", err)
		return
	}

	fmt.Printf("解析成功! 版本: %s\n", s.Version)
	for _, m := range s.MediaDescriptions {
		fmt.Printf("媒体类型: %s, 端口: %d, 协议: %s\n", 
			m.MediaName.Media, m.MediaName.Port.Value, m.MediaName.Protos[0])
	}
}

生成SDP

package main

import (
	"fmt"
	"github.com/pion/sdp/v3"
)

func main() {
	s := sdp.SessionDescription{
		Version: 0,
		Origin: sdp.Origin{
			Username:       "-",
			SessionID:      12345,
			SessionVersion: 0,
			NetworkType:    "IN",
			AddressType:    "IP4",
			UnicastAddress: "127.0.0.1",
		},
		SessionName: "Example SDP",
		ConnectionInformation: &sdp.ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address:     &sdp.Address{Address: "127.0.0.1"},
		},
		TimeDescriptions: []sdp.TimeDescription{
			{Timing: sdp.Timing{StartTime: 0, StopTime: 0}},
		},
		MediaDescriptions: []*sdp.MediaDescription{
			{
				MediaName: sdp.MediaName{
					Media:   "audio",
					Port:    sdp.RangedPort{Value: 4000},
					Protos:  []string{"RTP", "AVP"},
					Formats: []string{"96"},
				},
				Attributes: []sdp.Attribute{
					{Key: "rtpmap", Value: "96 opus/48000/2"},
				},
			},
		},
	}

	output, err := s.Marshal()
	if err != nil {
		fmt.Printf("生成SDP失败: %v\n", err)
		return
	}

	fmt.Printf("生成的SDP:\n%s\n", string(output))
}

高级功能

处理ICE候选信息

func addICECandidate(s *sdp.SessionDescription) {
	for _, m := range s.MediaDescriptions {
		m.Attributes = append(m.Attributes, sdp.Attribute{
			Key: "candidate",
			Value: "foundation 1 udp 2130706431 192.168.1.1 4000 typ host",
		})
	}
}

处理SSRC

func addSSRC(m *sdp.MediaDescription, ssrc uint32) {
	m.Attributes = append(m.Attributes, sdp.Attribute{
		Key: "ssrc",
		Value: fmt.Sprintf("%d cname:user@example.com", ssrc),
	})
}

处理带宽信息

func addBandwidth(s *sdp.SessionDescription, bwType string, value int) {
	s.Bandwidth = append(s.Bandwidth, sdp.Bandwidth{
		Experimental: false,
		Type:        bwType,
		Bandwidth:   uint64(value),
	})
}

实际应用示例

package main

import (
	"fmt"
	"github.com/pion/sdp/v3"
)

func createWebRTCSDP() string {
	s := sdp.SessionDescription{
		Version: 0,
		Origin: sdp.Origin{
			Username:       "-",
			SessionID:      123456789,
			SessionVersion: 1,
			NetworkType:    "IN",
			AddressType:    "IP4",
			UnicastAddress: "127.0.0.1",
		},
		SessionName: "WebRTC Session",
		ConnectionInformation: &sdp.ConnectionInformation{
			NetworkType: "IN",
			AddressType: "IP4",
			Address:     &sdp.Address{Address: "0.0.0.0"},
		},
		TimeDescriptions: []sdp.TimeDescription{
			{Timing: sdp.Timing{StartTime: 0, StopTime: 0}},
		},
		MediaDescriptions: []*sdp.MediaDescription{
			{
				MediaName: sdp.MediaName{
					Media:   "audio",
					Port:    sdp.RangedPort{Value: 9},
					Protos:  []string{"UDP", "TLS", "RTP", "SAVPF"},
					Formats: []string{"111", "103", "104", "9", "0", "8", "106", "105", "13", "110", "112", "113", "126"},
				},
				Attributes: []sdp.Attribute{
					{Key: "rtpmap", Value: "111 opus/48000/2"},
					{Key: "fmtp", Value: "111 minptime=10;useinbandfec=1"},
					{Key: "rtpmap", Value: "103 ISAC/16000"},
					{Key: "rtpmap", Value: "104 ISAC/32000"},
					{Key: "rtpmap", Value: "9 G722/8000"},
					{Key: "rtpmap", Value: "0 PCMU/8000"},
					{Key: "rtpmap", Value: "8 PCMA/8000"},
					{Key: "rtpmap", Value: "106 CN/32000"},
					{Key: "rtpmap", Value: "105 CN/16000"},
					{Key: "rtpmap", Value: "13 CN/8000"},
					{Key: "rtpmap", Value: "110 telephone-event/48000"},
					{Key: "rtpmap", Value: "112 telephone-event/32000"},
					{Key: "rtpmap", Value: "113 telephone-event/16000"},
					{Key: "rtpmap", Value: "126 telephone-event/8000"},
					{Key: "ssrc", Value: "12345678 cname:user@example.com"},
					{Key: "ice-ufrag", Value: "abc123"},
					{Key: "ice-pwd", Value: "def456"},
					{Key: "fingerprint", Value: "sha-256 12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF"},
					{Key: "setup", Value: "actpass"},
					{Key: "mid", Value: "audio"},
					{Key: "sendrecv"},
				},
			},
		},
	}

	output, _ := s.Marshal()
	return string(output)
}

func main() {
	sdpStr := createWebRTCSDP()
	fmt.Println("生成的WebRTC SDP:")
	fmt.Println(sdpStr)
}

注意事项

  1. 解析时注意错误处理,SDP格式严格
  2. 生成时确保必填字段完整(version, origin, session name等)
  3. 对于WebRTC应用,需要包含ICE和DTLS相关属性
  4. 媒体描述的顺序会影响协商结果

pion/sdp库提供了完整的SDP解析和生成能力,适合各种实时通信应用的开发。

回到顶部