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 | BufferLength  = 2048 | ||||||
| AutoPull     = false | AutoPull     = false | ||||||
| RemoteAddr   = "rtsp://localhost/${streamPath}" | 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中 | - ListenAddr 是监听端口,可以将rtsp流推到Monibuca中 | ||||||
| - BufferLength是指解析拉取的rtp包的缓冲大小 | - BufferLength是指解析拉取的rtp包的缓冲大小 | ||||||
| - AutoPull是指当有用户订阅一个新流的时候自动向远程拉流转发 | - AutoPull是指当有用户订阅一个新流的时候自动向远程拉流转发 | ||||||
| - RemoteAddr 指远程拉流地址,其中${streamPath}是占位符,实际使用流路径替换。 | - RemoteAddr 指远程拉流地址,其中${streamPath}是占位符,实际使用流路径替换。 | ||||||
|  | - AutoPullList 是一个数组,如果配置了该数组,则会在程序启动时自动启动拉流,StreamPath一定要是唯一的,不能重复 | ||||||
|  |  | ||||||
| ## 使用方法(拉流转发) | ## 使用方法(拉流转发) | ||||||
| ```go | ```go | ||||||
|   | |||||||
							
								
								
									
										176
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								client.go
									
									
									
									
									
								
							| @@ -4,6 +4,7 @@ import ( | |||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"crypto/md5" | 	"crypto/md5" | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/binary" | 	"encoding/binary" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -30,6 +31,7 @@ func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (err error) { | |||||||
| 		rtsp.aRTPChannel = 2 | 		rtsp.aRTPChannel = 2 | ||||||
| 		rtsp.aRTPControlChannel = 3 | 		rtsp.aRTPControlChannel = 3 | ||||||
| 		rtsp.URL = rtspUrl | 		rtsp.URL = rtspUrl | ||||||
|  | 		rtsp.UDPServer = &UDPServer{Session: rtsp} | ||||||
| 		if err = rtsp.requestStream(); err != nil { | 		if err = rtsp.requestStream(); err != nil { | ||||||
| 			Println(err) | 			Println(err) | ||||||
| 			rtsp.Close() | 			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) | 	Authorization := fmt.Sprintf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"", username, realm, nonce, l.String(), response) | ||||||
| 	return Authorization, nil | 	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) { | func (client *RTSP) checkAuth(method string, resp *Response) (string, error) { | ||||||
| 	if resp.StatusCode == 401 { | 	if resp.StatusCode == 401 { | ||||||
| 		// need auth. | 		// need auth. | ||||||
| @@ -91,8 +105,7 @@ func (client *RTSP) checkAuth(method string, resp *Response) (string, error) { | |||||||
| 					client.authLine = authLine | 					client.authLine = authLine | ||||||
| 					return DigestAuth(authLine, method, client.URL) | 					return DigestAuth(authLine, method, client.URL) | ||||||
| 				} else if strings.IndexAny(authLine, "Basic") == 0 { | 				} else if strings.IndexAny(authLine, "Basic") == 0 { | ||||||
| 					// not support yet | 					return BasicAuth(authLine, method, client.URL) | ||||||
| 					// TODO.. |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			return "", fmt.Errorf("auth error") | 			return "", fmt.Errorf("auth error") | ||||||
| @@ -102,9 +115,7 @@ func (client *RTSP) checkAuth(method string, resp *Response) (string, error) { | |||||||
| 				client.authLine = authLine | 				client.authLine = authLine | ||||||
| 				return DigestAuth(authLine, method, client.URL) | 				return DigestAuth(authLine, method, client.URL) | ||||||
| 			} else if strings.IndexAny(authLine, "Basic") == 0 { | 			} else if strings.IndexAny(authLine, "Basic") == 0 { | ||||||
| 				// not support yet | 				return BasicAuth(authLine, method, client.URL) | ||||||
| 				// TODO.. |  | ||||||
| 				return "", fmt.Errorf("not support Basic auth yet") |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -144,7 +155,7 @@ func (client *RTSP) requestStream() (err error) { | |||||||
| 	client.connRW = bufio.NewReadWriter(bufio.NewReaderSize(&timeoutConn, networkBuffer), bufio.NewWriterSize(&timeoutConn, networkBuffer)) | 	client.connRW = bufio.NewReadWriter(bufio.NewReaderSize(&timeoutConn, networkBuffer), bufio.NewWriterSize(&timeoutConn, networkBuffer)) | ||||||
|  |  | ||||||
| 	headers := make(map[string]string) | 	headers := make(map[string]string) | ||||||
| 	headers["Require"] = "implicit-play" | 	//headers["Require"] = "implicit-play" | ||||||
| 	// An OPTIONS request returns the request types the server will accept. | 	// An OPTIONS request returns the request types the server will accept. | ||||||
| 	resp, err := client.Request("OPTIONS", headers) | 	resp, err := client.Request("OPTIONS", headers) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -189,77 +200,60 @@ func (client *RTSP) requestStream() (err error) { | |||||||
| 	} | 	} | ||||||
| 	client.SDPRaw = resp.Body | 	client.SDPRaw = resp.Body | ||||||
| 	client.SDPMap = ParseSDP(client.SDPRaw) | 	client.SDPMap = ParseSDP(client.SDPRaw) | ||||||
|  | 	client.VSdp, client.HasVideo = client.SDPMap["video"] | ||||||
|  | 	client.ASdp, client.HasAudio = client.SDPMap["audio"] | ||||||
| 	session := "" | 	session := "" | ||||||
| 	if videoInfo, ok := client.SDPMap["video"]; ok { | 	otherChannel := 4 | ||||||
| 		client.VControl = videoInfo.Control | 	for t, sdpInfo := range client.SDPMap { | ||||||
| 		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, "/") |  | ||||||
| 		} |  | ||||||
| 		headers = make(map[string]string) | 		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 != "" { | 		if session != "" { | ||||||
| 			headers["Session"] = 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) | 		var _url = sdpInfo.Control | ||||||
| 		if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil { | 		if !strings.HasPrefix(strings.ToLower(sdpInfo.Control), "rtsp://") { | ||||||
| 			return err | 			_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(sdpInfo.Control, "/") | ||||||
| 		} | 		} | ||||||
| 		session, _ = resp.Header["Session"].(string) | 		switch t { | ||||||
| 		session = strings.Split(session, ";")[0] | 		case "video": | ||||||
| 	} | 			if len(sdpInfo.SpropParameterSets) > 1 { | ||||||
| 	if audioInfo, ok := client.SDPMap["audio"]; ok { | 				client.WriteSPS(sdpInfo.SpropParameterSets[0]) | ||||||
| 		client.AControl = audioInfo.Control | 				client.WritePPS(sdpInfo.SpropParameterSets[1]) | ||||||
| 		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 client.TransType == TRANS_TYPE_TCP { | ||||||
| 			if err != nil { | 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel) | ||||||
| 				Printf("Setup audio err.%v", err) | 			} else { | ||||||
| 				return err | 				//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 { | 		if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -269,40 +263,51 @@ func (client *RTSP) requestStream() (err error) { | |||||||
| 	headers = make(map[string]string) | 	headers = make(map[string]string) | ||||||
| 	if session != "" { | 	if session != "" { | ||||||
| 		headers["Session"] = session | 		headers["Session"] = session | ||||||
|  | 		client.Session = session | ||||||
| 	} | 	} | ||||||
| 	resp, err = client.Request("PLAY", headers) | 	resp, err = client.Request("PLAY", headers) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (client *RTSP) startStream() { | func (client *RTSP) startStream() { | ||||||
| 	//startTime := time.Now() | 	startTime := time.Now() | ||||||
| 	//loggerTime := time.Now().Add(-10 * time.Second) | 	//loggerTime := time.Now().Add(-10 * time.Second) | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		if client.Err() == nil && config.Reconnect { | 		if client.Err() == nil && config.Reconnect { | ||||||
| 			Printf("reconnecting:%s", client.URL) | 			Printf("reconnecting:%s", client.URL) | ||||||
| 			client.RTSPClientInfo = RTSPClientInfo{} | 			client.RTSPClientInfo = RTSPClientInfo{} | ||||||
| 			if err := client.requestStream(); err != nil { | 			if err := client.requestStream(); err != nil { | ||||||
| 				Println(err) | 				t := time.NewTicker(time.Second * 5) | ||||||
| 				client.Stop() | 				for { | ||||||
| 				return | 					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() | ||||||
| 			} | 			} | ||||||
| 			go client.startStream() |  | ||||||
| 		} else { | 		} else { | ||||||
| 			client.Stop() | 			client.Stop() | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 	for client.Err() == nil { | 	for client.Err() == nil { | ||||||
| 		//if client.OptionIntervalMillis > 0 { | 		if time.Since(startTime) > time.Minute { | ||||||
| 		//	if time.Since(startTime) > time.Duration(client.OptionIntervalMillis)*time.Millisecond { | 			startTime = time.Now() | ||||||
| 		//		startTime = time.Now() | 			headers := make(map[string]string) | ||||||
| 		//		headers := make(map[string]string) | 			headers["Require"] = "implicit-play" | ||||||
| 		//		headers["Require"] = "implicit-play" | 			// An OPTIONS request returns the request types the server will accept. | ||||||
| 		//		// An OPTIONS request returns the request types the server will accept. | 			if err := client.RequestNoResp("GET_PARAMETER", headers); err != nil { | ||||||
| 		//		if err := client.RequestNoResp("OPTIONS", headers); err != nil { | 				// ignore... | ||||||
| 		//			// ignore... | 			} | ||||||
| 		//		} | 		} | ||||||
| 		//	} |  | ||||||
| 		//} |  | ||||||
| 		b, err := client.connRW.ReadByte() | 		b, err := client.connRW.ReadByte() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			Printf("client.connRW.ReadByte err:%v", err) | 			Printf("client.connRW.ReadByte err:%v", err) | ||||||
| @@ -332,9 +337,6 @@ func (client *RTSP) startStream() { | |||||||
| 				pack = &RTPPack{ | 				pack = &RTPPack{ | ||||||
| 					Type: RTP_TYPE_AUDIO, | 					Type: RTP_TYPE_AUDIO, | ||||||
| 				} | 				} | ||||||
| 				if client.ACodec == "" { |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 			case client.aRTPControlChannel: | 			case client.aRTPControlChannel: | ||||||
| 				pack = &RTPPack{ | 				pack = &RTPPack{ | ||||||
| 					Type: RTP_TYPE_AUDIOCONTROL, | 					Type: RTP_TYPE_AUDIOCONTROL, | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,8 +3,12 @@ module github.com/Monibuca/plugin-rtsp | |||||||
| go 1.13 | go 1.13 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/Monibuca/engine/v2 v2.1.2 | 	github.com/Monibuca/engine/v2 v2.2.2 | ||||||
| 	github.com/Monibuca/plugin-rtp v0.0.0-20200531014802-504413c0dfcb | 	github.com/Monibuca/plugin-rtp v1.0.0 | ||||||
| 	github.com/pion/rtp v1.5.4 // indirect | 	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 | 	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.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||||
| github.com/Monibuca/engine/v2 v2.1.2 h1:7dUrHJAPEtvGFOO4GsKGjfMCmcbMrtLyYQ7WoK5EpG0= | 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.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 h1:CnmoQ8XsWxs/6mulbQfTGUa8cPr6c/3bkkTsNozRBwE= | ||||||
| github.com/Monibuca/plugin-rtp v0.0.0-20200531014802-504413c0dfcb/go.mod h1:8HxBilkF835Lepe/DLUCjaw1mRiu3MxTDsG7g9UcfZA= | 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 h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= | ||||||
| github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= | 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 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/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 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= | ||||||
| github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= | 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 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.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 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= | ||||||
| github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= | 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 h1:PuNg6xqV3brIUihatcKZj1YDUs+M45L0ZbrZWYtkDxY= | ||||||
| github.com/pion/rtp v1.5.4/go.mod h1:bg60AL5GotNOlYZsqycbhDtEV3TkfbpXG0KBiUq29Mg= | 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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 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 h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY= | ||||||
| github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | 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 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | 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 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-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 h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= | ||||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 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= | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								main.go
									
									
									
									
									
								
							| @@ -18,12 +18,16 @@ import ( | |||||||
|  |  | ||||||
| var collection sync.Map | var collection sync.Map | ||||||
| var config = struct { | var config = struct { | ||||||
| 	ListenAddr string | 	ListenAddr   string | ||||||
| 	AutoPull   bool | 	AutoPull     bool | ||||||
| 	RemoteAddr string | 	RemoteAddr   string | ||||||
| 	Timeout    int | 	Timeout      int | ||||||
| 	Reconnect  bool | 	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() { | func init() { | ||||||
| 	InstallPlugin(&PluginConfig{ | 	InstallPlugin(&PluginConfig{ | ||||||
| @@ -67,6 +71,13 @@ func runPlugin() { | |||||||
| 			w.Write([]byte(fmt.Sprintf(`{"code":1,"msg":"%s"}`, err.Error()))) | 			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 != "" { | 	if config.ListenAddr != "" { | ||||||
| 		log.Fatal(ListenRtsp(config.ListenAddr)) | 		log.Fatal(ListenRtsp(config.ListenAddr)) | ||||||
| 	} | 	} | ||||||
| @@ -131,10 +142,8 @@ type RTSP struct { | |||||||
| 	SDPMap   map[string]*SDPInfo | 	SDPMap   map[string]*SDPInfo | ||||||
| 	nonce    string | 	nonce    string | ||||||
| 	closeOld bool | 	closeOld bool | ||||||
| 	AControl string | 	ASdp     *SDPInfo | ||||||
| 	VControl string | 	VSdp     *SDPInfo | ||||||
| 	ACodec   string |  | ||||||
| 	VCodec   string |  | ||||||
| 	aacsent  bool | 	aacsent  bool | ||||||
| 	Timeout  int | 	Timeout  int | ||||||
| 	//tcp channels | 	//tcp channels | ||||||
| @@ -146,6 +155,22 @@ type RTSP struct { | |||||||
| 	UDPClient          *UDPClient | 	UDPClient          *UDPClient | ||||||
| 	Auth               func(string) string | 	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 { | type RTSPClientInfo struct { | ||||||
| 	Agent    string | 	Agent    string | ||||||
| 	Session  string | 	Session  string | ||||||
|   | |||||||
| @@ -31,17 +31,13 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo { | |||||||
| 			switch typeval[0] { | 			switch typeval[0] { | ||||||
| 			case "m": | 			case "m": | ||||||
| 				if len(fields) > 0 { | 				if len(fields) > 0 { | ||||||
| 					switch fields[0] { | 					info = &SDPInfo{AVType: fields[0]} | ||||||
| 					case "audio", "video": | 					sdpMap[info.AVType] = info | ||||||
| 						sdpMap[fields[0]] = &SDPInfo{AVType: fields[0]} | 					mfields := strings.Split(fields[1], " ") | ||||||
| 						info = sdpMap[fields[0]] | 					if len(mfields) >= 3 { | ||||||
| 						mfields := strings.Split(fields[1], " ") | 						info.PayloadType, _ = strconv.Atoi(mfields[2]) | ||||||
| 						if len(mfields) >= 3 { |  | ||||||
| 							info.PayloadType, _ = strconv.Atoi(mfields[2]) |  | ||||||
| 						} |  | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
| 			case "a": | 			case "a": | ||||||
| 				if info != nil { | 				if info != nil { | ||||||
| 					for _, field := range fields { | 					for _, field := range fields { | ||||||
| @@ -60,6 +56,10 @@ func ParseSDP(sdpRaw string) map[string]*SDPInfo { | |||||||
| 						if len(keyval) >= 2 { | 						if len(keyval) >= 2 { | ||||||
| 							key := keyval[0] | 							key := keyval[0] | ||||||
| 							switch key { | 							switch key { | ||||||
|  | 							case "PCMA": | ||||||
|  | 								info.Codec = "pcma" | ||||||
|  | 							case "PCMU": | ||||||
|  | 								info.Codec = "pcmu" | ||||||
| 							case "MPEG4-GENERIC": | 							case "MPEG4-GENERIC": | ||||||
| 								info.Codec = "aac" | 								info.Codec = "aac" | ||||||
| 							case "H264": | 							case "H264": | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								session.go
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								session.go
									
									
									
									
									
								
							| @@ -318,19 +318,20 @@ func (session *RTSP) handleRequest(req *Request) { | |||||||
| 		session.SDPRaw = req.Body | 		session.SDPRaw = req.Body | ||||||
| 		session.SDPMap = ParseSDP(req.Body) | 		session.SDPMap = ParseSDP(req.Body) | ||||||
| 		if session.Publish(streamPath) { | 		if session.Publish(streamPath) { | ||||||
| 			sdp, ok := session.SDPMap["audio"] | 			if session.ASdp, session.HasAudio = session.SDPMap["audio"]; session.HasAudio { | ||||||
| 			if ok { | 				if len(session.ASdp.Control) > 0 { | ||||||
| 				session.AControl = sdp.Control | 					session.WriteASC(session.ASdp.Config) | ||||||
| 				session.ACodec = sdp.Codec | 				} else { | ||||||
| 				session.WriteASC(sdp.Config) | 					session.setAudioFormat() | ||||||
| 				Printf("audio codec[%s]\n", session.ACodec) | 				} | ||||||
|  | 				Printf("audio codec[%s]\n", session.ASdp.Codec) | ||||||
| 			} | 			} | ||||||
| 			if sdp, ok = session.SDPMap["video"]; ok { | 			if session.VSdp, session.HasVideo = session.SDPMap["video"]; session.HasVideo { | ||||||
| 				session.VControl = sdp.Control | 				if len(session.VSdp.SpropParameterSets) > 1 { | ||||||
| 				session.VCodec = sdp.Codec | 					session.WriteSPS(session.VSdp.SpropParameterSets[0]) | ||||||
| 				session.WriteSPS(sdp.SpropParameterSets[0]) | 					session.WritePPS(session.VSdp.SpropParameterSets[1]) | ||||||
| 				session.WritePPS(sdp.SpropParameterSets[1]) | 				} | ||||||
| 				Printf("video codec[%s]\n", session.VCodec) | 				Printf("video codec[%s]\n", session.VSdp.Codec) | ||||||
| 			} | 			} | ||||||
| 			session.Stream.Type = "RTSP" | 			session.Stream.Type = "RTSP" | ||||||
| 			session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo | 			session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo | ||||||
| @@ -378,36 +379,38 @@ func (session *RTSP) handleRequest(req *Request) { | |||||||
| 		//	res.Status = "Error Status" | 		//	res.Status = "Error Status" | ||||||
| 		//	return | 		//	return | ||||||
| 		//} | 		//} | ||||||
| 		vPath := "" | 		var vPath, aPath string | ||||||
| 		if strings.Index(strings.ToLower(session.VControl), "rtsp://") == 0 { | 		if session.HasVideo { | ||||||
| 			vControlUrl, err := url.Parse(session.VControl) | 			if strings.Index(strings.ToLower(session.VSdp.Control), "rtsp://") == 0 { | ||||||
| 			if err != nil { | 				vControlUrl, err := url.Parse(session.VSdp.Control) | ||||||
| 				res.StatusCode = 500 | 				if err != nil { | ||||||
| 				res.Status = "Invalid VControl" | 					res.StatusCode = 500 | ||||||
| 				return | 					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 |  | ||||||
| 		} | 		} | ||||||
|  | 		if session.HasAudio { | ||||||
| 		aPath := "" | 			if strings.Index(strings.ToLower(session.ASdp.Control), "rtsp://") == 0 { | ||||||
| 		if strings.Index(strings.ToLower(session.AControl), "rtsp://") == 0 { | 				aControlUrl, err := url.Parse(session.ASdp.Control) | ||||||
| 			aControlUrl, err := url.Parse(session.AControl) | 				if err != nil { | ||||||
| 			if err != nil { | 					res.StatusCode = 500 | ||||||
| 				res.StatusCode = 500 | 					res.Status = "Invalid AControl" | ||||||
| 				res.Status = "Invalid AControl" | 					return | ||||||
| 				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+))?") | 		mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user