rtsp-simple-server
rtsp-simple-server
是RTSP / RTMP / LL-HLS 服务器和代理,允许读取、发布和代理视频和音频流。
目前支持的流有:
RTSP
,
RTMP
,
HLS
。
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 failuresif 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 changeif 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 failuresif 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}returnnil}
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的简单过程了,如果想进行深入了解的话,可以从代码中获得。
版权归原作者 go2coding 所有, 如有侵权,请联系我们删除。