mirror of
				https://github.com/Monibuca/plugin-rtsp.git
				synced 2025-11-01 03:12:45 +08:00 
			
		
		
		
	Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 7e61ba71f7 | ||
|   | d6384dcbd5 | ||
|   | 2159a6fd9b | ||
|   | 02f3e91085 | ||
|   | 7f40078b50 | ||
|   | bb563d64c7 | ||
|   | f7cb146b89 | ||
|   | 9bb49cb9f7 | ||
|   | 087d1aab4d | ||
|   | f949464328 | ||
|   | d89f1e2405 | ||
|   | 1d3fbfc20b | ||
|   | fd64a69a12 | ||
|   | 0e4406ad14 | ||
|   | 22f33886a9 | ||
|   | 8b1892209d | 
| @@ -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 | ||||
|   | ||||
							
								
								
									
										144
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								client.go
									
									
									
									
									
								
							| @@ -4,6 +4,7 @@ import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"crypto/md5" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| @@ -30,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() | ||||
| @@ -78,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. | ||||
| @@ -91,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") | ||||
| @@ -102,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) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -144,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 { | ||||
| @@ -189,77 +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.WriteSPS(videoInfo.SpropParameterSets[0]) | ||||
| 		client.WritePPS(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 session != "" { | ||||
| 			headers["Session"] = session | ||||
| 		} | ||||
| 		var _url = sdpInfo.Control | ||||
| 		if !strings.HasPrefix(strings.ToLower(sdpInfo.Control), "rtsp://") { | ||||
| 			_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(sdpInfo.Control, "/") | ||||
| 		} | ||||
| 		switch t { | ||||
| 		case "video": | ||||
| 			if len(sdpInfo.SpropParameterSets) > 1 { | ||||
| 				client.WriteSPS(sdpInfo.SpropParameterSets[0]) | ||||
| 				client.WritePPS(sdpInfo.SpropParameterSets[1]) | ||||
| 			} | ||||
| 			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 { | ||||
| 				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 | ||||
| 			} | ||||
| 		if session != "" { | ||||
| 			headers["Session"] = session | ||||
| 		case "audio": | ||||
| 			if len(sdpInfo.Config) > 0 { | ||||
| 				client.WriteASC(sdpInfo.Config) | ||||
| 			}else{ | ||||
| 				client.setAudioFormat() | ||||
| 			} | ||||
| 		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 | ||||
| 		} | ||||
| 		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 | ||||
| 		if len(audioInfo.Config) < 2 { | ||||
| 			Printf("Setup audio err codec not support: %s", client.ACodec) | ||||
| 		} else { | ||||
| 			client.WriteASC(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} | ||||
| 			} | ||||
| 			err = client.UDPServer.SetupAudio() | ||||
| 			if err != nil { | ||||
| 				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 | ||||
| 			} | ||||
| 		if session != "" { | ||||
| 			headers["Session"] = session | ||||
| 		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 | ||||
| 			} | ||||
| 		} | ||||
| 		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 | ||||
| 		} | ||||
| @@ -269,40 +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 func() { | ||||
| 		if client.Err() == nil && config.Reconnect { | ||||
| 			Printf("reconnecting:%s", client.URL) | ||||
| 			client.RTSPClientInfo = RTSPClientInfo{} | ||||
| 			if err := client.requestStream(); err != nil { | ||||
| 				Println(err) | ||||
| 				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) | ||||
| @@ -332,9 +337,6 @@ func (client *RTSP) startStream() { | ||||
| 				pack = &RTPPack{ | ||||
| 					Type: RTP_TYPE_AUDIO, | ||||
| 				} | ||||
| 				if client.ACodec == "" { | ||||
| 					continue | ||||
| 				} | ||||
| 			case client.aRTPControlChannel: | ||||
| 				pack = &RTPPack{ | ||||
| 					Type: RTP_TYPE_AUDIOCONTROL, | ||||
|   | ||||
							
								
								
									
										10
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,8 +3,12 @@ module github.com/Monibuca/plugin-rtsp | ||||
| go 1.13 | ||||
|  | ||||
| require ( | ||||
| 	github.com/Monibuca/engine/v2 v2.1.2 | ||||
| 	github.com/Monibuca/plugin-rtp v0.0.0-20200531014802-504413c0dfcb | ||||
| 	github.com/pion/rtp v1.5.4 // indirect | ||||
| 	github.com/Monibuca/engine/v2 v2.2.2 | ||||
| 	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 | ||||
| ) | ||||
|   | ||||
							
								
								
									
										24
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								go.sum
									
									
									
									
									
								
							| @@ -4,8 +4,18 @@ github.com/Monibuca/engine/v2 v2.1.0 h1:pHeDCEFDusKFsZLpconYj8U5LCaWApnjd+yQRHYg | ||||
| github.com/Monibuca/engine/v2 v2.1.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.1.2 h1:7dUrHJAPEtvGFOO4GsKGjfMCmcbMrtLyYQ7WoK5EpG0= | ||||
| github.com/Monibuca/engine/v2 v2.1.2/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.1.9 h1:IulMIeP24qv8xWaI+tcg233Y7w3mCaLXxt4iQaVpT7s= | ||||
| github.com/Monibuca/engine/v2 v2.1.9/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.2.0 h1:A4SyWwzVLegd8Oa6LfSW3LpNfBmWq+MHJJLO55gvaYI= | ||||
| github.com/Monibuca/engine/v2 v2.2.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.2.1 h1:I9mcnj9ZABl974n+HKGWe4pFcn9z8kETLVpD1fx2zEI= | ||||
| github.com/Monibuca/engine/v2 v2.2.1/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/engine/v2 v2.2.2 h1:ho5M3aFW9Mlj9Lb56Qvk0m+9L8yWc7RhwPh8dRWAeBk= | ||||
| github.com/Monibuca/engine/v2 v2.2.2/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||
| github.com/Monibuca/plugin-rtp v0.0.0-20200531014802-504413c0dfcb h1:CnmoQ8XsWxs/6mulbQfTGUa8cPr6c/3bkkTsNozRBwE= | ||||
| github.com/Monibuca/plugin-rtp v0.0.0-20200531014802-504413c0dfcb/go.mod h1:8HxBilkF835Lepe/DLUCjaw1mRiu3MxTDsG7g9UcfZA= | ||||
| 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= | ||||
| @@ -18,18 +28,30 @@ 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/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/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 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= | ||||
| 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/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 h1:PuNg6xqV3brIUihatcKZj1YDUs+M45L0ZbrZWYtkDxY= | ||||
| 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/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/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 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= | ||||
| @@ -39,6 +61,8 @@ github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzg | ||||
| 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/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= | ||||
|   | ||||
							
								
								
									
										35
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								main.go
									
									
									
									
									
								
							| @@ -23,7 +23,11 @@ var config = struct { | ||||
| 	RemoteAddr   string | ||||
| 	Timeout      int | ||||
| 	Reconnect    bool | ||||
| }{":554", false, "rtsp://localhost/${streamPath}", 0,false} | ||||
| 	AutoPullList []*struct { | ||||
| 		URL        string | ||||
| 		StreamPath string | ||||
| 	} | ||||
| }{":554", false, "rtsp://localhost/${streamPath}", 0, false, nil} | ||||
|  | ||||
| func init() { | ||||
| 	InstallPlugin(&PluginConfig{ | ||||
| @@ -67,6 +71,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)) | ||||
| 	} | ||||
| @@ -131,10 +142,8 @@ type RTSP struct { | ||||
| 	SDPMap   map[string]*SDPInfo | ||||
| 	nonce    string | ||||
| 	closeOld bool | ||||
| 	AControl string | ||||
| 	VControl string | ||||
| 	ACodec   string | ||||
| 	VCodec   string | ||||
| 	ASdp     *SDPInfo | ||||
| 	VSdp     *SDPInfo | ||||
| 	aacsent  bool | ||||
| 	Timeout  int | ||||
| 	//tcp channels | ||||
| @@ -146,6 +155,22 @@ type RTSP struct { | ||||
| 	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 | ||||
|   | ||||
| @@ -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]] | ||||
| 					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": | ||||
|   | ||||
							
								
								
									
										45
									
								
								session.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								session.go
									
									
									
									
									
								
							| @@ -318,19 +318,20 @@ func (session *RTSP) handleRequest(req *Request) { | ||||
| 		session.SDPRaw = req.Body | ||||
| 		session.SDPMap = ParseSDP(req.Body) | ||||
| 		if session.Publish(streamPath) { | ||||
| 			sdp, ok := session.SDPMap["audio"] | ||||
| 			if ok { | ||||
| 				session.AControl = sdp.Control | ||||
| 				session.ACodec = sdp.Codec | ||||
| 				session.WriteASC(sdp.Config) | ||||
| 				Printf("audio codec[%s]\n", session.ACodec) | ||||
| 			if session.ASdp, session.HasAudio = session.SDPMap["audio"]; session.HasAudio { | ||||
| 				if len(session.ASdp.Control) > 0 { | ||||
| 					session.WriteASC(session.ASdp.Config) | ||||
| 				} else { | ||||
| 					session.setAudioFormat() | ||||
| 				} | ||||
| 			if sdp, ok = session.SDPMap["video"]; ok { | ||||
| 				session.VControl = sdp.Control | ||||
| 				session.VCodec = sdp.Codec | ||||
| 				session.WriteSPS(sdp.SpropParameterSets[0]) | ||||
| 				session.WritePPS(sdp.SpropParameterSets[1]) | ||||
| 				Printf("video codec[%s]\n", session.VCodec) | ||||
| 				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 | ||||
| @@ -378,9 +379,10 @@ 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) | ||||
| 		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" | ||||
| @@ -391,12 +393,12 @@ func (session *RTSP) handleRequest(req *Request) { | ||||
| 				} | ||||
| 				vPath = vControlUrl.String() | ||||
| 			} else { | ||||
| 			vPath = session.VControl | ||||
| 				vPath = session.VSdp.Control | ||||
| 			} | ||||
|  | ||||
| 		aPath := "" | ||||
| 		if strings.Index(strings.ToLower(session.AControl), "rtsp://") == 0 { | ||||
| 			aControlUrl, err := url.Parse(session.AControl) | ||||
| 		} | ||||
| 		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" | ||||
| @@ -407,7 +409,8 @@ func (session *RTSP) handleRequest(req *Request) { | ||||
| 				} | ||||
| 				aPath = aControlUrl.String() | ||||
| 			} else { | ||||
| 			aPath = session.AControl | ||||
| 				aPath = session.ASdp.Control | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user