当前位置:   article > 正文

Github每日精选(第50期):RTSP / RTMP / LL-HLS 服务器rtsp-simple-server

rtsp-simple-server

rtsp-simple-server

rtsp-simple-server 是RTSP / RTMP / LL-HLS 服务器和代理,允许读取、发布和代理视频和音频流。

目前支持的流有:RTSPRTMPHLS

在这里插入图片描述

github的地址在这里

特征:
  • 将直播流发布到服务器
  • 从服务器读取实时流
  • 来自其他服务器或摄像机的代理流,始终或按需
  • 每个流可以有多个视频和音频轨道,用任何 RTP 兼容的编解码器编码,包括 H264、H265、VP8、- VP9、MPEG2、MP3、AAC、Opus、PCM、JPEG
  • 流会自动从一种协议转换为另一种协议。例如,可以使用 RTSP 发布流并使用 HLS 读取它
  • 在不同的路径中一次提供多个流
  • 对用户进行身份验证;使用内部或外部身份验证
  • 将阅读器重定向到其他 RTSP 服务器(负载平衡)
  • 通过 HTTP API 查询和控制服务器
  • 在不断开现有客户端的情况下重新加载配置(热重新加载)
  • 阅读 Prometheus 兼容的指标
  • 当客户端连接、断开、读取或发布流时运行外部命令
  • 与 Raspberry Pi 相机原生兼容
  • 与 Linux、Windows 和 macOS 兼容,不需要任何依赖项或解释器,它是单个可执行文件
安装

安装非常的简单,采用docker的方式:

docker run --rm -it -e RTSP_PROTOCOLS=tcp -p 8554:8554 -p 1935:1935 -p 8888:8888 aler9/rtsp-simple-server
  • 1
用法

启动流服务器,记住那里的ip地址,并把流推到服务器上。

ffmpeg.exe -re -i a.mp4 -vcodec h264 -acodec aac -f rtsp -rtsp_transport tcp rtsp://192.168.100.170:8554/mystream
  • 1

我这里采用的是ffmpeg 把流推到服务器,服务器的地址为192.168.100.170,我们把它推到mystream 的目录下,接下来取流的方式就比较简单了,可以使用vlc进行播放。

vlc rtsp://192.168.100.170:8554/mystream
  • 1

也可以用直接拉流,存为视频:

ffmpeg -i rtsp://192.168.100.170:8554/mystream -c copy output.mp4
  • 1

这样就完成了一次取流和拉流的功能,可能大家对这种流媒体并不熟悉,但是流媒体编程也是一个中友好的方向,下面简单看下代码过程。

代码分析

rtsp-simple-server使用的是go语言编写,如果学着编写一款服务器的软件,可以用这款作为入门的代码。

rtsp的授权过程:

