diff --git a/README.md b/README.md index bed214c9..ce772537 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,8 @@ makepkg -si or _GStreamer_: ```sh - gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! queue ! s.sink_0 d.audio_0 ! queue ! s.sink_1 + gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream filesrc location=file.mp4 \ + ! qtdemux name=d d.video_0 ! queue ! s.sink_0 d.audio_0 ! queue ! s.sink_1 ``` 2. Open the stream. For instance, you can open the stream with _VLC_: @@ -260,19 +261,24 @@ The resulting stream will be available in path `/mystream`. GStreamer can publish a stream to the server in multiple ways (RTSP client, RTMP client, UDP/MPEG-TS, WebRTC with WHIP). The recommended one consists in publishing as a [RTSP client](#rtsp-clients): ```sh -gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! queue ! s.sink_0 d.audio_0 ! queue ! s.sink_1 +gst-launch-1.0 rtspclientsink name=s location=rtsp://localhost:8554/mystream \ +filesrc location=file.mp4 ! qtdemux name=d \ +d.video_0 ! queue ! s.sink_0 \ +d.audio_0 ! queue ! s.sink_1 ``` If the stream is video only: ```sh -gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! rtspclientsink name=s location=rtsp://localhost:8554/mystream +gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d \ +d.video_0 ! rtspclientsink location=rtsp://localhost:8554/mystream ``` The RTSP protocol supports multiple underlying transport protocols, each with its own characteristics (see [RTSP-specific features](#rtsp-specific-features)). You can set the transport protocol by using the `protocols` flag: ```sh -gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d d.video_0 ! rtspclientsink protocols=tcp name=s location=rtsp://localhost:8554/mystream +gst-launch-1.0 filesrc location=file.mp4 ! qtdemux name=d \ +d.video_0 ! rtspclientsink protocols=tcp name=s location=rtsp://localhost:8554/mystream ``` The resulting stream will be available in path `/mystream`. @@ -831,7 +837,10 @@ Although the server can produce HLS with a variety of video and audio codecs (th If you want to support most browsers, you can to re-encode the stream by using the H264 and AAC codecs, for instance by using FFmpeg: ```sh -ffmpeg -i rtsp://original-source -pix_fmt yuv420p -c:v libx264 -preset ultrafast -b:v 600k -c:a aac -b:a 160k -f rtsp rtsp://localhost:8554/mystream +ffmpeg -i rtsp://original-source \ +-pix_fmt yuv420p -c:v libx264 -preset ultrafast -b:v 600k \ +-c:a aac -b:a 160k \ +-f rtsp rtsp://localhost:8554/mystream ``` ##### LL-HLS @@ -1038,7 +1047,10 @@ To change the format, codec or compression of a stream, use _FFmpeg_ or _GStream paths: all: original: - runOnReady: ffmpeg -i rtsp://localhost:$RTSP_PORT/$MTX_PATH -pix_fmt yuv420p -c:v libx264 -preset ultrafast -b:v 600k -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:$RTSP_PORT/compressed + runOnReady: > + ffmpeg -i rtsp://localhost:$RTSP_PORT/$MTX_PATH + -pix_fmt yuv420p -c:v libx264 -preset ultrafast -b:v 600k + -max_muxing_queue_size 1024 -f rtsp rtsp://localhost:$RTSP_PORT/compressed runOnReadyRestart: yes ``` @@ -1049,7 +1061,10 @@ To save available streams to disk, you can use the `runOnReady` parameter and _F ```yml paths: mypath: - runOnReady: ffmpeg -i rtsp://localhost:$RTSP_PORT/$MTX_PATH -c copy -f segment -strftime 1 -segment_time 60 -segment_format mpegts saved_%Y-%m-%d_%H-%M-%S.ts + runOnReady: > + ffmpeg -i rtsp://localhost:$RTSP_PORT/$MTX_PATH + -c copy + -f segment -strftime 1 -segment_time 60 -segment_format mpegts saved_%Y-%m-%d_%H-%M-%S.ts runOnReadyRestart: yes ``` diff --git a/internal/core/api.go b/internal/core/api.go index fc9caa4d..da1c8473 100644 --- a/internal/core/api.go +++ b/internal/core/api.go @@ -139,6 +139,16 @@ func abortWithError(ctx *gin.Context, err error) { } } +func paramName(ctx *gin.Context) (string, bool) { + name := ctx.Param("name") + + if len(name) < 2 || name[0] != '/' { + return "", false + } + + return name[1:], true +} + type apiPathManager interface { apiPathsList() (*apiPathsList, error) apiPathsGet(string) (*apiPath, error) @@ -333,12 +343,11 @@ func (a *api) onConfigSet(ctx *gin.Context) { } func (a *api) onConfigPathsAdd(ctx *gin.Context) { - name := ctx.Param("name") - if len(name) < 2 || name[0] != '/' { + name, ok := paramName(ctx) + if !ok { ctx.AbortWithStatus(http.StatusBadRequest) return } - name = name[1:] in, err := loadConfPathData(ctx) if err != nil { @@ -381,12 +390,11 @@ func (a *api) onConfigPathsAdd(ctx *gin.Context) { } func (a *api) onConfigPathsEdit(ctx *gin.Context) { - name := ctx.Param("name") - if len(name) < 2 || name[0] != '/' { + name, ok := paramName(ctx) + if !ok { ctx.AbortWithStatus(http.StatusBadRequest) return } - name = name[1:] in, err := loadConfPathData(ctx) if err != nil { @@ -423,12 +431,11 @@ func (a *api) onConfigPathsEdit(ctx *gin.Context) { } func (a *api) onConfigPathsDelete(ctx *gin.Context) { - name := ctx.Param("name") - if len(name) < 2 || name[0] != '/' { + name, ok := paramName(ctx) + if !ok { ctx.AbortWithStatus(http.StatusBadRequest) return } - name = name[1:] a.mutex.Lock() defer a.mutex.Unlock() @@ -475,12 +482,11 @@ func (a *api) onPathsList(ctx *gin.Context) { } func (a *api) onPathsGet(ctx *gin.Context) { - name := ctx.Param("name") - if len(name) < 2 || name[0] != '/' { + name, ok := paramName(ctx) + if !ok { ctx.AbortWithStatus(http.StatusBadRequest) return } - name = name[1:] data, err := a.pathManager.apiPathsGet(name) if err != nil { @@ -778,12 +784,11 @@ func (a *api) onHLSMuxersList(ctx *gin.Context) { } func (a *api) onHLSMuxersGet(ctx *gin.Context) { - name := ctx.Param("name") - if len(name) < 2 || name[0] != '/' { + name, ok := paramName(ctx) + if !ok { ctx.AbortWithStatus(http.StatusBadRequest) return } - name = name[1:] data, err := a.hlsManager.apiMuxersGet(name) if err != nil {