mirror of
				https://github.com/Monibuca/plugin-rtsp.git
				synced 2025-11-01 03:12:45 +08:00 
			
		
		
		
	Compare commits
	
		
			24 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 02f3e91085 | ||
|   | 7f40078b50 | ||
|   | bb563d64c7 | ||
|   | f7cb146b89 | ||
|   | 9bb49cb9f7 | ||
|   | 087d1aab4d | ||
|   | f949464328 | ||
|   | d89f1e2405 | ||
|   | 1d3fbfc20b | ||
|   | fd64a69a12 | ||
|   | 0e4406ad14 | ||
|   | 22f33886a9 | ||
|   | 8b1892209d | ||
|   | 2e9cf9a4ca | ||
|   | 67da93d8e2 | ||
|   | cb733b368f | ||
|   | fadeccddab | ||
|   | 93df7632a6 | ||
|   | 53c4788df2 | ||
|   | f5bdd6a298 | ||
|   | eaddc60775 | ||
|   | 655170cb24 | ||
|   | 55bd2ce785 | ||
|   | 2004a6afc1 | 
							
								
								
									
										193
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								client.go
									
									
									
									
									
								
							| @@ -4,10 +4,10 @@ import ( | |||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"crypto/md5" | 	"crypto/md5" | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/binary" | 	"encoding/binary" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	. "github.com/Monibuca/engine/v2" |  | ||||||
| 	"github.com/pixelbender/go-sdp/sdp" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| @@ -15,11 +15,14 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	. "github.com/Monibuca/engine/v2" | ||||||
|  | 	. "github.com/Monibuca/plugin-rtp" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // PullStream 从外部拉流 | // PullStream 从外部拉流 | ||||||
| func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (result bool) { | 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.Stream.Type = "RTSP" | ||||||
| 		rtsp.RTSPInfo.StreamInfo = &rtsp.Stream.StreamInfo | 		rtsp.RTSPInfo.StreamInfo = &rtsp.Stream.StreamInfo | ||||||
| 		rtsp.TransType = TRANS_TYPE_TCP | 		rtsp.TransType = TRANS_TYPE_TCP | ||||||
| @@ -28,16 +31,18 @@ func (rtsp *RTSP) PullStream(streamPath string, rtspUrl string) (result bool) { | |||||||
| 		rtsp.aRTPChannel = 2 | 		rtsp.aRTPChannel = 2 | ||||||
| 		rtsp.aRTPControlChannel = 3 | 		rtsp.aRTPControlChannel = 3 | ||||||
| 		rtsp.URL = rtspUrl | 		rtsp.URL = rtspUrl | ||||||
| 		if err := rtsp.requestStream();err != nil { | 		rtsp.UDPServer = &UDPServer{Session: rtsp} | ||||||
|  | 		if err = rtsp.requestStream(); err != nil { | ||||||
|  | 			Println(err) | ||||||
| 			rtsp.Close() | 			rtsp.Close() | ||||||
| 			return false | 			return | ||||||
| 		} | 		} | ||||||
| 		go rtsp.startStream() | 		go rtsp.startStream() | ||||||
| 		collection.Store(streamPath, rtsp) | 		collection.Store(streamPath, rtsp) | ||||||
| 		// 	go rtsp.run() |  | ||||||
| 	} |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | 	return errors.New("publish badname") | ||||||
|  | } | ||||||
| func DigestAuth(authLine string, method string, URL string) (string, error) { | func DigestAuth(authLine string, method string, URL string) (string, error) { | ||||||
| 	l, err := url.Parse(URL) | 	l, err := url.Parse(URL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -75,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. | ||||||
| @@ -88,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") | ||||||
| @@ -99,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") |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -141,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 { | ||||||
| @@ -184,115 +198,116 @@ func (client *RTSP) requestStream() (err error) { | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	_sdp, err := sdp.ParseString(resp.Body) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	client.Sdp = _sdp |  | ||||||
| 	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 := "" | ||||||
| 	for _, media := range _sdp.Media { | 	otherChannel := 4 | ||||||
| 		switch media.Type { | 	for t, sdpInfo := range client.SDPMap { | ||||||
| 		case "video": |  | ||||||
| 			client.VControl = media.Attributes.Get("control") |  | ||||||
| 			client.VCodec = media.Format[0].Name |  | ||||||
| 			client.SPS = client.SDPMap["video"].SpropParameterSets[0] |  | ||||||
| 			client.PPS = client.SDPMap["video"].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 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 { | 			if client.TransType == TRANS_TYPE_TCP { | ||||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel) | 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.vRTPChannel, client.vRTPControlChannel) | ||||||
| 			} else { | 			} else { | ||||||
| 				if client.UDPServer == nil { |  | ||||||
| 					client.UDPServer = &UDPServer{Session: client} |  | ||||||
| 				} |  | ||||||
| 				//RTP/AVP;unicast;client_port=64864-64865 | 				//RTP/AVP;unicast;client_port=64864-64865 | ||||||
| 				err = client.UDPServer.SetupVideo() | 				if err = client.UDPServer.SetupVideo(); err != nil { | ||||||
| 				if err != nil { |  | ||||||
| 					Printf("Setup video err.%v", err) | 					Printf("Setup video err.%v", err) | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.VPort, client.UDPServer.VControlPort) | 				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 | 				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) |  | ||||||
| 			resp, err = client.RequestWithPath("SETUP", _url, headers, true) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 			session, _ = resp.Header["Session"].(string) |  | ||||||
| 		case "audio": | 		case "audio": | ||||||
| 			client.AControl = media.Attributes.Get("control") | 			if len(sdpInfo.Config) > 0 { | ||||||
| 			client.ACodec = media.Format[0].Name | 				client.WriteASC(sdpInfo.Config) | ||||||
| 			client.AudioSpecificConfig = client.SDPMap["audio"].Config |  | ||||||
| 			var _url = "" |  | ||||||
| 			if strings.Index(strings.ToLower(client.AControl), "rtsp://") == 0 { |  | ||||||
| 				_url = client.AControl |  | ||||||
| 			}else{ | 			}else{ | ||||||
| 				_url = strings.TrimRight(client.URL, "/") + "/" + strings.TrimLeft(client.AControl, "/") | 				client.setAudioFormat() | ||||||
| 			} | 			} | ||||||
| 			headers = make(map[string]string) |  | ||||||
| 			if client.TransType == TRANS_TYPE_TCP { | 			if client.TransType == TRANS_TYPE_TCP { | ||||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel) | 				headers["Transport"] = fmt.Sprintf("RTP/AVP/TCP;unicast;interleaved=%d-%d", client.aRTPChannel, client.aRTPControlChannel) | ||||||
| 			} else { | 			} else { | ||||||
| 				if client.UDPServer == nil { | 				if err = client.UDPServer.SetupAudio(); err != nil { | ||||||
| 					client.UDPServer = &UDPServer{Session: client} |  | ||||||
| 				} |  | ||||||
| 				err = client.UDPServer.SetupAudio() |  | ||||||
| 				if err != nil { |  | ||||||
| 					Printf("Setup audio err.%v", err) | 					Printf("Setup audio err.%v", err) | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 				headers["Transport"] = fmt.Sprintf("RTP/AVP/UDP;unicast;client_port=%d-%d", client.UDPServer.APort, client.UDPServer.AControlPort) | 				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 | 				client.Conn.timeout = 0 //	UDP ignore timeout | ||||||
| 			} | 			} | ||||||
| 			if session != "" { | 		default: | ||||||
| 				headers["Session"] = session | 			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) | 		} | ||||||
| 			resp, err = client.RequestWithPath("SETUP", _url, headers, true) | 		if resp, err = client.RequestWithPath("SETUP", _url, headers, true); err != nil { | ||||||
| 			if err != nil { |  | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		session, _ = resp.Header["Session"].(string) | 		session, _ = resp.Header["Session"].(string) | ||||||
| 		} | 		session = strings.Split(session, ";")[0] | ||||||
| 	} | 	} | ||||||
| 	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) | ||||||
| 	if err != nil { |  | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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 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 { | 				for { | ||||||
| 		//if client.OptionIntervalMillis > 0 { | 					Printf("reconnecting:%s in 5 seconds", client.URL) | ||||||
| 		//	if time.Since(startTime) > time.Duration(client.OptionIntervalMillis)*time.Millisecond { | 					select { | ||||||
| 		//		startTime = time.Now() | 					case <-client.Done(): | ||||||
| 		//		headers := make(map[string]string) | 						client.Stop() | ||||||
| 		//		headers["Require"] = "implicit-play" | 						return | ||||||
| 		//		// An OPTIONS request returns the request types the server will accept. | 					case <-t.C: | ||||||
| 		//		if err := client.RequestNoResp("OPTIONS", headers); err != nil { | 						if err = client.requestStream(); err == nil { | ||||||
| 		//			// ignore... | 							go client.startStream() | ||||||
| 		//		} | 							return | ||||||
| 		//	} | 						} | ||||||
| 		//} | 					} | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				go client.startStream() | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			client.Stop() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	for client.Err() == nil { | ||||||
|  | 		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() | 		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) | ||||||
| @@ -315,38 +330,30 @@ func (client *RTSP) startStream() { | |||||||
| 				Printf("io.ReadFull err:%v", err) | 				Printf("io.ReadFull err:%v", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			rtpBuf := content |  | ||||||
| 			var pack *RTPPack | 			var pack *RTPPack | ||||||
|  |  | ||||||
| 			switch channel { | 			switch channel { | ||||||
| 			case client.aRTPChannel: | 			case client.aRTPChannel: | ||||||
| 				pack = &RTPPack{ | 				pack = &RTPPack{ | ||||||
| 					Type: RTP_TYPE_AUDIO, | 					Type: RTP_TYPE_AUDIO, | ||||||
| 					Buffer: rtpBuf, |  | ||||||
| 				} | 				} | ||||||
| 			case client.aRTPControlChannel: | 			case client.aRTPControlChannel: | ||||||
| 				pack = &RTPPack{ | 				pack = &RTPPack{ | ||||||
| 					Type: RTP_TYPE_AUDIOCONTROL, | 					Type: RTP_TYPE_AUDIOCONTROL, | ||||||
| 					Buffer: rtpBuf, |  | ||||||
| 				} | 				} | ||||||
| 			case client.vRTPChannel: | 			case client.vRTPChannel: | ||||||
| 				pack = &RTPPack{ | 				pack = &RTPPack{ | ||||||
| 					Type: RTP_TYPE_VIDEO, | 					Type: RTP_TYPE_VIDEO, | ||||||
| 					Buffer: rtpBuf, |  | ||||||
| 				} | 				} | ||||||
| 			case client.vRTPControlChannel: | 			case client.vRTPControlChannel: | ||||||
| 				pack = &RTPPack{ | 				pack = &RTPPack{ | ||||||
| 					Type: RTP_TYPE_VIDEOCONTROL, | 					Type: RTP_TYPE_VIDEOCONTROL, | ||||||
| 					Buffer: rtpBuf, |  | ||||||
| 				} | 				} | ||||||
| 			default: | 			default: | ||||||
| 				Printf("unknow rtp pack type, channel:%v", channel) | 				Printf("unknow rtp pack type, channel:%v", channel) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			if pack == nil { | 			pack.Unmarshal(content) | ||||||
| 				Printf("session tcp got nil rtp pack") |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			//if client.debugLogEnable { | 			//if client.debugLogEnable { | ||||||
| 			//	rtp := ParseRTP(pack.Buffer) | 			//	rtp := ParseRTP(pack.Buffer) | ||||||
| 			//	if rtp != nil { | 			//	if rtp != nil { | ||||||
| @@ -365,13 +372,13 @@ func (client *RTSP) startStream() { | |||||||
| 			//} | 			//} | ||||||
|  |  | ||||||
| 			client.InBytes += int(length + 4) | 			client.InBytes += int(length + 4) | ||||||
| 			client.handleRTP(pack) | 			client.PushPack(pack) | ||||||
|  |  | ||||||
| 		default: // rtsp | 		default: // rtsp | ||||||
| 			builder := bytes.Buffer{} | 			builder := bytes.Buffer{} | ||||||
| 			builder.WriteByte(b) | 			builder.WriteByte(b) | ||||||
| 			contentLen := 0 | 			contentLen := 0 | ||||||
| 			for { | 			for client.Err() == nil { | ||||||
| 				line, prefix, err := client.connRW.ReadLine() | 				line, prefix, err := client.connRW.ReadLine() | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					Printf("client.connRW.ReadLine err:%v", err) | 					Printf("client.connRW.ReadLine err:%v", err) | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,10 +3,12 @@ module github.com/Monibuca/plugin-rtsp | |||||||
| go 1.13 | go 1.13 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/EasyDarwin/EasyDarwin v8.1.0+incompatible // indirect | 	github.com/Monibuca/engine/v2 v2.2.0 | ||||||
| 	github.com/Monibuca/engine/v2 v2.0.0 | 	github.com/Monibuca/plugin-rtp v1.0.0 | ||||||
| 	github.com/jinzhu/gorm v1.9.12 // indirect | 	github.com/logrusorgru/aurora v2.0.3+incompatible // indirect | ||||||
| 	github.com/pixelbender/go-sdp v1.0.0 | 	github.com/mattn/go-colorable v0.1.7 // indirect | ||||||
| 	github.com/reactivex/rxgo v1.0.0 // 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 | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										71
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,81 +1,62 @@ | |||||||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/EasyDarwin/EasyDarwin v8.1.0+incompatible h1:Rr8dRbZtcJhiJvGx5Vs7IENM6RUUwGkZiIj5+WrNhm8= | github.com/Monibuca/engine/v2 v2.1.0 h1:pHeDCEFDusKFsZLpconYj8U5LCaWApnjd+yQRHYgQsQ= | ||||||
| github.com/EasyDarwin/EasyDarwin v8.1.0+incompatible/go.mod h1:xnmC+Q2+wugEDpQGxivSFNYPOhmNlIQHBfl0hMeriSU= | github.com/Monibuca/engine/v2 v2.1.0/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||||
| github.com/Monibuca/engine v1.2.1 h1:TJmC6eZA1lR1MScWgempZLiEZD4T6aY/nn/rlQ9UdK8= | github.com/Monibuca/engine/v2 v2.1.2 h1:7dUrHJAPEtvGFOO4GsKGjfMCmcbMrtLyYQ7WoK5EpG0= | ||||||
| github.com/Monibuca/engine v1.2.1/go.mod h1:WbDkXENLjcPjyjCR1Mix1GA+uAlwORkv/+8aMVrDX2g= | github.com/Monibuca/engine/v2 v2.1.2/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||||
| github.com/Monibuca/engine v1.2.2 h1:hNjsrZpOmui8lYhgCJ5ltJU8g/k0Rrdysx2tHNGGnbI= | github.com/Monibuca/engine/v2 v2.1.9 h1:IulMIeP24qv8xWaI+tcg233Y7w3mCaLXxt4iQaVpT7s= | ||||||
| github.com/Monibuca/engine/v2 v2.0.0 h1:8FjaScrtN8QdbcxO9zZYABMC0Re3I1O1T4p94zAXYb0= | github.com/Monibuca/engine/v2 v2.1.9/go.mod h1:34EYjjV15G6myuHOKaJkO7y5tJ1Arq/NfC9Weacr2mc= | ||||||
| github.com/Monibuca/engine/v2 v2.0.0/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/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/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= | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 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 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0= | ||||||
| github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo= | 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 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg= | ||||||
| github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg= | 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 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/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/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 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/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/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= | github.com/pion/randutil v0.0.0 h1:aLWLVhTG2jzoD25F0OlW6nXvXrjoGwiXq2Sz7j7NzL0= | ||||||
| github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= | github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= | ||||||
| github.com/pixelbender/go-sdp v1.0.0 h1:hLP2ALBN4sLpgp2r3EDcFUSN3AyOkg1jonuWEJniotY= | github.com/pion/rtp v1.5.4 h1:PuNg6xqV3brIUihatcKZj1YDUs+M45L0ZbrZWYtkDxY= | ||||||
| github.com/pixelbender/go-sdp v1.0.0/go.mod h1:6IBlz9+BrUHoFTea7gcp4S54khtOhjCW/nVDLhmZBAs= | 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/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 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= | ||||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | 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 h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= | ||||||
| github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= | 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-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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | golang.org/x/sys v0.0.0-20200828161417-c663848e9a16 h1:54u1berWyLujz9htI1BHtZpcCEYaYNUSDFLXMNDd7To= | ||||||
| google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= | golang.org/x/sys v0.0.0-20200828161417-c663848e9a16/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= |  | ||||||
| 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= | ||||||
|   | |||||||
							
								
								
									
										163
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								main.go
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ package rtsp | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net" | 	"net" | ||||||
| @@ -12,20 +11,19 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	. "github.com/Monibuca/engine/v2" | 	. "github.com/Monibuca/engine/v2" | ||||||
| 	. "github.com/Monibuca/engine/v2/avformat" |  | ||||||
| 	"github.com/Monibuca/engine/v2/util" | 	"github.com/Monibuca/engine/v2/util" | ||||||
| 	"github.com/pixelbender/go-sdp/sdp" | 	. "github.com/Monibuca/plugin-rtp" | ||||||
| 	"github.com/teris-io/shortid" | 	"github.com/teris-io/shortid" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var collection = sync.Map{} | var collection sync.Map | ||||||
| var config = struct { | var config = struct { | ||||||
| 	ListenAddr string | 	ListenAddr string | ||||||
| 	BufferLength int |  | ||||||
| 	AutoPull   bool | 	AutoPull   bool | ||||||
| 	RemoteAddr string | 	RemoteAddr string | ||||||
| 	Timeout    int | 	Timeout    int | ||||||
| }{":554", 2048, true, "rtsp://localhost/${streamPath}", 0} | 	Reconnect  bool | ||||||
|  | }{":554", false, "rtsp://localhost/${streamPath}", 0, false} | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	InstallPlugin(&PluginConfig{ | 	InstallPlugin(&PluginConfig{ | ||||||
| @@ -54,7 +52,6 @@ func runPlugin() { | |||||||
| 			collection.Range(func(key, value interface{}) bool { | 			collection.Range(func(key, value interface{}) bool { | ||||||
| 				rtsp := value.(*RTSP) | 				rtsp := value.(*RTSP) | ||||||
| 				pinfo := &rtsp.RTSPInfo | 				pinfo := &rtsp.RTSPInfo | ||||||
| 				//pinfo.BufferRate = len(rtsp.OutGoing) * 100 / config.BufferLength |  | ||||||
| 				info = append(info, pinfo) | 				info = append(info, pinfo) | ||||||
| 				return true | 				return true | ||||||
| 			}) | 			}) | ||||||
| @@ -64,9 +61,7 @@ func runPlugin() { | |||||||
| 	http.HandleFunc("/rtsp/pull", func(w http.ResponseWriter, r *http.Request) { | 	http.HandleFunc("/rtsp/pull", func(w http.ResponseWriter, r *http.Request) { | ||||||
| 		targetURL := r.URL.Query().Get("target") | 		targetURL := r.URL.Query().Get("target") | ||||||
| 		streamPath := r.URL.Query().Get("streamPath") | 		streamPath := r.URL.Query().Get("streamPath") | ||||||
| 		var err error | 		if err := new(RTSP).PullStream(streamPath, targetURL); err == nil { | ||||||
| 		if err == nil { |  | ||||||
| 			new(RTSP).PullStream(streamPath, targetURL) |  | ||||||
| 			w.Write([]byte(`{"code":0}`)) | 			w.Write([]byte(`{"code":0}`)) | ||||||
| 		} else { | 		} else { | ||||||
| 			w.Write([]byte(fmt.Sprintf(`{"code":1,"msg":"%s"}`, err.Error()))) | 			w.Write([]byte(fmt.Sprintf(`{"code":1,"msg":"%s"}`, err.Error()))) | ||||||
| @@ -123,7 +118,7 @@ func ListenRtsp(addr string) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| type RTSP struct { | type RTSP struct { | ||||||
| 	Publisher | 	RTP | ||||||
| 	RTSPInfo | 	RTSPInfo | ||||||
| 	RTSPClientInfo | 	RTSPClientInfo | ||||||
| 	ID        string | 	ID        string | ||||||
| @@ -132,19 +127,14 @@ type RTSP struct { | |||||||
| 	connWLock sync.RWMutex | 	connWLock sync.RWMutex | ||||||
| 	Type      SessionType | 	Type      SessionType | ||||||
| 	TransType TransType | 	TransType TransType | ||||||
| 	SDPRaw    string |  | ||||||
| 	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 |  | ||||||
| 	avcsent   bool |  | ||||||
| 	aacsent  bool | 	aacsent  bool | ||||||
| 	Timeout  int | 	Timeout  int | ||||||
| 	// stats info |  | ||||||
| 	fuBuffer []byte |  | ||||||
| 	//tcp channels | 	//tcp channels | ||||||
| 	aRTPChannel        int | 	aRTPChannel        int | ||||||
| 	aRTPControlChannel int | 	aRTPControlChannel int | ||||||
| @@ -152,26 +142,33 @@ type RTSP struct { | |||||||
| 	vRTPControlChannel int | 	vRTPControlChannel int | ||||||
| 	UDPServer          *UDPServer | 	UDPServer          *UDPServer | ||||||
| 	UDPClient          *UDPClient | 	UDPClient          *UDPClient | ||||||
| 	SPS                 []byte |  | ||||||
| 	PPS                 []byte |  | ||||||
| 	AudioSpecificConfig []byte |  | ||||||
| 	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 | ||||||
| 	Sdp       *sdp.Session |  | ||||||
| 	authLine string | 	authLine string | ||||||
| 	Seq      int | 	Seq      int | ||||||
| } | } | ||||||
| type RTSPInfo struct { | type RTSPInfo struct { | ||||||
| 	URL        string | 	URL        string | ||||||
| 	SyncCount  int64 | 	SDPRaw     string | ||||||
| 	Header     *string |  | ||||||
| 	BufferRate int |  | ||||||
| 	InBytes    int | 	InBytes    int | ||||||
| 	OutBytes   int | 	OutBytes   int | ||||||
|  |  | ||||||
| 	StreamInfo *StreamInfo | 	StreamInfo *StreamInfo | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -199,115 +196,3 @@ func (conn *RichConn) Write(b []byte) (n int, err error) { | |||||||
| 	} | 	} | ||||||
| 	return conn.Conn.Write(b) | 	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: |  | ||||||
| 		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] { | 			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]} |  | ||||||
| 						info = sdpMap[fields[0]] |  | ||||||
| 					mfields := strings.Split(fields[1], " ") | 					mfields := strings.Split(fields[1], " ") | ||||||
| 					if len(mfields) >= 3 { | 					if len(mfields) >= 3 { | ||||||
| 						info.PayloadType, _ = strconv.Atoi(mfields[2]) | 						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": | ||||||
|   | |||||||
							
								
								
									
										130
									
								
								session.go
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								session.go
									
									
									
									
									
								
							| @@ -13,14 +13,10 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	. "github.com/Monibuca/engine/v2" | 	. "github.com/Monibuca/engine/v2" | ||||||
|  | 	. "github.com/Monibuca/plugin-rtp" | ||||||
| 	"github.com/teris-io/shortid" | 	"github.com/teris-io/shortid" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type RTPPack struct { |  | ||||||
| 	Type   RTPType |  | ||||||
| 	Buffer []byte |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type SessionType int | type SessionType int | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -38,29 +34,6 @@ func (st SessionType) String() string { | |||||||
| 	return "unknow" | 	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 | type TransType int | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -99,9 +72,11 @@ func (session *RTSP) Stop() { | |||||||
| 		session.UDPServer = nil | 		session.UDPServer = nil | ||||||
| 	} | 	} | ||||||
| 	if session.Running() { | 	if session.Running() { | ||||||
| 		collection.Delete(session.StreamPath) |  | ||||||
| 		session.Cancel() | 		session.Cancel() | ||||||
| 	} | 	} | ||||||
|  | 	if session.Stream != nil { | ||||||
|  | 		collection.Delete(session.StreamPath) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // AcceptPush 接受推流 | // AcceptPush 接受推流 | ||||||
| @@ -126,53 +101,41 @@ func (session *RTSP) AcceptPush() { | |||||||
| 			} | 			} | ||||||
| 			channel := int(buf1) | 			channel := int(buf1) | ||||||
| 			rtpLen := int(binary.BigEndian.Uint16(buf2)) | 			rtpLen := int(binary.BigEndian.Uint16(buf2)) | ||||||
|  | 			pack := new(RTPPack) | ||||||
| 			rtpBytes := make([]byte, rtpLen) | 			rtpBytes := make([]byte, rtpLen) | ||||||
| 			if _, err := io.ReadFull(session.connRW, rtpBytes); err != nil { | 			if _, err := io.ReadFull(session.connRW, rtpBytes); err != nil { | ||||||
| 				Println(err) | 				Println(err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			var pack *RTPPack | 			if err = pack.Unmarshal(rtpBytes); err != nil { | ||||||
|  | 				Println(err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
| 			switch channel { | 			switch channel { | ||||||
| 			case session.aRTPChannel: | 			case session.aRTPChannel: | ||||||
| 				pack = &RTPPack{ | 				pack.Type = RTP_TYPE_AUDIO | ||||||
| 					Type:   RTP_TYPE_AUDIO, |  | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} |  | ||||||
| 				elapsed := time.Now().Sub(timer) | 				elapsed := time.Now().Sub(timer) | ||||||
| 				if elapsed >= 30*time.Second { | 				if elapsed >= 30*time.Second { | ||||||
| 					Println("Recv an audio RTP package") | 					Println("Recv an audio RTP package") | ||||||
| 					timer = time.Now() | 					timer = time.Now() | ||||||
| 				} | 				} | ||||||
| 			case session.aRTPControlChannel: | 			case session.aRTPControlChannel: | ||||||
| 				pack = &RTPPack{ | 				pack.Type = RTP_TYPE_AUDIOCONTROL | ||||||
| 					Type:   RTP_TYPE_AUDIOCONTROL, |  | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} |  | ||||||
| 			case session.vRTPChannel: | 			case session.vRTPChannel: | ||||||
| 				pack = &RTPPack{ | 				pack.Type = RTP_TYPE_VIDEO | ||||||
| 					Type:   RTP_TYPE_VIDEO, |  | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} |  | ||||||
| 				elapsed := time.Now().Sub(timer) | 				elapsed := time.Now().Sub(timer) | ||||||
| 				if elapsed >= 30*time.Second { | 				if elapsed >= 30*time.Second { | ||||||
| 					Println("Recv an video RTP package") | 					Println("Recv an video RTP package") | ||||||
| 					timer = time.Now() | 					timer = time.Now() | ||||||
| 				} | 				} | ||||||
| 			case session.vRTPControlChannel: | 			case session.vRTPControlChannel: | ||||||
| 				pack = &RTPPack{ | 				pack.Type = RTP_TYPE_VIDEOCONTROL | ||||||
| 					Type:   RTP_TYPE_VIDEOCONTROL, |  | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} |  | ||||||
| 			default: | 			default: | ||||||
| 				Printf("unknow rtp pack type, %v", pack.Type) | 				Printf("unknow rtp pack type, %v", pack.Type) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			if pack == nil { |  | ||||||
| 				Printf("session tcp got nil rtp pack") |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			session.InBytes += rtpLen + 4 | 			session.InBytes += rtpLen + 4 | ||||||
| 			session.handleRTP(pack) | 			session.PushPack(pack) | ||||||
| 		} else { // rtsp cmd | 		} else { // rtsp cmd | ||||||
| 			reqBuf := bytes.NewBuffer(nil) | 			reqBuf := bytes.NewBuffer(nil) | ||||||
| 			reqBuf.WriteByte(buf1) | 			reqBuf.WriteByte(buf1) | ||||||
| @@ -354,21 +317,22 @@ func (session *RTSP) handleRequest(req *Request) { | |||||||
|  |  | ||||||
| 		session.SDPRaw = req.Body | 		session.SDPRaw = req.Body | ||||||
| 		session.SDPMap = ParseSDP(req.Body) | 		session.SDPMap = ParseSDP(req.Body) | ||||||
| 		sdp, ok := session.SDPMap["audio"] | 		if session.Publish(streamPath) { | ||||||
| 		if ok { | 			if session.ASdp, session.HasAudio = session.SDPMap["audio"]; session.HasAudio { | ||||||
| 			session.AControl = sdp.Control | 				if len(session.ASdp.Control) >0 { | ||||||
| 			session.ACodec = sdp.Codec | 					session.WriteASC(session.ASdp.Config) | ||||||
| 			session.AudioSpecificConfig = sdp.Config | 				}else{ | ||||||
| 			Printf("audio codec[%s]\n", session.ACodec) | 					session.setAudioFormat() | ||||||
| 				} | 				} | ||||||
| 		if sdp, ok = session.SDPMap["video"];ok { | 				Printf("audio codec[%s]\n", session.ASdp.Codec) | ||||||
| 			session.VControl = sdp.Control | 			} | ||||||
| 			session.VCodec = sdp.Codec | 			if session.VSdp, session.HasVideo = session.SDPMap["video"]; session.HasVideo { | ||||||
| 			session.SPS = sdp.SpropParameterSets[0] | 				if len(session.VSdp.SpropParameterSets) > 1 { | ||||||
| 			session.PPS = sdp.SpropParameterSets[1] | 					session.WriteSPS(session.VSdp.SpropParameterSets[0]) | ||||||
| 			Printf("video codec[%s]\n", session.VCodec) | 					session.WritePPS(session.VSdp.SpropParameterSets[1]) | ||||||
|  | 				} | ||||||
|  | 				Printf("video codec[%s]\n", session.VSdp.Codec) | ||||||
| 			} | 			} | ||||||
| 		if session.Publisher.Publish(streamPath) { |  | ||||||
| 			session.Stream.Type = "RTSP" | 			session.Stream.Type = "RTSP" | ||||||
| 			session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo | 			session.RTSPInfo.StreamInfo = &session.Stream.StreamInfo | ||||||
| 			collection.Store(streamPath, session) | 			collection.Store(streamPath, session) | ||||||
| @@ -416,8 +380,8 @@ func (session *RTSP) handleRequest(req *Request) { | |||||||
| 		//	return | 		//	return | ||||||
| 		//} | 		//} | ||||||
| 		vPath := "" | 		vPath := "" | ||||||
| 		if strings.Index(strings.ToLower(session.VControl), "rtsp://") == 0 { | 		if strings.Index(strings.ToLower(session.VSdp.Control), "rtsp://") == 0 { | ||||||
| 			vControlUrl, err := url.Parse(session.VControl) | 			vControlUrl, err := url.Parse(session.VSdp.Control) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				res.StatusCode = 500 | 				res.StatusCode = 500 | ||||||
| 				res.Status = "Invalid VControl" | 				res.Status = "Invalid VControl" | ||||||
| @@ -428,12 +392,12 @@ func (session *RTSP) handleRequest(req *Request) { | |||||||
| 			} | 			} | ||||||
| 			vPath = vControlUrl.String() | 			vPath = vControlUrl.String() | ||||||
| 		} else { | 		} else { | ||||||
| 			vPath = session.VControl | 			vPath = session.VSdp.Control | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		aPath := "" | 		aPath := "" | ||||||
| 		if strings.Index(strings.ToLower(session.AControl), "rtsp://") == 0 { | 		if strings.Index(strings.ToLower(session.ASdp.Control), "rtsp://") == 0 { | ||||||
| 			aControlUrl, err := url.Parse(session.AControl) | 			aControlUrl, err := url.Parse(session.ASdp.Control) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				res.StatusCode = 500 | 				res.StatusCode = 500 | ||||||
| 				res.Status = "Invalid AControl" | 				res.Status = "Invalid AControl" | ||||||
| @@ -444,7 +408,7 @@ func (session *RTSP) handleRequest(req *Request) { | |||||||
| 			} | 			} | ||||||
| 			aPath = aControlUrl.String() | 			aPath = aControlUrl.String() | ||||||
| 		} else { | 		} else { | ||||||
| 			aPath = session.AControl | 			aPath = session.ASdp.Control | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?") | 		mtcp := regexp.MustCompile("interleaved=(\\d+)(-(\\d+))?") | ||||||
| @@ -575,7 +539,7 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		err = session.UDPClient.SendRTP(pack) | 		err = session.UDPClient.SendRTP(pack) | ||||||
| 		session.OutBytes += len(pack.Buffer) | 		session.OutBytes += len(pack.Raw) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	switch pack.Type { | 	switch pack.Type { | ||||||
| @@ -586,12 +550,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | |||||||
| 		session.connWLock.Lock() | 		session.connWLock.Lock() | ||||||
| 		session.connRW.Write(bufChannel) | 		session.connRW.Write(bufChannel) | ||||||
| 		bufLen := make([]byte, 2) | 		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(bufLen) | ||||||
| 		session.connRW.Write(pack.Buffer) | 		session.connRW.Write(pack.Raw) | ||||||
| 		session.connRW.Flush() | 		session.connRW.Flush() | ||||||
| 		session.connWLock.Unlock() | 		session.connWLock.Unlock() | ||||||
| 		session.OutBytes += len(pack.Buffer) + 4 | 		session.OutBytes += len(pack.Raw) + 4 | ||||||
| 	case RTP_TYPE_AUDIOCONTROL: | 	case RTP_TYPE_AUDIOCONTROL: | ||||||
| 		bufChannel := make([]byte, 2) | 		bufChannel := make([]byte, 2) | ||||||
| 		bufChannel[0] = 0x24 | 		bufChannel[0] = 0x24 | ||||||
| @@ -599,12 +563,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | |||||||
| 		session.connWLock.Lock() | 		session.connWLock.Lock() | ||||||
| 		session.connRW.Write(bufChannel) | 		session.connRW.Write(bufChannel) | ||||||
| 		bufLen := make([]byte, 2) | 		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(bufLen) | ||||||
| 		session.connRW.Write(pack.Buffer) | 		session.connRW.Write(pack.Raw) | ||||||
| 		session.connRW.Flush() | 		session.connRW.Flush() | ||||||
| 		session.connWLock.Unlock() | 		session.connWLock.Unlock() | ||||||
| 		session.OutBytes += len(pack.Buffer) + 4 | 		session.OutBytes += len(pack.Raw) + 4 | ||||||
| 	case RTP_TYPE_VIDEO: | 	case RTP_TYPE_VIDEO: | ||||||
| 		bufChannel := make([]byte, 2) | 		bufChannel := make([]byte, 2) | ||||||
| 		bufChannel[0] = 0x24 | 		bufChannel[0] = 0x24 | ||||||
| @@ -612,12 +576,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | |||||||
| 		session.connWLock.Lock() | 		session.connWLock.Lock() | ||||||
| 		session.connRW.Write(bufChannel) | 		session.connRW.Write(bufChannel) | ||||||
| 		bufLen := make([]byte, 2) | 		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(bufLen) | ||||||
| 		session.connRW.Write(pack.Buffer) | 		session.connRW.Write(pack.Raw) | ||||||
| 		session.connRW.Flush() | 		session.connRW.Flush() | ||||||
| 		session.connWLock.Unlock() | 		session.connWLock.Unlock() | ||||||
| 		session.OutBytes += len(pack.Buffer) + 4 | 		session.OutBytes += len(pack.Raw) + 4 | ||||||
| 	case RTP_TYPE_VIDEOCONTROL: | 	case RTP_TYPE_VIDEOCONTROL: | ||||||
| 		bufChannel := make([]byte, 2) | 		bufChannel := make([]byte, 2) | ||||||
| 		bufChannel[0] = 0x24 | 		bufChannel[0] = 0x24 | ||||||
| @@ -625,12 +589,12 @@ func (session *RTSP) SendRTP(pack *RTPPack) (err error) { | |||||||
| 		session.connWLock.Lock() | 		session.connWLock.Lock() | ||||||
| 		session.connRW.Write(bufChannel) | 		session.connRW.Write(bufChannel) | ||||||
| 		bufLen := make([]byte, 2) | 		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(bufLen) | ||||||
| 		session.connRW.Write(pack.Buffer) | 		session.connRW.Write(pack.Raw) | ||||||
| 		session.connRW.Flush() | 		session.connRW.Flush() | ||||||
| 		session.connWLock.Unlock() | 		session.connWLock.Unlock() | ||||||
| 		session.OutBytes += len(pack.Buffer) + 4 | 		session.OutBytes += len(pack.Raw) + 4 | ||||||
| 	default: | 	default: | ||||||
| 		err = fmt.Errorf("session tcp send rtp got unkown pack type[%v]", pack.Type) | 		err = fmt.Errorf("session tcp send rtp got unkown pack type[%v]", pack.Type) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -2,9 +2,10 @@ package rtsp | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	. "github.com/Monibuca/engine/v2" | ||||||
|  | 	. "github.com/Monibuca/plugin-rtp" | ||||||
| 	"net" | 	"net" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	. "github.com/Monibuca/engine/v2" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type UDPClient struct { | type UDPClient struct { | ||||||
| @@ -151,7 +152,7 @@ func (c *UDPClient) SendRTP(pack *RTPPack) (err error) { | |||||||
| 		return | 		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) | 		err = fmt.Errorf("udp client write bytes error, %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	. "github.com/Monibuca/engine/v2" | 	. "github.com/Monibuca/engine/v2" | ||||||
|  | 	. "github.com/Monibuca/plugin-rtp" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type UDPServer struct { | type UDPServer struct { | ||||||
| @@ -29,7 +30,7 @@ func (s *UDPServer) HandleRTP(pack *RTPPack) { | |||||||
| 	s.Lock() | 	s.Lock() | ||||||
| 	defer s.Unlock() | 	defer s.Unlock() | ||||||
| 	if s.Session != nil { | 	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) | 					Printf("Package recv from AConn.len:%d\n", n) | ||||||
| 					timer = time.Now() | 					timer = time.Now() | ||||||
| 				} | 				} | ||||||
| 				rtpBytes := make([]byte, n) |  | ||||||
| 				s.AddInputBytes(n) | 				s.AddInputBytes(n) | ||||||
| 				copy(rtpBytes, bufUDP) |  | ||||||
| 				pack := &RTPPack{ | 				pack := &RTPPack{ | ||||||
| 					Type: RTP_TYPE_AUDIO, | 					Type: RTP_TYPE_AUDIO, | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} | 				} | ||||||
|  | 				pack.Unmarshal(bufUDP[:n]) | ||||||
| 				s.HandleRTP(pack) | 				s.HandleRTP(pack) | ||||||
| 			} else { | 			} else { | ||||||
| 				Println("udp server read audio pack error", err) | 				Println("udp server read audio pack error", err) | ||||||
| @@ -131,13 +130,11 @@ func (s *UDPServer) SetupAudio() (err error) { | |||||||
| 		for !s.Stoped { | 		for !s.Stoped { | ||||||
| 			if n, _, err := s.AControlConn.ReadFromUDP(bufUDP); err == nil { | 			if n, _, err := s.AControlConn.ReadFromUDP(bufUDP); err == nil { | ||||||
| 				//Printf("Package recv from AControlConn.len:%d\n", n) | 				//Printf("Package recv from AControlConn.len:%d\n", n) | ||||||
| 				rtpBytes := make([]byte, n) |  | ||||||
| 				s.AddInputBytes(n) | 				s.AddInputBytes(n) | ||||||
| 				copy(rtpBytes, bufUDP) |  | ||||||
| 				pack := &RTPPack{ | 				pack := &RTPPack{ | ||||||
| 					Type: RTP_TYPE_AUDIOCONTROL, | 					Type: RTP_TYPE_AUDIOCONTROL, | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} | 				} | ||||||
|  | 				pack.Unmarshal(bufUDP[:n]) | ||||||
| 				s.HandleRTP(pack) | 				s.HandleRTP(pack) | ||||||
| 			} else { | 			} else { | ||||||
| 				Println("udp server read audio control pack error", err) | 				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) | 					Printf("Package recv from VConn.len:%d\n", n) | ||||||
| 					timer = time.Now() | 					timer = time.Now() | ||||||
| 				} | 				} | ||||||
| 				rtpBytes := make([]byte, n) |  | ||||||
| 				s.AddInputBytes(n) | 				s.AddInputBytes(n) | ||||||
| 				copy(rtpBytes, bufUDP) |  | ||||||
| 				pack := &RTPPack{ | 				pack := &RTPPack{ | ||||||
| 					Type: RTP_TYPE_VIDEO, | 					Type: RTP_TYPE_VIDEO, | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} | 				} | ||||||
|  | 				pack.Unmarshal(bufUDP[:n]) | ||||||
| 				s.HandleRTP(pack) | 				s.HandleRTP(pack) | ||||||
| 			} else { | 			} else { | ||||||
| 				Println("udp server read video pack error", err) | 				Println("udp server read video pack error", err) | ||||||
| @@ -224,13 +219,11 @@ func (s *UDPServer) SetupVideo() (err error) { | |||||||
| 		for !s.Stoped { | 		for !s.Stoped { | ||||||
| 			if n, _, err := s.VControlConn.ReadFromUDP(bufUDP); err == nil { | 			if n, _, err := s.VControlConn.ReadFromUDP(bufUDP); err == nil { | ||||||
| 				//Printf("Package recv from VControlConn.len:%d\n", n) | 				//Printf("Package recv from VControlConn.len:%d\n", n) | ||||||
| 				rtpBytes := make([]byte, n) |  | ||||||
| 				s.AddInputBytes(n) | 				s.AddInputBytes(n) | ||||||
| 				copy(rtpBytes, bufUDP) |  | ||||||
| 				pack := &RTPPack{ | 				pack := &RTPPack{ | ||||||
| 					Type: RTP_TYPE_VIDEOCONTROL, | 					Type: RTP_TYPE_VIDEOCONTROL, | ||||||
| 					Buffer: rtpBytes, |  | ||||||
| 				} | 				} | ||||||
|  | 				pack.Unmarshal(bufUDP[:n]) | ||||||
| 				s.HandleRTP(pack) | 				s.HandleRTP(pack) | ||||||
| 			} else { | 			} else { | ||||||
| 				Println("udp server read video control pack error", err) | 				Println("udp server read video control pack error", err) | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								ui/dist/plugin-rtsp.common.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								ui/dist/plugin-rtsp.common.js
									
									
									
									
										vendored
									
									
								
							| @@ -172,14 +172,14 @@ if (typeof window !== 'undefined') { | |||||||
| // Indicate to webpack that this file can be concatenated | // Indicate to webpack that this file can be concatenated | ||||||
| /* harmony default export */ var setPublicPath = (null); | /* harmony default export */ var setPublicPath = (null); | ||||||
|  |  | ||||||
| // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=0225c7f1& | // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=de44c72c& | ||||||
| var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){ | var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){ | ||||||
| var item = ref.row; | var item = ref.row; | ||||||
| return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_c('Progress',{attrs:{"stroke-width":20,"percent":Math.ceil(item.BufferRate),"text-inside":""}})],1),_c('td',[_vm._v(_vm._s(item.SyncCount))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)} | return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_vm._v(_vm._s(_vm.unitFormat(item.InBytes)))]),_c('td',[_vm._v(_vm._s(_vm.unitFormat(item.OutBytes)))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")]),_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.stop(item)}}},[_vm._v("中止")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)} | ||||||
| var staticRenderFns = [] | var staticRenderFns = [] | ||||||
|  |  | ||||||
|  |  | ||||||
| // CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=0225c7f1& | // CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=de44c72c& | ||||||
|  |  | ||||||
| // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& | // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& | ||||||
| // | // | ||||||
| @@ -203,12 +203,22 @@ var staticRenderFns = [] | |||||||
| // | // | ||||||
| // | // | ||||||
| // | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  |  | ||||||
| let listES = null; | let listES = null; | ||||||
| /* harmony default export */ var Appvue_type_script_lang_js_ = ({ | /* harmony default export */ var Appvue_type_script_lang_js_ = ({ | ||||||
|     components: { |  | ||||||
|         StartTime |  | ||||||
|     }, |  | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       currentStream: null, |       currentStream: null, | ||||||
| @@ -216,7 +226,13 @@ let listES = null; | |||||||
|       remoteAddr: "", |       remoteAddr: "", | ||||||
|       streamPath: "", |       streamPath: "", | ||||||
|       openPull: false, |       openPull: false, | ||||||
|             columns:["StreamPath","开始时间","缓冲","同步数","操作" ].map(title=>({title})) |       columns: [ | ||||||
|  |         "StreamPath", | ||||||
|  |         "开始时间", | ||||||
|  |         "总接收", | ||||||
|  |         "总发送", | ||||||
|  |         "操作" | ||||||
|  |       ].map(title => ({ title })) | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -233,10 +249,10 @@ let listES = null; | |||||||
|     }, |     }, | ||||||
|     showHeader(item) { |     showHeader(item) { | ||||||
|       this.$Modal.info({ |       this.$Modal.info({ | ||||||
|                 title: "RTSP Header", |         title: "RTSP SDPRaw", | ||||||
|         width: "1000px", |         width: "1000px", | ||||||
|         scrollable: true, |         scrollable: true, | ||||||
|                 content: item.Header |         content: item.SDPRaw | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|     addPull() { |     addPull() { | ||||||
| @@ -253,6 +269,19 @@ let listES = null; | |||||||
|             this.$toast.error(x.msg); |             this.$toast.error(x.msg); | ||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|  |     }, | ||||||
|  |     stop(item) { | ||||||
|  |       this.ajax | ||||||
|  |         .get(this.apiHost + "/api/stop", { | ||||||
|  |           stream: item.StreamInfo.StreamPath | ||||||
|  |         }) | ||||||
|  |         .then(x => { | ||||||
|  |           if (x == "success") { | ||||||
|  |             this.$toast.success("已停止拉流"); | ||||||
|  |           } else { | ||||||
|  |             this.$toast.error(x.msg); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
| @@ -267,7 +296,7 @@ let listES = null; | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|         ] |     ]; | ||||||
|   }, |   }, | ||||||
|   destroyed() { |   destroyed() { | ||||||
|     listES.close(); |     listES.close(); | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								ui/dist/plugin-rtsp.common.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								ui/dist/plugin-rtsp.common.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										49
									
								
								ui/dist/plugin-rtsp.umd.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								ui/dist/plugin-rtsp.umd.js
									
									
									
									
										vendored
									
									
								
							| @@ -181,14 +181,14 @@ if (typeof window !== 'undefined') { | |||||||
| // Indicate to webpack that this file can be concatenated | // Indicate to webpack that this file can be concatenated | ||||||
| /* harmony default export */ var setPublicPath = (null); | /* harmony default export */ var setPublicPath = (null); | ||||||
|  |  | ||||||
| // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=0225c7f1& | // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"29918b3a-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=template&id=de44c72c& | ||||||
| var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){ | var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('mu-data-table',{attrs:{"data":_vm.Streams,"columns":_vm.columns},scopedSlots:_vm._u([{key:"default",fn:function(ref){ | ||||||
| var item = ref.row; | var item = ref.row; | ||||||
| return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_c('Progress',{attrs:{"stroke-width":20,"percent":Math.ceil(item.BufferRate),"text-inside":""}})],1),_c('td',[_vm._v(_vm._s(item.SyncCount))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)} | return [_c('td',[_vm._v(_vm._s(item.StreamInfo.StreamPath))]),_c('td',[_c('StartTime',{attrs:{"value":item.StreamInfo.StartTime}})],1),_c('td',[_vm._v(_vm._s(_vm.unitFormat(item.InBytes)))]),_c('td',[_vm._v(_vm._s(_vm.unitFormat(item.OutBytes)))]),_c('td',[_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.showHeader(item)}}},[_vm._v("头信息")]),_c('mu-button',{attrs:{"flat":""},on:{"click":function($event){return _vm.stop(item)}}},[_vm._v("中止")])],1)]}}])}),_c('mu-dialog',{attrs:{"title":"拉流转发","width":"360","open":_vm.openPull},on:{"update:open":function($event){_vm.openPull=$event}}},[_c('mu-text-field',{attrs:{"label":"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:(_vm.remoteAddr),callback:function ($$v) {_vm.remoteAddr=$$v},expression:"remoteAddr"}}),_c('mu-text-field',{attrs:{"label":"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:(_vm.streamPath),callback:function ($$v) {_vm.streamPath=$$v},expression:"streamPath"}}),_c('mu-button',{attrs:{"slot":"actions","flat":"","color":"primary"},on:{"click":_vm.addPull},slot:"actions"},[_vm._v("确定")])],1)],1)} | ||||||
| var staticRenderFns = [] | var staticRenderFns = [] | ||||||
|  |  | ||||||
|  |  | ||||||
| // CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=0225c7f1& | // CONCATENATED MODULE: ./src/App.vue?vue&type=template&id=de44c72c& | ||||||
|  |  | ||||||
| // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& | // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& | ||||||
| // | // | ||||||
| @@ -212,12 +212,22 @@ var staticRenderFns = [] | |||||||
| // | // | ||||||
| // | // | ||||||
| // | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  | // | ||||||
|  |  | ||||||
| let listES = null; | let listES = null; | ||||||
| /* harmony default export */ var Appvue_type_script_lang_js_ = ({ | /* harmony default export */ var Appvue_type_script_lang_js_ = ({ | ||||||
|     components: { |  | ||||||
|         StartTime |  | ||||||
|     }, |  | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       currentStream: null, |       currentStream: null, | ||||||
| @@ -225,7 +235,13 @@ let listES = null; | |||||||
|       remoteAddr: "", |       remoteAddr: "", | ||||||
|       streamPath: "", |       streamPath: "", | ||||||
|       openPull: false, |       openPull: false, | ||||||
|             columns:["StreamPath","开始时间","缓冲","同步数","操作" ].map(title=>({title})) |       columns: [ | ||||||
|  |         "StreamPath", | ||||||
|  |         "开始时间", | ||||||
|  |         "总接收", | ||||||
|  |         "总发送", | ||||||
|  |         "操作" | ||||||
|  |       ].map(title => ({ title })) | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -242,10 +258,10 @@ let listES = null; | |||||||
|     }, |     }, | ||||||
|     showHeader(item) { |     showHeader(item) { | ||||||
|       this.$Modal.info({ |       this.$Modal.info({ | ||||||
|                 title: "RTSP Header", |         title: "RTSP SDPRaw", | ||||||
|         width: "1000px", |         width: "1000px", | ||||||
|         scrollable: true, |         scrollable: true, | ||||||
|                 content: item.Header |         content: item.SDPRaw | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|     addPull() { |     addPull() { | ||||||
| @@ -262,6 +278,19 @@ let listES = null; | |||||||
|             this.$toast.error(x.msg); |             this.$toast.error(x.msg); | ||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|  |     }, | ||||||
|  |     stop(item) { | ||||||
|  |       this.ajax | ||||||
|  |         .get(this.apiHost + "/api/stop", { | ||||||
|  |           stream: item.StreamInfo.StreamPath | ||||||
|  |         }) | ||||||
|  |         .then(x => { | ||||||
|  |           if (x == "success") { | ||||||
|  |             this.$toast.success("已停止拉流"); | ||||||
|  |           } else { | ||||||
|  |             this.$toast.error(x.msg); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
| @@ -276,7 +305,7 @@ let listES = null; | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|         ] |     ]; | ||||||
|   }, |   }, | ||||||
|   destroyed() { |   destroyed() { | ||||||
|     listES.close(); |     listES.close(); | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								ui/dist/plugin-rtsp.umd.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								ui/dist/plugin-rtsp.umd.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								ui/dist/plugin-rtsp.umd.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								ui/dist/plugin-rtsp.umd.min.js
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,2 @@ | |||||||
| (function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e():"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["plugin-rtsp"]=e():t["plugin-rtsp"]=e()})("undefined"!==typeof self?self:this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s="fb15")}({"034f":function(t,e,r){"use strict";var n=r("85ec"),o=r.n(n);o.a},"85ec":function(t,e,r){},f6fd:function(t,e){(function(t){var e="currentScript",r=t.getElementsByTagName("script");e in t||Object.defineProperty(t,e,{get:function(){try{throw new Error}catch(n){var t,e=(/.*at [^\(]*\((.*):.+:.+\)$/gi.exec(n.stack)||[!1])[1];for(t in r)if(r[t].src==e||"interactive"==r[t].readyState)return r[t];return null}}})})(document)},fb15:function(t,e,r){"use strict";var n;(r.r(e),"undefined"!==typeof window)&&(r("f6fd"),(n=window.document.currentScript)&&(n=n.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(r.p=n[1]));var o=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",[r("mu-data-table",{attrs:{data:t.Streams,columns:t.columns},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return[r("td",[t._v(t._s(n.StreamInfo.StreamPath))]),r("td",[r("StartTime",{attrs:{value:n.StreamInfo.StartTime}})],1),r("td",[r("Progress",{attrs:{"stroke-width":20,percent:Math.ceil(n.BufferRate),"text-inside":""}})],1),r("td",[t._v(t._s(n.SyncCount))]),r("td",[r("mu-button",{attrs:{flat:""},on:{click:function(e){return t.showHeader(n)}}},[t._v("头信息")])],1)]}}])}),r("mu-dialog",{attrs:{title:"拉流转发",width:"360",open:t.openPull},on:{"update:open":function(e){t.openPull=e}}},[r("mu-text-field",{attrs:{label:"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:t.remoteAddr,callback:function(e){t.remoteAddr=e},expression:"remoteAddr"}}),r("mu-text-field",{attrs:{label:"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:t.streamPath,callback:function(e){t.streamPath=e},expression:"streamPath"}}),r("mu-button",{attrs:{slot:"actions",flat:"",color:"primary"},on:{click:t.addPull},slot:"actions"},[t._v("确定")])],1)],1)},a=[];let s=null;var i={components:{StartTime:StartTime},data(){return{currentStream:null,Streams:null,remoteAddr:"",streamPath:"",openPull:!1,columns:["StreamPath","开始时间","缓冲","同步数","操作"].map(t=>({title:t}))}},methods:{fetchlist(){s=new EventSource(this.apiHost+"/rtsp/list"),s.onmessage=t=>{t.data&&(this.Streams=JSON.parse(t.data)||[],this.Streams.sort((t,e)=>t.StreamInfo.StreamPath>e.StreamInfo.StreamPath?1:-1))}},showHeader(t){this.$Modal.info({title:"RTSP Header",width:"1000px",scrollable:!0,content:t.Header})},addPull(){this.openPull=!1,this.ajax.getJSON(this.apiHost+"/rtsp/pull",{target:this.remoteAddr,streamPath:this.streamPath}).then(t=>{0==t.code?this.$toast.success("已启动拉流"):this.$toast.error(t.msg)})}},mounted(){this.fetchlist();let t=this;this.$parent.titleOps=[{template:'<m-button @click="onClick">拉流转发</m-button>',methods:{onClick(){t.openPull=!0}}}]},destroyed(){s.close()}},l=i;r("034f");function u(t,e,r,n,o,a,s,i){var l,u="function"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=r,u._compiled=!0),n&&(u.functional=!0),a&&(u._scopeId="data-v-"+a),s?(l=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(s)},u._ssrRegister=l):o&&(l=i?function(){o.call(this,this.$root.$options.shadowRoot)}:o),l)if(u.functional){u._injectStyles=l;var c=u.render;u.render=function(t,e){return l.call(e),c(t,e)}}else{var d=u.beforeCreate;u.beforeCreate=d?[].concat(d,l):[l]}return{exports:t,options:u}}var c=u(l,o,a,!1,null,null,null),d=c.exports;e["default"]=d}})["default"]})); | (function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e():"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["plugin-rtsp"]=e():t["plugin-rtsp"]=e()})("undefined"!==typeof self?self:this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s="fb15")}({"034f":function(t,e,r){"use strict";var n=r("85ec"),o=r.n(n);o.a},"85ec":function(t,e,r){},f6fd:function(t,e){(function(t){var e="currentScript",r=t.getElementsByTagName("script");e in t||Object.defineProperty(t,e,{get:function(){try{throw new Error}catch(n){var t,e=(/.*at [^\(]*\((.*):.+:.+\)$/gi.exec(n.stack)||[!1])[1];for(t in r)if(r[t].src==e||"interactive"==r[t].readyState)return r[t];return null}}})})(document)},fb15:function(t,e,r){"use strict";var n;(r.r(e),"undefined"!==typeof window)&&(r("f6fd"),(n=window.document.currentScript)&&(n=n.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(r.p=n[1]));var o=function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",[r("mu-data-table",{attrs:{data:t.Streams,columns:t.columns},scopedSlots:t._u([{key:"default",fn:function(e){var n=e.row;return[r("td",[t._v(t._s(n.StreamInfo.StreamPath))]),r("td",[r("StartTime",{attrs:{value:n.StreamInfo.StartTime}})],1),r("td",[t._v(t._s(t.unitFormat(n.InBytes)))]),r("td",[t._v(t._s(t.unitFormat(n.OutBytes)))]),r("td",[r("mu-button",{attrs:{flat:""},on:{click:function(e){return t.showHeader(n)}}},[t._v("头信息")]),r("mu-button",{attrs:{flat:""},on:{click:function(e){return t.stop(n)}}},[t._v("中止")])],1)]}}])}),r("mu-dialog",{attrs:{title:"拉流转发",width:"360",open:t.openPull},on:{"update:open":function(e){t.openPull=e}}},[r("mu-text-field",{attrs:{label:"rtsp url","label-float":"","help-text":"Please enter URL of rtsp..."},model:{value:t.remoteAddr,callback:function(e){t.remoteAddr=e},expression:"remoteAddr"}}),r("mu-text-field",{attrs:{label:"streamPath","label-float":"","help-text":"Please enter streamPath to publish."},model:{value:t.streamPath,callback:function(e){t.streamPath=e},expression:"streamPath"}}),r("mu-button",{attrs:{slot:"actions",flat:"",color:"primary"},on:{click:t.addPull},slot:"actions"},[t._v("确定")])],1)],1)},a=[];let s=null;var i={data(){return{currentStream:null,Streams:null,remoteAddr:"",streamPath:"",openPull:!1,columns:["StreamPath","开始时间","总接收","总发送","操作"].map(t=>({title:t}))}},methods:{fetchlist(){s=new EventSource(this.apiHost+"/rtsp/list"),s.onmessage=t=>{t.data&&(this.Streams=JSON.parse(t.data)||[],this.Streams.sort((t,e)=>t.StreamInfo.StreamPath>e.StreamInfo.StreamPath?1:-1))}},showHeader(t){this.$Modal.info({title:"RTSP SDPRaw",width:"1000px",scrollable:!0,content:t.SDPRaw})},addPull(){this.openPull=!1,this.ajax.getJSON(this.apiHost+"/rtsp/pull",{target:this.remoteAddr,streamPath:this.streamPath}).then(t=>{0==t.code?this.$toast.success("已启动拉流"):this.$toast.error(t.msg)})},stop(t){this.ajax.get(this.apiHost+"/api/stop",{stream:t.StreamInfo.StreamPath}).then(t=>{"success"==t?this.$toast.success("已停止拉流"):this.$toast.error(t.msg)})}},mounted(){this.fetchlist();let t=this;this.$parent.titleOps=[{template:'<m-button @click="onClick">拉流转发</m-button>',methods:{onClick(){t.openPull=!0}}}]},destroyed(){s.close()}},l=i;r("034f");function u(t,e,r,n,o,a,s,i){var l,u="function"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=r,u._compiled=!0),n&&(u.functional=!0),a&&(u._scopeId="data-v-"+a),s?(l=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(s)},u._ssrRegister=l):o&&(l=i?function(){o.call(this,this.$root.$options.shadowRoot)}:o),l)if(u.functional){u._injectStyles=l;var c=u.render;u.render=function(t,e){return l.call(e),c(t,e)}}else{var f=u.beforeCreate;u.beforeCreate=f?[].concat(f,l):[l]}return{exports:t,options:u}}var c=u(l,o,a,!1,null,null,null),f=c.exports;e["default"]=f}})["default"]})); | ||||||
| //# sourceMappingURL=plugin-rtsp.umd.min.js.map | //# sourceMappingURL=plugin-rtsp.umd.min.js.map | ||||||
							
								
								
									
										2
									
								
								ui/dist/plugin-rtsp.umd.min.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								ui/dist/plugin-rtsp.umd.min.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -3,17 +3,30 @@ | |||||||
|     <mu-data-table :data="Streams" :columns="columns"> |     <mu-data-table :data="Streams" :columns="columns"> | ||||||
|       <template #default="{row:item}"> |       <template #default="{row:item}"> | ||||||
|         <td>{{item.StreamInfo.StreamPath}}</td> |         <td>{{item.StreamInfo.StreamPath}}</td> | ||||||
|                 <td><StartTime :value="item.StreamInfo.StartTime"></StartTime></td> |         <td> | ||||||
|                 <td><Progress :stroke-width="20" :percent="Math.ceil(item.BufferRate)" text-inside /></td> |           <StartTime :value="item.StreamInfo.StartTime"></StartTime> | ||||||
|                 <td>{{item.SyncCount}}</td> |         </td> | ||||||
|                 <td><mu-button flat @click="showHeader(item)">头信息</mu-button></td> |         <td>{{unitFormat(item.InBytes)}}</td> | ||||||
|  |         <td>{{unitFormat(item.OutBytes)}}</td> | ||||||
|  |         <td> | ||||||
|  |           <mu-button flat @click="showHeader(item)">头信息</mu-button> | ||||||
|  |           <mu-button flat @click="stop(item)">中止</mu-button> | ||||||
|  |         </td> | ||||||
|       </template> |       </template> | ||||||
|     </mu-data-table> |     </mu-data-table> | ||||||
|     <mu-dialog title="拉流转发" width="360" :open.sync="openPull"> |     <mu-dialog title="拉流转发" width="360" :open.sync="openPull"> | ||||||
|             <mu-text-field v-model="remoteAddr" label="rtsp url" label-float help-text="Please enter URL of rtsp..."> |       <mu-text-field | ||||||
|             </mu-text-field> |         v-model="remoteAddr" | ||||||
|             <mu-text-field v-model="streamPath" label="streamPath" label-float |         label="rtsp url" | ||||||
|                 help-text="Please enter streamPath to publish."></mu-text-field> |         label-float | ||||||
|  |         help-text="Please enter URL of rtsp..." | ||||||
|  |       ></mu-text-field> | ||||||
|  |       <mu-text-field | ||||||
|  |         v-model="streamPath" | ||||||
|  |         label="streamPath" | ||||||
|  |         label-float | ||||||
|  |         help-text="Please enter streamPath to publish." | ||||||
|  |       ></mu-text-field> | ||||||
|       <mu-button slot="actions" flat color="primary" @click="addPull">确定</mu-button> |       <mu-button slot="actions" flat color="primary" @click="addPull">确定</mu-button> | ||||||
|     </mu-dialog> |     </mu-dialog> | ||||||
|   </div> |   </div> | ||||||
| @@ -22,9 +35,6 @@ | |||||||
| <script> | <script> | ||||||
| let listES = null; | let listES = null; | ||||||
| export default { | export default { | ||||||
|     components: { |  | ||||||
|         StartTime |  | ||||||
|     }, |  | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       currentStream: null, |       currentStream: null, | ||||||
| @@ -32,7 +42,13 @@ export default { | |||||||
|       remoteAddr: "", |       remoteAddr: "", | ||||||
|       streamPath: "", |       streamPath: "", | ||||||
|       openPull: false, |       openPull: false, | ||||||
|             columns:["StreamPath","开始时间","缓冲","同步数","操作" ].map(title=>({title})) |       columns: [ | ||||||
|  |         "StreamPath", | ||||||
|  |         "开始时间", | ||||||
|  |         "总接收", | ||||||
|  |         "总发送", | ||||||
|  |         "操作" | ||||||
|  |       ].map(title => ({ title })) | ||||||
|     }; |     }; | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
| @@ -49,10 +65,10 @@ export default { | |||||||
|     }, |     }, | ||||||
|     showHeader(item) { |     showHeader(item) { | ||||||
|       this.$Modal.info({ |       this.$Modal.info({ | ||||||
|                 title: "RTSP Header", |         title: "RTSP SDPRaw", | ||||||
|         width: "1000px", |         width: "1000px", | ||||||
|         scrollable: true, |         scrollable: true, | ||||||
|                 content: item.Header |         content: item.SDPRaw | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|     addPull() { |     addPull() { | ||||||
| @@ -69,6 +85,19 @@ export default { | |||||||
|             this.$toast.error(x.msg); |             this.$toast.error(x.msg); | ||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|  |     }, | ||||||
|  |     stop(item) { | ||||||
|  |       this.ajax | ||||||
|  |         .get(this.apiHost + "/api/stop", { | ||||||
|  |           stream: item.StreamInfo.StreamPath | ||||||
|  |         }) | ||||||
|  |         .then(x => { | ||||||
|  |           if (x == "success") { | ||||||
|  |             this.$toast.success("已停止拉流"); | ||||||
|  |           } else { | ||||||
|  |             this.$toast.error(x.msg); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   mounted() { |   mounted() { | ||||||
| @@ -83,7 +112,7 @@ export default { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|         ] |     ]; | ||||||
|   }, |   }, | ||||||
|   destroyed() { |   destroyed() { | ||||||
|     listES.close(); |     listES.close(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user