Github每日精选(第50期):RTSP / RTMP / LL-HLS 服务器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
用法

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

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

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

vlc rtsp://192.168.100.170:8554/mystream

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

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

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

代码分析

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
}

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)
}

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

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 我点击了您提供的链接,它似乎是一个国家级本科电子设计竞赛的GitHub项目。这个项目可能包含参与竞赛的学生或团队的电子设计作品,并且通过GitHub进行共享和展示。 在这个链接中,我没有找到详细的说明文档或项目描述,只能看到一些文件和文件夹。这些文件可能包含提交给竞赛的设计文件,可能是硬件电路设计的原理图、PCB板设计的Gerber文件、以及相应的固件程序文件等。在没有具体说明时,我无法确定这些文件的具体内容。 如果您对这个项目感兴趣并希望了解更多信息,最好的办法是联系项目的创建者或维护者,向他们询问有关竞赛和设计项目的详细信息。您可以通过GitHub页面找到项目的创建者的联系方式,例如电子邮件或其他社交媒体平台。 总之,根据提供的链接,我可以看到这是一个国家级本科电子设计竞赛的GitHub项目。但是,由于没有详细的描述和说明,我无法提供更具体的信息。如有需要,请通过联系项目创建者了解更多相关内容。 ### 回答2: ttps://github.com/grf-sunomikp31/national-undergraduate-electronics-design-c 是一个GitHub上的项目链接。这个项目是一个关于国家本科电子设计竞赛的项目。 国家本科电子设计竞赛是一个旨在培养和展示大学本科生电子设计能力的竞赛。这个竞赛不仅能够锻炼学生的实践能力,还可以推动电子科技的发展和进步。这个GitHub项目是一个和这个竞赛相关的项目,可能包括竞赛规则、参赛项目的代码、设计文档等等。 在GitHub上发布这个项目的好处是可以方便地分享和查看项目的内容。其他参赛选手、教师、评委或其他有兴趣的人可以通过这个项目链接来了解和参考其中的内容。GitHub提供了版本控制和协作的功能,可以方便多人协作和管理项目。 如果你想了解这个项目的具体内容,可以点击链接进入GitHub页面,查看项目的文件和代码等。如果你对国家本科电子设计竞赛感兴趣,或者正在参加这个竞赛,通过GitHub链接可以获取到一些参考和学习的资源。 总之,这个ttps://github.com/grf-sunomikp31/national-undergraduate-electronics-design-c 是一个国家本科电子设计竞赛相关的GitHub项目,可以从中了解到关于竞赛的信息和资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go2coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值