func (c *rtspConn) authenticate(
	pathName string,
	pathIPs []fmt.Stringer,
	pathUser conf.Credential,
	pathPass conf.Credential,
	isPublishing bool,
	req *base.Request,
	query string,
) error {
	if c.externalAuthenticationURL != "" {
		username := ""
		password := ""

		var auth headers.Authorization
		err := auth.Unmarshal(req.Header["Authorization"])
		if err == nil && auth.Method == headers.AuthBasic {
			username = auth.BasicUser
			password = auth.BasicPass
		}

		err = externalAuth(
			c.externalAuthenticationURL,
			c.ip().String(),
			username,
			password,
			pathName,
			isPublishing,
			query)
		if err != nil {
			c.authFailures++

			// VLC with login prompt sends 4 requests:
			// 1) without credentials
			// 2) with password but without username
			// 3) without credentials
			// 4) with password and username
			// therefore we must allow up to 3 failures
			if c.authFailures > 3 {
				return pathErrAuthCritical{
					message: "unauthorized: " + err.Error(),
					response: &base.Response{
						StatusCode: base.StatusUnauthorized,
					},
				}
			}

			v := "IPCAM"
			return pathErrAuthNotCritical{
				message: "unauthorized: " + err.Error(),
				response: &base.Response{
					StatusCode: base.StatusUnauthorized,
					Header: base.Header{
						"WWW-Authenticate": headers.Authenticate{
							Method: headers.AuthBasic,
							Realm:  &v,
						}.Marshal(),
					},
				},
			}
		}
	}

	if pathIPs != nil {
		ip := c.ip()
		if !ipEqualOrInRange(ip, pathIPs) {
			return pathErrAuthCritical{
				message: fmt.Sprintf("IP '%s' not allowed", ip),
				response: &base.Response{
					StatusCode: base.StatusUnauthorized,
				},
			}
		}
	}

	if pathUser != "" {
		// reset authValidator every time the credentials change
		if c.authValidator == nil || c.authUser != string(pathUser) || c.authPass != string(pathPass) {
			c.authUser = string(pathUser)
			c.authPass = string(pathPass)
			c.authValidator = auth.NewValidator(string(pathUser), string(pathPass), c.authMethods)
		}

		err := c.authValidator.ValidateRequest(req)
		if err != nil {
			c.authFailures++

			// VLC with login prompt sends 4 requests:
			// 1) without credentials
			// 2) with password but without username
			// 3) without credentials
			// 4) with password and username
			// therefore we must allow up to 3 failures
			if c.authFailures > 3 {
				return pathErrAuthCritical{
					message: "unauthorized: " + err.Error(),
					response: &base.Response{
						StatusCode: base.StatusUnauthorized,
					},
				}
			}

			return pathErrAuthNotCritical{
				response: &base.Response{
					StatusCode: base.StatusUnauthorized,
					Header: base.Header{
						"WWW-Authenticate": c.authValidator.Header(),
					},
				},
			}
		}

		// login successful, reset authFailures
		c.authFailures = 0
	}

	return nil
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

rtsp 的协议过程:


// OnDescribe implements gortsplib.ServerHandlerOnDescribe.
func (s *rtspServer) OnDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx,
) (*base.Response, *gortsplib.ServerStream, error) {
	s.mutex.RLock()
	c := s.conns[ctx.Conn]
	s.mutex.RUnlock()
	return c.onDescribe(ctx)
}

// OnAnnounce implements gortsplib.ServerHandlerOnAnnounce.
func (s *rtspServer) OnAnnounce(ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) {
	s.mutex.RLock()
	c := s.conns[ctx.Conn]
	se := s.sessions[ctx.Session]
	s.mutex.RUnlock()
	return se.onAnnounce(c, ctx)
}

// OnSetup implements gortsplib.ServerHandlerOnSetup.
func (s *rtspServer) OnSetup(ctx *gortsplib.ServerHandlerOnSetupCtx) (*base.Response, *gortsplib.ServerStream, error) {
	s.mutex.RLock()
	c := s.conns[ctx.Conn]
	se := s.sessions[ctx.Session]
	s.mutex.RUnlock()
	return se.onSetup(c, ctx)
}

// OnPlay implements gortsplib.ServerHandlerOnPlay.
func (s *rtspServer) OnPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
	s.mutex.RLock()
	se := s.sessions[ctx.Session]
	s.mutex.RUnlock()
	return se.onPlay(ctx)
}

// OnRecord implements gortsplib.ServerHandlerOnRecord.
func (s *rtspServer) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) {
	s.mutex.RLock()
	se := s.sessions[ctx.Session]
	s.mutex.RUnlock()
	return se.onRecord(ctx)
}

// OnPause implements gortsplib.ServerHandlerOnPause.
func (s *rtspServer) OnPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Response, error) {
	s.mutex.RLock()
	se := s.sessions[ctx.Session]
	s.mutex.RUnlock()
	return se.onPause(ctx)
}

// OnPacketRTP implements gortsplib.ServerHandlerOnPacket.
func (s *rtspServer) OnPacketRTP(ctx *gortsplib.ServerHandlerOnPacketRTPCtx) {
	s.mutex.RLock()
	se := s.sessions[ctx.Session]
	s.mutex.RUnlock()
	se.onPacketRTP(ctx)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

这个就是相当于rtsp的简单过程了,如果想进行深入了解的话,可以从代码中获得。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/148837
推荐阅读
相关标签
  

闽ICP备14008679号