mirror of
				https://github.com/Monibuca/plugin-rtsp.git
				synced 2025-10-31 10:56:41 +08:00 
			
		
		
		
	Compare commits
	
		
			26 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f9f5966750 | ||
|   | 7ff13d89ce | ||
|   | a9cb4cd853 | ||
|   | 7e61ba71f7 | ||
|   | d6384dcbd5 | ||
|   | 2159a6fd9b | ||
|   | 02f3e91085 | ||
|   | 7f40078b50 | ||
|   | bb563d64c7 | ||
|   | f7cb146b89 | ||
|   | 9bb49cb9f7 | ||
|   | 087d1aab4d | ||
|   | f949464328 | ||
|   | d89f1e2405 | ||
|   | 1d3fbfc20b | ||
|   | fd64a69a12 | ||
|   | 0e4406ad14 | ||
|   | 22f33886a9 | ||
|   | 8b1892209d | ||
|   | 2e9cf9a4ca | ||
|   | 67da93d8e2 | ||
|   | cb733b368f | ||
|   | fadeccddab | ||
|   | 93df7632a6 | ||
|   | 53c4788df2 | ||
|   | f5bdd6a298 | 
| @@ -13,12 +13,15 @@ ListenAddr  = ":554" | ||||
| BufferLength  = 2048 | ||||
| AutoPull     = false | ||||
| RemoteAddr   = "rtsp://localhost/${streamPath}" | ||||
| [[RTSP.AutoPullList]] | ||||
| URL = "rtsp://admin:admin@192.168.1.212:554/cam/realmonitor?channel=1&subtype=1" | ||||
| StreamPath = "live/rtsp" | ||||
| ``` | ||||
| - ListenAddr 是监听端口,可以将rtsp流推到Monibuca中 | ||||
| - BufferLength是指解析拉取的rtp包的缓冲大小 | ||||
| - AutoPull是指当有用户订阅一个新流的时候自动向远程拉流转发 | ||||
| - RemoteAddr 指远程拉流地址,其中${streamPath}是占位符,实际使用流路径替换。 | ||||
|  | ||||
| - AutoPullList 是一个数组,如果配置了该数组,则会在程序启动时自动启动拉流,StreamPath一定要是唯一的,不能重复 | ||||
|  | ||||
| ## 使用方法(拉流转发) | ||||
| ```go | ||||
|   | ||||
							
								
								
									
										201
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										201
									
								
								client.go
									
									
									
									
									
								
							| @@ -4,6 +4,7 @@ import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| @@ -16,11 +17,12 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	. "github.com/Monibuca/engine/v2" | ||||
| 	. "github.com/Monibuca/plugin-rtp" | ||||
| ) | ||||
|  | ||||
| // PullStream 从外部拉流 | ||||
| func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (err error) { | ||||
| 	if result := rtsp.Publisher.Publish(streamPath); result { | ||||
| 	if result := rtsp.Publish(streamPath); result { | ||||
| 		rtsp.Stream.Type = "RTSP" | ||||
| 		rtsp.RTSPInfo.StreamInfo = &rtsp.Stream.StreamInfo | ||||
| 		rtsp.TransType = TRANS_TYPE_TCP | ||||
| @@ -29,6 +31,7 @@ func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (err error) { | ||||
| 		rtsp.aRTPChannel = 2 | ||||
| 		rtsp.aRTPControlChannel = 3 | ||||
| 		rtsp.URL = rtspUrl | ||||
| 		rtsp.UDPServer = &UDPServer{Session: rtsp} | ||||
| 		if err = rtsp.requestStream(); err != nil { | ||||
| 			Println(err) | ||||
| 			rtsp.Close() | ||||
| @@ -77,6 +80,18 @@ func DigestAuth(authLine string, method string, URL string) (string, error) { | ||||
| 	Authorization := fmt.Sprintf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", username, realm, nonce, l.String(), response) | ||||
| 	return Authorization, nil | ||||
| } | ||||
| // auth Basic验证 | ||||
| func BasicAuth(authLine string, method string, URL string) (string, error) { | ||||
| 	l, err := url.Parse(URL) | ||||
| 	if err != nil { | ||||
| 		return "", fmt.Errorf("Url parse error:%v,%v", URL, err) | ||||
| 	} | ||||
| 	username := l.User.Username() | ||||
| 	password, _ := l.User.Password() | ||||
| 	userAndpass := []byte(username + ":" + password) | ||||
| 	Authorization := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString(userAndpass)) | ||||
| 	return Authorization, nil | ||||
| } | ||||
| func (client *RTSP) checkAuth(method string, resp *Response) (string, error) { | ||||
| 	if resp.StatusCode == 401 { | ||||
| 		// need auth. | ||||
| @@ -90,8 +105,7 @@ func (client *RTSP) checkAuth(method string, resp *Response) (string, error) { | ||||
| 					client.authLine = authLine | ||||
| 					return DigestAuth(authLine, method, client.URL) | ||||
| 				} else if strings.IndexAny(authLine, "Basic") == 0 { | ||||
| 					// not support yet | ||||
| 					// TODO.. | ||||
| 					return BasicAuth(authLine, method, client.URL) | ||||
| 				} | ||||
| 			} | ||||
| 			return "", fmt.Errorf("auth error") | ||||
| @@ -101,9 +115,7 @@ func (client *RTSP) checkAuth(method string, resp *Response) (string, error) { | ||||
| 				client.authLine = authLine | ||||
| 				return DigestAuth(authLine, method, client.URL) | ||||
| 			} else if strings.IndexAny(authLine, "Basic") == 0 { | ||||
| 				// not support yet | ||||
| 				// TODO.. | ||||
| 				return "", fmt.Errorf("not support Basic auth yet") | ||||
| 				return BasicAuth(authLine, method, client.URL) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -143,7 +155,7 @@ func (client *RTSP) requestStream() (err error) { | ||||
| 	client.connRW = bufio.NewReadWriter(bufio.NewReaderSize(&timeoutConn, networkBuffer), bufio.NewWriterSize(&timeoutConn, networkBuffer)) | ||||
|  | ||||
| 	headers := make(map[string]string) | ||||
| 	headers["Require"] = "implicit-play" | ||||
| 	//headers["Require"] = "implicit-play" | ||||
| 	// An OPTIONS request returns the request types the server will accept. | ||||
| 	resp, err := client.Request("OPTIONS", headers) | ||||
| 	if err != nil { | ||||
| @@ -188,73 +200,60 @@ func (client *RTSP) requestStream() (err error) { | ||||
| 	} | ||||
| 	client.SDPRaw = resp.Body | ||||
| 	client.SDPMap = ParseSDP(client.SDPRaw) | ||||
| 	client.VSdp, client.HasVideo = client.SDPMap["video"] | ||||
| 	client.ASdp, client.HasAudio = client.SDPMap["audio"] | ||||
| 	session := "" | ||||
| 	if videoInfo, ok := client.SDPMap["video"]; ok { | ||||
| 		client.VControl = videoInfo.Control | ||||
| 		client.VCodec = videoInfo.Codec | ||||
| 		client.SPS = videoInfo.SpropParameterSets[0] | ||||
| 		client.PPS = videoInfo.SpropParameterSets[1] | ||||
| 		var _url = "" | ||||
| 		if strings.Index(strings.ToLower(client.VControl), "rtsp://") == 0 { | ||||
| 			_url = client.VControl | ||||
| 		} else { | ||||
| 			_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.VControl, "/") | ||||
| 		} | ||||
| 	otherChannel := 4 | ||||
| 	for t, sdpInfo := range client.SDPMap { | ||||
| 		headers = make(map[string]string) | ||||
| 		if client.TransType == TRANS_TYPE_TCP { | ||||
| 			headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel) | ||||
| 		} else { | ||||
| 			if client.UDPServer == nil { | ||||
| 				client.UDPServer = &UDPServer{Session: client} | ||||
| 			} | ||||
| 			//RTP/AVP;unicast;client_port=64864-64865 | ||||
| 			err = client.UDPServer.SetupVideo() | ||||
| 			if err != nil { | ||||
| 				Printf("Setup video err.%v", err) | ||||
| 				return err | ||||
| 			} | ||||
| 			headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort) | ||||
| 			client.Conn.timeout = 0 //	UDP ignore timeout | ||||
| 		} | ||||
| 		if session != "" { | ||||
| 			headers["Session"] = session | ||||
| 		} | ||||
| 		Printf("Parse DESCRIBE response, VIDEO VControl:%s, VCode:%s, url:%s,Session:%s,vRTPChannel:%d,vRTPControlChannel:%d", client.VControl, client.VCodec, _url, session, client.vRTPChannel, client.vRTPControlChannel) | ||||
| 		if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil { | ||||
| 			return err | ||||
| 		var _url = sdpInfo.Control | ||||
| 		if !strings.HasPrefix(strings.ToLower(sdpInfo.Control), "rtsp://") { | ||||
| 			_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(sdpInfo.Control, "/") | ||||
| 		} | ||||
| 		session, _ = resp.Header["Session"].(string) | ||||
| 		session = strings.Split(session, ";")[0] | ||||
| 	} | ||||
| 	if audioInfo, ok := client.SDPMap["audio"]; ok { | ||||
| 		client.AControl = audioInfo.Control | ||||
| 		client.ACodec = audioInfo.Codec | ||||
| 		client.AudioSpecificConfig = audioInfo.Config | ||||
| 		var _url = "" | ||||
| 		if strings.Index(strings.ToLower(client.AControl), "rtsp://") == 0 { | ||||
| 			_url = client.AControl | ||||
| 		} else { | ||||
| 			_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.AControl, "/") | ||||
| 		} | ||||
| 		headers = make(map[string]string) | ||||
| 		if client.TransType == TRANS_TYPE_TCP { | ||||
| 			headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel) | ||||
| 		} else { | ||||
| 			if client.UDPServer == nil { | ||||
| 				client.UDPServer = &UDPServer{Session: client} | ||||
| 		switch t { | ||||
| 		case "video": | ||||
| 			if len(sdpInfo.SpropParameterSets) > 1 { | ||||
| 				client.WriteSPS(sdpInfo.SpropParameterSets[0]) | ||||
| 				client.WritePPS(sdpInfo.SpropParameterSets[1]) | ||||
| 			} | ||||
| 			err = client.UDPServer.SetupAudio() | ||||
| 			if err != nil { | ||||
| 				Printf("Setup audio err.%v", err) | ||||
| 				return err | ||||
| 			if client.TransType == TRANS_TYPE_TCP { | ||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel) | ||||
| 			} else { | ||||
| 				//RTP/AVP;unicast;client_port=64864-64865 | ||||
| 				if err = client.UDPServer.SetupVideo(); err != nil { | ||||
| 					Printf("Setup video err.%v", err) | ||||
| 					return err | ||||
| 				} | ||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort) | ||||
| 				client.Conn.timeout = 0 //	UDP ignore timeout | ||||
| 			} | ||||
| 		case "audio": | ||||
| 			if len(sdpInfo.Config) > 0 { | ||||
| 				client.WriteASC(sdpInfo.Config) | ||||
| 			}else{ | ||||
| 				client.setAudioFormat() | ||||
| 			} | ||||
| 			if client.TransType == TRANS_TYPE_TCP { | ||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel) | ||||
| 			} else { | ||||
| 				if err = client.UDPServer.SetupAudio(); err != nil { | ||||
| 					Printf("Setup audio err.%v", err) | ||||
| 					return err | ||||
| 				} | ||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort) | ||||
| 				client.Conn.timeout = 0 //	UDP ignore timeout | ||||
| 			} | ||||
| 		default: | ||||
| 			if client.TransType == TRANS_TYPE_TCP { | ||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", otherChannel, otherChannel+1) | ||||
| 				otherChannel += 2 | ||||
| 			} else { | ||||
| 				//TODO: UDP support | ||||
| 			} | ||||
| 			headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort) | ||||
| 			client.Conn.timeout = 0 //	UDP ignore timeout | ||||
| 		} | ||||
| 		if session != "" { | ||||
| 			headers["Session"] = session | ||||
| 		} | ||||
| 		Printf("Parse DESCRIBE response, AUDIO AControl:%s, ACodec:%s, url:%s,Session:%s, aRTPChannel:%d,aRTPControlChannel:%d", client.AControl, client.ACodec, _url, session, client.aRTPChannel, client.aRTPControlChannel) | ||||
| 		if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -264,27 +263,51 @@ func (client *RTSP) requestStream() (err error) { | ||||
| 	headers = make(map[string]string) | ||||
| 	if session != "" { | ||||
| 		headers["Session"] = session | ||||
| 		client.Session = session | ||||
| 	} | ||||
| 	resp, err = client.Request("PLAY", headers) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (client *RTSP) startStream() { | ||||
| 	//startTime := time.Now() | ||||
| 	startTime := time.Now() | ||||
| 	//loggerTime := time.Now().Add(-10 * time.Second) | ||||
| 	defer client.Stop() | ||||
| 	defer func() { | ||||
| 		if client.Err() == nil && config.Reconnect { | ||||
| 			Printf("reconnecting:%s", client.URL) | ||||
| 			client.RTSPClientInfo = RTSPClientInfo{} | ||||
| 			if err := client.requestStream(); err != nil { | ||||
| 				t := time.NewTicker(time.Second * 5) | ||||
| 				for { | ||||
| 					Printf("reconnecting:%s in 5 seconds", client.URL) | ||||
| 					select { | ||||
| 					case <-client.Done(): | ||||
| 						client.Stop() | ||||
| 						return | ||||
| 					case <-t.C: | ||||
| 						if err = client.requestStream(); err == nil { | ||||
| 							go client.startStream() | ||||
| 							return | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				go client.startStream() | ||||
| 			} | ||||
| 		} else { | ||||
| 			client.Stop() | ||||
| 		} | ||||
| 	}() | ||||
| 	for client.Err() == nil { | ||||
| 		//if client.OptionIntervalMillis > 0 { | ||||
| 		//	if time.Since(startTime) > time.Duration(client.OptionIntervalMillis)*time.Millisecond { | ||||
| 		//		startTime = time.Now() | ||||
| 		//		headers := make(map[string]string) | ||||
| 		//		headers["Require"] = "implicit-play" | ||||
| 		//		// An OPTIONS request returns the request types the server will accept. | ||||
| 		//		if err := client.RequestNoResp("OPTIONS", headers); err != nil { | ||||
| 		//			// ignore... | ||||
| 		//		} | ||||
| 		//	} | ||||
| 		//} | ||||
| 		if time.Since(startTime) > time.Minute { | ||||
| 			startTime = time.Now() | ||||
| 			headers := make(map[string]string) | ||||
| 			headers["Require"] = "implicit-play" | ||||
| 			// An OPTIONS request returns the request types the server will accept. | ||||
| 			if err := client.RequestNoResp("GET_PARAMETER", headers); err != nil { | ||||
| 				// ignore... | ||||
| 			} | ||||
| 		} | ||||
| 		b, err := client.connRW.ReadByte() | ||||
| 		if err != nil { | ||||
| 			Printf("client.connRW.ReadByte err:%v", err) | ||||
| @@ -307,38 +330,30 @@ func (client *RTSP) startStream() { | ||||
| 				Printf("io.ReadFull err:%v", err) | ||||
| 				return | ||||
| 			} | ||||
| 			rtpBuf := content | ||||
| 			var pack *RTPPack | ||||
|  | ||||
| 			switch channel { | ||||
| 			case client.aRTPChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_AUDIO, | ||||
| 					Buffer: rtpBuf, | ||||
| 					Type: RTP_TYPE_AUDIO, | ||||
| 				} | ||||
| 			case client.aRTPControlChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_AUDIOCONTROL, | ||||
| 					Buffer: rtpBuf, | ||||
| 					Type: RTP_TYPE_AUDIOCONTROL, | ||||
| 				} | ||||
| 			case client.vRTPChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_VIDEO, | ||||
| 					Buffer: rtpBuf, | ||||
| 					Type: RTP_TYPE_VIDEO, | ||||
| 				} | ||||
| 			case client.vRTPControlChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_VIDEOCONTROL, | ||||
| 					Buffer: rtpBuf, | ||||
| 					Type: RTP_TYPE_VIDEOCONTROL, | ||||
| 				} | ||||
| 			default: | ||||
| 				Printf("unknow rtp pack type, channel:%v", channel) | ||||
| 				continue | ||||
| 			} | ||||
| 			if pack == nil { | ||||
| 				Printf("session tcp got nil rtp pack") | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			pack.Unmarshal(content) | ||||
| 			//if client.debugLogEnable { | ||||
| 			//	rtp := ParseRTP(pack.Buffer) | ||||
| 			//	if rtp != nil { | ||||
| @@ -357,7 +372,7 @@ func (client *RTSP) startStream() { | ||||
| 			//} | ||||
|  | ||||
| 			client.InBytes += int(length + 4) | ||||
| 			client.handleRTP(pack) | ||||
| 			client.PushPack(pack) | ||||
|  | ||||
| 		default: // rtsp | ||||
| 			builder := bytes.Buffer{} | ||||
|   | ||||
							
								
								
									
										16
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,14 +1,14 @@ | ||||
| module github.com/Monibuca/plugin-rtsp | ||||
|  | ||||
| go 1.13 | ||||
| go 1.16 | ||||
|  | ||||
| require ( | ||||
| 	github.com/Monibuca/engine/v2 v2.0.0 | ||||
| 	github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect | ||||
| 	github.com/gobwas/pool v0.2.0 // indirect | ||||
| 	github.com/gobwas/ws v1.0.3 // indirect | ||||
| 	github.com/jinzhu/gorm v1.9.12 // indirect | ||||
| 	github.com/pixelbender/go-sdp v1.0.0 | ||||
| 	github.com/reactivex/rxgo v1.0.0 // indirect | ||||
| 	github.com/Monibuca/engine/v2 v2.4.0 | ||||
| 	github.com/Monibuca/plugin-rtp v1.0.0 | ||||
| 	github.com/logrusorgru/aurora v2.0.3+incompatible // indirect | ||||
| 	github.com/mattn/go-colorable v0.1.7 // indirect | ||||
| 	github.com/pion/rtp v1.6.0 // indirect | ||||
| 	github.com/shirou/gopsutil v2.20.7+incompatible // indirect | ||||
| 	github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf | ||||
| 	golang.org/x/sys v0.0.0-20200828161417-c663848e9a16 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										76
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,87 +1,51 @@ | ||||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/EasyDarwin/EasyDarwin v8.1.0+incompatible h1:Rr8dRbZtcJhiJvGx5Vs7IENM6RUUwGkZiIj5+WrNhm8= | ||||
| github.com/EasyDarwin/EasyDarwin v8.1.0+incompatible/go.mod h1:xnmC+Q2+wugEDpQGxivSFNYPOhmNlIQHBfl0hMeriSU= | ||||
| github.com/Monibuca/engine v1.2.1 h1:TJmC6eZA1lR1MScWgempZLiEZD4T6aY/nn/rlQ9UdK8= | ||||
| github.com/Monibuca/engine v1.2.1/go.mod h1:WbDkXENLjcPjyjCR1Mix1GA+uAlwORkv/+8aMVrDX2g= | ||||
| github.com/Monibuca/engine v1.2.2 h1:hNjsrZpOmui8lYhgCJ5ltJU8g/k0Rrdysx2tHNGGnbI= | ||||
| github.com/Monibuca/engine/v2 v2.0.0 h1:8FjaScrtN8QdbcxO9zZYABMC0Re3I1O1T4p94zAXYb0= | ||||
| github.com/Monibuca/engine/v2 v2.0.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.2.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.4.0 h1:aa647MW5ToMdVQlB4nehfc3Vcos6752aLYRAkRnMLqo= | ||||
| github.com/Monibuca/engine/v2 v2.4.0/go.mod h1:LBuAJFcTtUjVsGKWcUKQpIftRECf7Ii9DfuGWHL3Ngg= | ||||
| github.com/Monibuca/plugin-rtp v1.0.0 h1:yksNsIIGxoKX8UZirkAUK+mGZ/XoEeS2vqbIqtqXyCg= | ||||
| github.com/Monibuca/plugin-rtp v1.0.0/go.mod h1:0xkNm23a/BjVnEMz1zXyOqfEjoVmGe3PJqPNF1KyFGc= | ||||
| github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= | ||||
| github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= | ||||
| github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= | ||||
| github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= | ||||
| github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= | ||||
| github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= | ||||
| github.com/falconray0704/gortmp v0.0.0-20170613085150-e3f9bb02c7c8 h1:Bkx+0neYCcHW7BUeVCbR2GOn47NesdImh8nHHOKccD4= | ||||
| github.com/falconray0704/gortmp v0.0.0-20170613085150-e3f9bb02c7c8/go.mod h1:/JBZajtCDe9Z4j84v5QWo4PLn1K6jcBHh6qXN/bm/vw= | ||||
| github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0= | ||||
| github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo= | ||||
| github.com/funny/utest v0.0.0-20161029064919-43870a374500 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg= | ||||
| github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg= | ||||
| github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= | ||||
| github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= | ||||
| github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= | ||||
| github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= | ||||
| github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= | ||||
| github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= | ||||
| github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= | ||||
| github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= | ||||
| github.com/gobwas/ws v1.0.3 h1:ZOigqf7iBxkA4jdQ3am7ATzdlOFp9YzA6NmuvEEZc9g= | ||||
| github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= | ||||
| github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= | ||||
| github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= | ||||
| github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= | ||||
| github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= | ||||
| github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= | ||||
| github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= | ||||
| github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= | ||||
| github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= | ||||
| github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | ||||
| github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= | ||||
| github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | ||||
| github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= | ||||
| github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= | ||||
| github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | ||||
| github.com/mask-pp/rtp-ps v1.0.0 h1:JFxuJL9N+gD1ldgJlAy3b7rYfY8wAVHi9ODNmdP4+EE= | ||||
| github.com/mask-pp/rtp-ps v1.0.0/go.mod h1:jCxsZ2G7z/jX+aqFypEWMePnhNrfnUiXUEKm6Xp0vgU= | ||||
| github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= | ||||
| github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= | ||||
| github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= | ||||
| github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | ||||
| github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | ||||
| github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= | ||||
| github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | ||||
| github.com/pixelbender/go-sdp v1.0.0 h1:hLP2ALBN4sLpgp2r3EDcFUSN3AyOkg1jonuWEJniotY= | ||||
| github.com/pixelbender/go-sdp v1.0.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs= | ||||
| github.com/pion/randutil v0.0.0 h1:aLWLVhTG2jzoD25F0OlW6nXvXrjoGwiXq2Sz7j7NzL0= | ||||
| github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= | ||||
| github.com/pion/rtp v1.5.4/go.mod h1:bg60AL5GotNOlYZsqycbhDtEV3TkfbpXG0KBiUq29Mg= | ||||
| github.com/pion/rtp v1.6.0 h1:4Ssnl/T5W2LzxHj9ssYpGVEQh3YYhQFNVmSWO88MMwk= | ||||
| github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/reactivex/rxgo v1.0.0 h1:qpT8/kVwAJDSeGsqx4oUXxgk3UCtAq/EreBGWYRxEcA= | ||||
| github.com/reactivex/rxgo v1.0.0/go.mod h1:/S1ygE20oE1BvZGIwd3fXx/m6s6pOX5G6zmXg9ninlQ= | ||||
| github.com/shirou/gopsutil v2.20.1+incompatible h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY= | ||||
| github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||||
| github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= | ||||
| github.com/shirou/gopsutil v2.20.7+incompatible h1:Ymv4OD12d6zm+2yONe39VSmp2XooJe8za7ngOLW/o/w= | ||||
| github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= | ||||
| github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= | ||||
| github.com/zhangpeihao/goamf v0.0.0-20140409082417-3ff2c19514a8/go.mod h1:RZd/IqzNpFANwOB9rVmsnAYpo/6KesK4PqrN1a5cRgg= | ||||
| github.com/zhangpeihao/log v0.0.0-20170117094621-62e921e41859/go.mod h1:OAvmouyIV28taMw4SC4+hSnouObQqQkTQNOhU3Zowl0= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= | ||||
| golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= | ||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| golang.org/x/sys v0.0.0-20200828161417-c663848e9a16 h1:54u1berWyLujz9htI1BHtZpcCEYaYNUSDFLXMNDd7To= | ||||
| golang.org/x/sys v0.0.0-20200828161417-c663848e9a16/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||
|   | ||||
							
								
								
									
										201
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										201
									
								
								main.go
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ package rtsp | ||||
|  | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"embed" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"net" | ||||
| @@ -12,18 +12,26 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	. "github.com/Monibuca/engine/v2" | ||||
| 	. "github.com/Monibuca/engine/v2/avformat" | ||||
| 	"github.com/Monibuca/engine/v2/util" | ||||
| 	. "github.com/Monibuca/plugin-rtp" | ||||
| 	"github.com/teris-io/shortid" | ||||
| ) | ||||
|  | ||||
| var collection = sync.Map{} | ||||
| //go:embed ui/* | ||||
| //go:embed README.md | ||||
| var ui embed.FS | ||||
| var collection sync.Map | ||||
| var config = struct { | ||||
| 	ListenAddr string | ||||
| 	AutoPull   bool | ||||
| 	RemoteAddr string | ||||
| 	Timeout    int | ||||
| }{":554", false, "rtsp://localhost/${streamPath}", 0} | ||||
| 	ListenAddr   string | ||||
| 	AutoPull     bool | ||||
| 	RemoteAddr   string | ||||
| 	Timeout      int | ||||
| 	Reconnect    bool | ||||
| 	AutoPullList []*struct { | ||||
| 		URL        string | ||||
| 		StreamPath string | ||||
| 	} | ||||
| }{":554", false, "rtsp://localhost/${streamPath}", 0, false, nil} | ||||
|  | ||||
| func init() { | ||||
| 	InstallPlugin(&PluginConfig{ | ||||
| @@ -36,6 +44,7 @@ func init() { | ||||
| 				config.AutoPull = value.(bool) | ||||
| 			}, | ||||
| 		}, | ||||
| 		UIFile: &ui, | ||||
| 	}) | ||||
| } | ||||
| func runPlugin() { | ||||
| @@ -59,6 +68,7 @@ func runPlugin() { | ||||
| 		} | ||||
| 	}) | ||||
| 	http.HandleFunc("/rtsp/pull", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Access-Control-Allow-Origin", "*") | ||||
| 		targetURL := r.URL.Query().Get("target") | ||||
| 		streamPath := r.URL.Query().Get("streamPath") | ||||
| 		if err := new(RTSP).PullStream(streamPath, targetURL); err == nil { | ||||
| @@ -67,6 +77,13 @@ func runPlugin() { | ||||
| 			w.Write([]byte(fmt.Sprintf(`{"code":1,"msg":"%s"}`, err.Error()))) | ||||
| 		} | ||||
| 	}) | ||||
| 	if len(config.AutoPullList) > 0 { | ||||
| 		for _, info := range config.AutoPullList { | ||||
| 			if err := new(RTSP).PullStream(info.StreamPath, info.URL); err != nil { | ||||
| 				Println(err) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if config.ListenAddr != "" { | ||||
| 		log.Fatal(ListenRtsp(config.ListenAddr)) | ||||
| 	} | ||||
| @@ -118,7 +135,7 @@ func ListenRtsp(addr string) error { | ||||
| } | ||||
|  | ||||
| type RTSP struct { | ||||
| 	Publisher | ||||
| 	RTP | ||||
| 	RTSPInfo | ||||
| 	RTSPClientInfo | ||||
| 	ID        string | ||||
| @@ -131,27 +148,35 @@ type RTSP struct { | ||||
| 	SDPMap   map[string]*SDPInfo | ||||
| 	nonce    string | ||||
| 	closeOld bool | ||||
| 	AControl string | ||||
| 	VControl string | ||||
| 	ACodec   string | ||||
| 	VCodec   string | ||||
| 	avcsent  bool | ||||
| 	ASdp     *SDPInfo | ||||
| 	VSdp     *SDPInfo | ||||
| 	aacsent  bool | ||||
| 	Timeout  int | ||||
| 	// stats info | ||||
| 	fuBuffer []byte | ||||
| 	//tcp channels | ||||
| 	aRTPChannel         int | ||||
| 	aRTPControlChannel  int | ||||
| 	vRTPChannel         int | ||||
| 	vRTPControlChannel  int | ||||
| 	UDPServer           *UDPServer | ||||
| 	UDPClient           *UDPClient | ||||
| 	SPS                 []byte | ||||
| 	PPS                 []byte | ||||
| 	AudioSpecificConfig []byte | ||||
| 	Auth                func(string) string | ||||
| 	aRTPChannel        int | ||||
| 	aRTPControlChannel int | ||||
| 	vRTPChannel        int | ||||
| 	vRTPControlChannel int | ||||
| 	UDPServer          *UDPServer | ||||
| 	UDPClient          *UDPClient | ||||
| 	Auth               func(string) string | ||||
| } | ||||
|  | ||||
| func (rtsp *RTSP) setAudioFormat() { | ||||
| 	switch rtsp.ASdp.Codec { | ||||
| 	case "aac": | ||||
| 		rtsp.AudioInfo.SoundFormat = 10 | ||||
| 	case "pcma": | ||||
| 		rtsp.AudioInfo.SoundFormat = 7 | ||||
| 		rtsp.AudioInfo.SoundRate = rtsp.ASdp.TimeScale | ||||
| 		rtsp.AudioInfo.SoundSize = 16 | ||||
| 	case "pcmu": | ||||
| 		rtsp.AudioInfo.SoundFormat = 8 | ||||
| 		rtsp.AudioInfo.SoundRate = rtsp.ASdp.TimeScale | ||||
| 		rtsp.AudioInfo.SoundSize = 16 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type RTSPClientInfo struct { | ||||
| 	Agent    string | ||||
| 	Session  string | ||||
| @@ -159,12 +184,10 @@ type RTSPClientInfo struct { | ||||
| 	Seq      int | ||||
| } | ||||
| type RTSPInfo struct { | ||||
| 	URL       string | ||||
| 	SyncCount int64 | ||||
| 	SDPRaw    string | ||||
| 	InBytes   int | ||||
| 	OutBytes  int | ||||
|  | ||||
| 	URL        string | ||||
| 	SDPRaw     string | ||||
| 	InBytes    int | ||||
| 	OutBytes   int | ||||
| 	StreamInfo *StreamInfo | ||||
| } | ||||
|  | ||||
| @@ -192,117 +215,3 @@ func (conn *RichConn) Write(b []byte) (n int, err error) { | ||||
| 	} | ||||
| 	return conn.Conn.Write(b) | ||||
| } | ||||
| func (rtsp *RTSP) handleNALU(nalType byte, payload []byte, ts int64) { | ||||
| 	rtsp.SyncCount++ | ||||
| 	vl := len(payload) | ||||
| 	switch nalType { | ||||
| 	// case NALU_SPS: | ||||
| 	// 	r := bytes.NewBuffer([]byte{}) | ||||
| 	// 	r.Write(RTMP_AVC_HEAD) | ||||
| 	// 	util.BigEndian.PutUint16(spsHead[1:], uint16(vl)) | ||||
| 	// 	r.Write(spsHead) | ||||
| 	// 	r.Write(payload) | ||||
| 	// case NALU_PPS: | ||||
| 	// 	r := bytes.NewBuffer([]byte{}) | ||||
| 	// 	util.BigEndian.PutUint16(ppsHead[1:], uint16(vl)) | ||||
| 	// 	r.Write(ppsHead) | ||||
| 	// 	r.Write(payload) | ||||
| 	// 	rtsp.PushVideo(0, r.Bytes()) | ||||
| 	// avcsent = true | ||||
| 	case NALU_IDR_Picture: | ||||
| 		if !rtsp.avcsent { | ||||
| 			r := bytes.NewBuffer([]byte{}) | ||||
| 			r.Write(RTMP_AVC_HEAD) | ||||
| 			spsHead := []byte{0xE1, 0, 0} | ||||
| 			util.BigEndian.PutUint16(spsHead[1:], uint16(len(rtsp.SPS))) | ||||
| 			r.Write(spsHead) | ||||
| 			r.Write(rtsp.SPS) | ||||
| 			ppsHead := []byte{0x01, 0, 0} | ||||
| 			util.BigEndian.PutUint16(ppsHead[1:], uint16(len(rtsp.PPS))) | ||||
| 			r.Write(ppsHead) | ||||
| 			r.Write(rtsp.PPS) | ||||
| 			rtsp.PushVideo(0, r.Bytes()) | ||||
| 			rtsp.avcsent = true | ||||
| 		} | ||||
| 		r := bytes.NewBuffer([]byte{}) | ||||
| 		iframeHead := []byte{0x17, 0x01, 0, 0, 0} | ||||
| 		util.BigEndian.PutUint24(iframeHead[2:], 0) | ||||
| 		r.Write(iframeHead) | ||||
| 		nalLength := []byte{0, 0, 0, 0} | ||||
| 		util.BigEndian.PutUint32(nalLength, uint32(vl)) | ||||
| 		r.Write(nalLength) | ||||
| 		r.Write(payload) | ||||
| 		rtsp.PushVideo(uint32(ts), r.Bytes()) | ||||
| 	case NALU_Non_IDR_Picture: | ||||
| 		if rtsp.avcsent { | ||||
| 			r := bytes.NewBuffer([]byte{}) | ||||
| 			pframeHead := []byte{0x27, 0x01, 0, 0, 0} | ||||
| 			util.BigEndian.PutUint24(pframeHead[2:], 0) | ||||
| 			r.Write(pframeHead) | ||||
| 			nalLength := []byte{0, 0, 0, 0} | ||||
| 			util.BigEndian.PutUint32(nalLength, uint32(vl)) | ||||
| 			r.Write(nalLength) | ||||
| 			r.Write(payload) | ||||
| 			rtsp.PushVideo(uint32(ts), r.Bytes()) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| func (rtsp *RTSP) handleRTP(pack *RTPPack) { | ||||
| 	data := pack.Buffer | ||||
| 	switch pack.Type { | ||||
| 	case RTP_TYPE_AUDIO: | ||||
| 		if !rtsp.aacsent { | ||||
| 			rtsp.PushAudio(0, append([]byte{0xAF, 0x00}, rtsp.AudioSpecificConfig...)) | ||||
| 			rtsp.aacsent = true | ||||
| 		} | ||||
| 		cc := data[0] & 0xF | ||||
| 		rtphdr := 12 + cc*4 | ||||
| 		payload := data[rtphdr:] | ||||
| 		auHeaderLen := (int16(payload[0]) << 8) + int16(payload[1]) | ||||
| 		auHeaderLen = auHeaderLen >> 3 | ||||
| 		auHeaderCount := int(auHeaderLen / 2) | ||||
| 		var auLenArray []int | ||||
| 		for iIndex := 0; iIndex < int(auHeaderCount); iIndex++ { | ||||
| 			auHeaderInfo := (int16(payload[2+2*iIndex]) << 8) + int16(payload[2+2*iIndex+1]) | ||||
| 			auLen := auHeaderInfo >> 3 | ||||
| 			auLenArray = append(auLenArray, int(auLen)) | ||||
| 		} | ||||
| 		startOffset := 2 + 2*auHeaderCount | ||||
| 		for _, auLen := range auLenArray { | ||||
| 			endOffset := startOffset + auLen | ||||
| 			addHead := []byte{0xAF, 0x01} | ||||
| 			rtsp.PushAudio(0, append(addHead, payload[startOffset:endOffset]...)) | ||||
| 			startOffset = startOffset + auLen | ||||
| 		} | ||||
| 	case RTP_TYPE_VIDEO: | ||||
| 		cc := data[0] & 0xF | ||||
| 		//rtp header | ||||
| 		rtphdr := 12 + cc*4 | ||||
|  | ||||
| 		//packet time | ||||
| 		ts := (int64(data[4]) << 24) + (int64(data[5]) << 16) + (int64(data[6]) << 8) + (int64(data[7])) | ||||
|  | ||||
| 		//packet number | ||||
| 		//packno := (int64(data[6]) << 8) + int64(data[7]) | ||||
| 		data = data[rtphdr:] | ||||
| 		nalType := data[0] & 0x1F | ||||
|  | ||||
| 		if nalType >= 1 && nalType <= 23 { | ||||
| 			rtsp.handleNALU(nalType, data, ts) | ||||
| 		} else if nalType == 28 { | ||||
| 			isStart := data[1]&0x80 != 0 | ||||
| 			isEnd := data[1]&0x40 != 0 | ||||
| 			nalType := data[1] & 0x1F | ||||
| 			//nri := (data[1]&0x60)>>5 | ||||
| 			nal := data[0]&0xE0 | data[1]&0x1F | ||||
| 			if isStart { | ||||
| 				rtsp.fuBuffer = []byte{0} | ||||
| 			} | ||||
| 			rtsp.fuBuffer = append(rtsp.fuBuffer, data[2:]...) | ||||
| 			if isEnd { | ||||
| 				rtsp.fuBuffer[0] = nal | ||||
| 				rtsp.handleNALU(nalType, rtsp.fuBuffer, ts) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -31,17 +31,13 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo { | ||||
| 			switch typeval[0] { | ||||
| 			case "m": | ||||
| 				if len(fields) > 0 { | ||||
| 					switch fields[0] { | ||||
| 					case "audio", "video": | ||||
| 						sdpMap[fields[0]] = &SDPInfo{AVType: fields[0]} | ||||
| 						info = sdpMap[fields[0]] | ||||
| 						mfields := strings.Split(fields[1], " ") | ||||
| 						if len(mfields) >= 3 { | ||||
| 							info.PayloadType, _ = strconv.Atoi(mfields[2]) | ||||
| 						} | ||||
| 					info = &SDPInfo{AVType: fields[0]} | ||||
| 					sdpMap[info.AVType] = info | ||||
| 					mfields := strings.Split(fields[1], " ") | ||||
| 					if len(mfields) >= 3 { | ||||
| 						info.PayloadType, _ = strconv.Atoi(mfields[2]) | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 			case "a": | ||||
| 				if info != nil { | ||||
| 					for _, field := range fields { | ||||
| @@ -60,6 +56,10 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo { | ||||
| 						if len(keyval) >= 2 { | ||||
| 							key := keyval[0] | ||||
| 							switch key { | ||||
| 							case "PCMA": | ||||
| 								info.Codec = "pcma" | ||||
| 							case "PCMU": | ||||
| 								info.Codec = "pcmu" | ||||
| 							case "MPEG4-GENERIC": | ||||
| 								info.Codec = "aac" | ||||
| 							case "H264": | ||||
|   | ||||
							
								
								
									
										174
									
								
								session.go
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								session.go
									
									
									
									
									
								
							| @@ -13,14 +13,10 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	. "github.com/Monibuca/engine/v2" | ||||
| 	. "github.com/Monibuca/plugin-rtp" | ||||
| 	"github.com/teris-io/shortid" | ||||
| ) | ||||
|  | ||||
| type RTPPack struct { | ||||
| 	Type   RTPType | ||||
| 	Buffer []byte | ||||
| } | ||||
|  | ||||
| type SessionType int | ||||
|  | ||||
| const ( | ||||
| @@ -38,29 +34,6 @@ func (st SessionType) String() string { | ||||
| 	return "unknow" | ||||
| } | ||||
|  | ||||
| type RTPType int | ||||
|  | ||||
| const ( | ||||
| 	RTP_TYPE_AUDIO RTPType = iota | ||||
| 	RTP_TYPE_VIDEO | ||||
| 	RTP_TYPE_AUDIOCONTROL | ||||
| 	RTP_TYPE_VIDEOCONTROL | ||||
| ) | ||||
|  | ||||
| func (rt RTPType) String() string { | ||||
| 	switch rt { | ||||
| 	case RTP_TYPE_AUDIO: | ||||
| 		return "audio" | ||||
| 	case RTP_TYPE_VIDEO: | ||||
| 		return "video" | ||||
| 	case RTP_TYPE_AUDIOCONTROL: | ||||
| 		return "audio control" | ||||
| 	case RTP_TYPE_VIDEOCONTROL: | ||||
| 		return "video control" | ||||
| 	} | ||||
| 	return "unknow" | ||||
| } | ||||
|  | ||||
| type TransType int | ||||
|  | ||||
| const ( | ||||
| @@ -128,53 +101,41 @@ func (session *RTSP) AcceptPush() { | ||||
| 			} | ||||
| 			channel := int(buf1) | ||||
| 			rtpLen := int(binary.BigEndian.Uint16(buf2)) | ||||
| 			pack := new(RTPPack) | ||||
| 			rtpBytes := make([]byte, rtpLen) | ||||
| 			if _, err := io.ReadFull(session.connRW, rtpBytes); err != nil { | ||||
| 				Println(err) | ||||
| 				return | ||||
| 			} | ||||
| 			var pack *RTPPack | ||||
| 			if err = pack.Unmarshal(rtpBytes); err != nil { | ||||
| 				Println(err) | ||||
| 				return | ||||
| 			} | ||||
| 			switch channel { | ||||
| 			case session.aRTPChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_AUDIO, | ||||
| 					Buffer: rtpBytes, | ||||
| 				} | ||||
| 				pack.Type = RTP_TYPE_AUDIO | ||||
| 				elapsed := time.Now().Sub(timer) | ||||
| 				if elapsed >= 30*time.Second { | ||||
| 					Println("Recv an audio RTP package") | ||||
| 					timer = time.Now() | ||||
| 				} | ||||
| 			case session.aRTPControlChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_AUDIOCONTROL, | ||||
| 					Buffer: rtpBytes, | ||||
| 				} | ||||
| 				pack.Type = RTP_TYPE_AUDIOCONTROL | ||||
| 			case session.vRTPChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_VIDEO, | ||||
| 					Buffer: rtpBytes, | ||||
| 				} | ||||
| 				pack.Type = RTP_TYPE_VIDEO | ||||
| 				elapsed := time.Now().Sub(timer) | ||||
| 				if elapsed >= 30*time.Second { | ||||
| 					Println("Recv an video RTP package") | ||||
| 					timer = time.Now() | ||||
| 				} | ||||
| 			case session.vRTPControlChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type:   RTP_TYPE_VIDEOCONTROL, | ||||
| 					Buffer: rtpBytes, | ||||
| 				} | ||||
| 				pack.Type = RTP_TYPE_VIDEOCONTROL | ||||
| 			default: | ||||
| 				Printf("unknow rtp pack type, %v", pack.Type) | ||||
| 				continue | ||||
| 			} | ||||
| 			if pack == nil { | ||||
| 				Printf("session tcp got nil rtp pack") | ||||
| 				continue | ||||
| 			} | ||||
| 			session.InBytes += rtpLen + 4 | ||||
| 			session.handleRTP(pack) | ||||
| 			session.PushPack(pack) | ||||
| 		} else { // rtsp cmd | ||||
| 			reqBuf := bytes.NewBuffer(nil) | ||||
| 			reqBuf.WriteByte(buf1) | ||||
| @@ -356,21 +317,22 @@ func (session *RTSP) handleRequest(req *Request) { | ||||
|  | ||||
| 		session.SDPRaw = req.Body | ||||
| 		session.SDPMap = ParseSDP(req.Body) | ||||
| 		sdp, ok := session.SDPMap["audio"] | ||||
| 		if ok { | ||||
| 			session.AControl = sdp.Control | ||||
| 			session.ACodec = sdp.Codec | ||||
| 			session.AudioSpecificConfig = sdp.Config | ||||
| 			Printf("audio codec[%s]\n", session.ACodec) | ||||
| 		} | ||||
| 		if sdp, ok = session.SDPMap["video"]; ok { | ||||
| 			session.VControl = sdp.Control | ||||
| 			session.VCodec = sdp.Codec | ||||
| 			session.SPS = sdp.SpropParameterSets[0] | ||||
| 			session.PPS = sdp.SpropParameterSets[1] | ||||
| 			Printf("video codec[%s]\n", session.VCodec) | ||||
| 		} | ||||
| 		if session.Publisher.Publish(streamPath) { | ||||
| 		if session.Publish(streamPath) { | ||||
| 			if session.ASdp, session.HasAudio = session.SDPMap["audio"]; session.HasAudio { | ||||
| 				if len(session.ASdp.Control) > 0 { | ||||
| 					session.WriteASC(session.ASdp.Config) | ||||
| 				} else { | ||||
| 					session.setAudioFormat() | ||||
| 				} | ||||
| 				Printf("audio codec[%s]\n", session.ASdp.Codec) | ||||
| 			} | ||||
| 			if session.VSdp, session.HasVideo = session.SDPMap["video"]; session.HasVideo { | ||||
| 				if len(session.VSdp.SpropParameterSets) > 1 { | ||||
| 					session.WriteSPS(session.VSdp.SpropParameterSets[0]) | ||||
| 					session.WritePPS(session.VSdp.SpropParameterSets[1]) | ||||
| 				} | ||||
| 				Printf("video codec[%s]\n", session.VSdp.Codec) | ||||
| 			} | ||||
| 			session.Stream.Type = "RTSP" | ||||
| 			session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo | ||||
| 			collection.Store(streamPath, session) | ||||
| @@ -417,36 +379,38 @@ func (session *RTSP) handleRequest(req *Request) { | ||||
| 		//	res.Status = "Error Status" | ||||
| 		//	return | ||||
| 		//} | ||||
| 		vPath := "" | ||||
| 		if strings.Index(strings.ToLower(session.VControl), "rtsp://") == 0 { | ||||
| 			vControlUrl, err := url.Parse(session.VControl) | ||||
| 			if err != nil { | ||||
| 				res.StatusCode = 500 | ||||
| 				res.Status = "Invalid VControl" | ||||
| 				return | ||||
| 		var vPath, aPath string | ||||
| 		if session.HasVideo { | ||||
| 			if strings.Index(strings.ToLower(session.VSdp.Control), "rtsp://") == 0 { | ||||
| 				vControlUrl, err := url.Parse(session.VSdp.Control) | ||||
| 				if err != nil { | ||||
| 					res.StatusCode = 500 | ||||
| 					res.Status = "Invalid VControl" | ||||
| 					return | ||||
| 				} | ||||
| 				if vControlUrl.Port() == "" { | ||||
| 					vControlUrl.Host = fmt.Sprintf("%s:554", vControlUrl.Host) | ||||
| 				} | ||||
| 				vPath = vControlUrl.String() | ||||
| 			} else { | ||||
| 				vPath = session.VSdp.Control | ||||
| 			} | ||||
| 			if vControlUrl.Port() == "" { | ||||
| 				vControlUrl.Host = fmt.Sprintf("%s:554", vControlUrl.Host) | ||||
| 			} | ||||
| 			vPath = vControlUrl.String() | ||||
| 		} else { | ||||
| 			vPath = session.VControl | ||||
| 		} | ||||
|  | ||||
| 		aPath := "" | ||||
| 		if strings.Index(strings.ToLower(session.AControl), "rtsp://") == 0 { | ||||
| 			aControlUrl, err := url.Parse(session.AControl) | ||||
| 			if err != nil { | ||||
| 				res.StatusCode = 500 | ||||
| 				res.Status = "Invalid AControl" | ||||
| 				return | ||||
| 		if session.HasAudio { | ||||
| 			if strings.Index(strings.ToLower(session.ASdp.Control), "rtsp://") == 0 { | ||||
| 				aControlUrl, err := url.Parse(session.ASdp.Control) | ||||
| 				if err != nil { | ||||
| 					res.StatusCode = 500 | ||||
| 					res.Status = "Invalid AControl" | ||||
| 					return | ||||
| 				} | ||||
| 				if aControlUrl.Port() == "" { | ||||
| 					aControlUrl.Host = fmt.Sprintf("%s:554", aControlUrl.Host) | ||||
| 				} | ||||
| 				aPath = aControlUrl.String() | ||||
| 			} else { | ||||
| 				aPath = session.ASdp.Control | ||||
| 			} | ||||
| 			if aControlUrl.Port() == "" { | ||||
| 				aControlUrl.Host = fmt.Sprintf("%s:554", aControlUrl.Host) | ||||
| 			} | ||||
| 			aPath = aControlUrl.String() | ||||
| 		} else { | ||||
| 			aPath = session.AControl | ||||
| 		} | ||||
|  | ||||
| 		mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?") | ||||
| @@ -577,7 +541,7 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | ||||
| 			return | ||||
| 		} | ||||
| 		err = session.UDPClient.SendRTP(pack) | ||||
| 		session.OutBytes += len(pack.Buffer) | ||||
| 		session.OutBytes += len(pack.Raw) | ||||
| 		return | ||||
| 	} | ||||
| 	switch pack.Type { | ||||
| @@ -588,12 +552,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | ||||
| 		session.connWLock.Lock() | ||||
| 		session.connRW.Write(bufChannel) | ||||
| 		bufLen := make([]byte, 2) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Buffer))) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Raw))) | ||||
| 		session.connRW.Write(bufLen) | ||||
| 		session.connRW.Write(pack.Buffer) | ||||
| 		session.connRW.Write(pack.Raw) | ||||
| 		session.connRW.Flush() | ||||
| 		session.connWLock.Unlock() | ||||
| 		session.OutBytes += len(pack.Buffer) + 4 | ||||
| 		session.OutBytes += len(pack.Raw) + 4 | ||||
| 	case RTP_TYPE_AUDIOCONTROL: | ||||
| 		bufChannel := make([]byte, 2) | ||||
| 		bufChannel[0] = 0x24 | ||||
| @@ -601,12 +565,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | ||||
| 		session.connWLock.Lock() | ||||
| 		session.connRW.Write(bufChannel) | ||||
| 		bufLen := make([]byte, 2) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Buffer))) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Raw))) | ||||
| 		session.connRW.Write(bufLen) | ||||
| 		session.connRW.Write(pack.Buffer) | ||||
| 		session.connRW.Write(pack.Raw) | ||||
| 		session.connRW.Flush() | ||||
| 		session.connWLock.Unlock() | ||||
| 		session.OutBytes += len(pack.Buffer) + 4 | ||||
| 		session.OutBytes += len(pack.Raw) + 4 | ||||
| 	case RTP_TYPE_VIDEO: | ||||
| 		bufChannel := make([]byte, 2) | ||||
| 		bufChannel[0] = 0x24 | ||||
| @@ -614,12 +578,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | ||||
| 		session.connWLock.Lock() | ||||
| 		session.connRW.Write(bufChannel) | ||||
| 		bufLen := make([]byte, 2) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Buffer))) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Raw))) | ||||
| 		session.connRW.Write(bufLen) | ||||
| 		session.connRW.Write(pack.Buffer) | ||||
| 		session.connRW.Write(pack.Raw) | ||||
| 		session.connRW.Flush() | ||||
| 		session.connWLock.Unlock() | ||||
| 		session.OutBytes += len(pack.Buffer) + 4 | ||||
| 		session.OutBytes += len(pack.Raw) + 4 | ||||
| 	case RTP_TYPE_VIDEOCONTROL: | ||||
| 		bufChannel := make([]byte, 2) | ||||
| 		bufChannel[0] = 0x24 | ||||
| @@ -627,12 +591,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | ||||
| 		session.connWLock.Lock() | ||||
| 		session.connRW.Write(bufChannel) | ||||
| 		bufLen := make([]byte, 2) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Buffer))) | ||||
| 		binary.BigEndian.PutUint16(bufLen, uint16(len(pack.Raw))) | ||||
| 		session.connRW.Write(bufLen) | ||||
| 		session.connRW.Write(pack.Buffer) | ||||
| 		session.connRW.Write(pack.Raw) | ||||
| 		session.connRW.Flush() | ||||
| 		session.connWLock.Unlock() | ||||
| 		session.OutBytes += len(pack.Buffer) + 4 | ||||
| 		session.OutBytes += len(pack.Raw) + 4 | ||||
| 	default: | ||||
| 		err = fmt.Errorf("session tcp send rtp got unkown pack type[%v]", pack.Type) | ||||
| 	} | ||||
|   | ||||
| @@ -2,9 +2,10 @@ package rtsp | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	. "github.com/Monibuca/engine/v2" | ||||
| 	. "github.com/Monibuca/plugin-rtp" | ||||
| 	"net" | ||||
| 	"strings" | ||||
| 	. "github.com/Monibuca/engine/v2" | ||||
| ) | ||||
|  | ||||
| type UDPClient struct { | ||||
| @@ -151,7 +152,7 @@ func (c *UDPClient) SendRTP(pack *RTPPack) (err error) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if _, err = conn.Write(pack.Buffer);err != nil { | ||||
| 	if _, err = conn.Write(pack.Raw); err != nil { | ||||
| 		err = fmt.Errorf("udp client write bytes error, %v", err) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -9,10 +9,11 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	. "github.com/Monibuca/engine/v2" | ||||
| 	. "github.com/Monibuca/plugin-rtp" | ||||
| ) | ||||
|  | ||||
| type UDPServer struct { | ||||
| 	Session    *RTSP | ||||
| 	Session *RTSP | ||||
| 	UDPClient | ||||
| 	sync.Mutex | ||||
| } | ||||
| @@ -29,7 +30,7 @@ func (s *UDPServer) HandleRTP(pack *RTPPack) { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
| 	if s.Session != nil { | ||||
| 		s.Session.handleRTP(pack) | ||||
| 		s.Session.PushPack(pack) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -90,13 +91,11 @@ func (s *UDPServer) SetupAudio() (err error) { | ||||
| 					Printf("Package recv from AConn.len:%d\n", n) | ||||
| 					timer = time.Now() | ||||
| 				} | ||||
| 				rtpBytes := make([]byte, n) | ||||
| 				s.AddInputBytes(n) | ||||
| 				copy(rtpBytes, bufUDP) | ||||
| 				pack := &RTPPack{ | ||||
| 					Type:   RTP_TYPE_AUDIO, | ||||
| 					Buffer: rtpBytes, | ||||
| 					Type: RTP_TYPE_AUDIO, | ||||
| 				} | ||||
| 				pack.Unmarshal(bufUDP[:n]) | ||||
| 				s.HandleRTP(pack) | ||||
| 			} else { | ||||
| 				Println("udp server read audio pack error", err) | ||||
| @@ -131,13 +130,11 @@ func (s *UDPServer) SetupAudio() (err error) { | ||||
| 		for !s.Stoped { | ||||
| 			if n, _, err := s.AControlConn.ReadFromUDP(bufUDP); err == nil { | ||||
| 				//Printf("Package recv from AControlConn.len:%d\n", n) | ||||
| 				rtpBytes := make([]byte, n) | ||||
| 				s.AddInputBytes(n) | ||||
| 				copy(rtpBytes, bufUDP) | ||||
| 				pack := &RTPPack{ | ||||
| 					Type:   RTP_TYPE_AUDIOCONTROL, | ||||
| 					Buffer: rtpBytes, | ||||
| 					Type: RTP_TYPE_AUDIOCONTROL, | ||||
| 				} | ||||
| 				pack.Unmarshal(bufUDP[:n]) | ||||
| 				s.HandleRTP(pack) | ||||
| 			} else { | ||||
| 				Println("udp server read audio control pack error", err) | ||||
| @@ -182,13 +179,11 @@ func (s *UDPServer) SetupVideo() (err error) { | ||||
| 					Printf("Package recv from VConn.len:%d\n", n) | ||||
| 					timer = time.Now() | ||||
| 				} | ||||
| 				rtpBytes := make([]byte, n) | ||||
| 				s.AddInputBytes(n) | ||||
| 				copy(rtpBytes, bufUDP) | ||||
| 				pack := &RTPPack{ | ||||
| 					Type:   RTP_TYPE_VIDEO, | ||||
| 					Buffer: rtpBytes, | ||||
| 					Type: RTP_TYPE_VIDEO, | ||||
| 				} | ||||
| 				pack.Unmarshal(bufUDP[:n]) | ||||
| 				s.HandleRTP(pack) | ||||
| 			} else { | ||||
| 				Println("udp server read video pack error", err) | ||||
| @@ -224,13 +219,11 @@ func (s *UDPServer) SetupVideo() (err error) { | ||||
| 		for !s.Stoped { | ||||
| 			if n, _, err := s.VControlConn.ReadFromUDP(bufUDP); err == nil { | ||||
| 				//Printf("Package recv from VControlConn.len:%d\n", n) | ||||
| 				rtpBytes := make([]byte, n) | ||||
| 				s.AddInputBytes(n) | ||||
| 				copy(rtpBytes, bufUDP) | ||||
| 				pack := &RTPPack{ | ||||
| 					Type:   RTP_TYPE_VIDEOCONTROL, | ||||
| 					Buffer: rtpBytes, | ||||
| 					Type: RTP_TYPE_VIDEOCONTROL, | ||||
| 				} | ||||
| 				pack.Unmarshal(bufUDP[:n]) | ||||
| 				s.HandleRTP(pack) | ||||
| 			} else { | ||||
| 				Println("udp server read video control pack error", err) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user