Skip to content

Commit

Permalink
Merge branch 'bluenviron:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Apaisal authored Oct 22, 2024
2 parents 7789bb0 + 1dd9755 commit 0b2679f
Show file tree
Hide file tree
Showing 26 changed files with 373 additions and 194 deletions.
45 changes: 36 additions & 9 deletions .github/workflows/bump_hls_js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,48 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- run: >
git config user.name mediamtx-bot
&& git config user.email bot@mediamtx
&& ((git checkout deps/hlsjs && git rebase ${GITHUB_REF_NAME}) || git checkout -b deps/hlsjs)
- run: |
set -e
VERSION=$(curl -s https://api.github.com/repos/video-dev/hls.js/releases?per_page=1 | grep tag_name | sed 's/\s\+"tag_name": "\(.\+\)",/\1/')
HASH=$(curl -sL https://github.com/video-dev/hls.js/releases/download/$VERSION/release.zip -o- | sha256sum | cut -f1 -d ' ')
echo $VERSION > internal/servers/hls/hlsjsdownloader/VERSION
echo $HASH > internal/servers/hls/hlsjsdownloader/HASH
echo VERSION=$VERSION >> $GITHUB_ENV
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { createHash } = require('crypto');
const fs = require('fs').promises;
// get last release
let curRelease = null;
for (let i = 1; i < 20; i++) {
const releases = await github.rest.repos.listReleases({
owner: 'video-dev',
repo: 'hls.js',
page: i,
});
for (const release of releases.data) {
if (!release.prerelease) {
curRelease = release;
break;
}
}
if (curRelease !== null) {
break;
}
}
// compute checksum
const content = await github.request(`https://github.com/video-dev/hls.js/releases/download/${curRelease['tag_name']}/release.zip`);
const hash = createHash('sha256').update(Buffer.from(content.data)).digest('hex');
// write version and checksum to disk
await fs.writeFile('internal/servers/hls/hlsjsdownloader/VERSION', curRelease['tag_name'] + '\n', 'utf-8');
await fs.writeFile('internal/servers/hls/hlsjsdownloader/HASH', hash + '\n', 'utf-8');
// make version available to next steps
core.exportVariable('VERSION', curRelease['tag_name']);
- id: check_repo
run: >
Expand Down
87 changes: 87 additions & 0 deletions .github/workflows/bump_rpicamera.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: bump_rpicamera

on:
schedule:
- cron: '4 5 * * *'
workflow_dispatch:

jobs:
bump_rpicamera:
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4

- run: >
git config user.name mediamtx-bot
&& git config user.email bot@mediamtx
&& ((git checkout deps/mediamtx-rpicamera && git rebase ${GITHUB_REF_NAME}) || git checkout -b deps/mediamtx-rpicamera)
- uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs').promises;
// get last release
let curRelease = null;
for (let i = 1; i < 20; i++) {
const releases = await github.rest.repos.listReleases({
owner: 'bluenviron',
repo: 'mediamtx-rpicamera',
page: i,
});
for (const release of releases.data) {
curRelease = release;
break;
}
if (curRelease !== null) {
break;
}
}
// write version to disk
await fs.writeFile('internal/staticsources/rpicamera/mtxrpicamdownloader/VERSION', curRelease['tag_name'] + '\n', 'utf-8');
// make version available to next steps
core.exportVariable('VERSION', curRelease.name);
- id: check_repo
run: >
test -n "$(git status --porcelain)" && echo "update=1" >> "$GITHUB_OUTPUT" || echo "update=0" >> "$GITHUB_OUTPUT"
- if: ${{ steps.check_repo.outputs.update == '1' }}
run: >
git reset ${GITHUB_REF_NAME}
&& git add .
&& git commit -m "bump mediamtx-rpicamera to ${VERSION}"
&& git push --set-upstream origin deps/mediamtx-rpicamera --force
- if: ${{ steps.check_repo.outputs.update == '1' }}
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prs = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:deps/mediamtx-rpicamera`,
state: 'open',
});
if (prs.data.length == 0) {
await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
head: 'deps/mediamtx-rpicamera',
base: context.ref.slice('refs/heads/'.length),
title: `bump mediamtx-rpicamera to ${process.env.VERSION}`,
});
} else {
github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prs.data[0].number,
title: `bump mediamtx-rpicamera to ${process.env.VERSION}`,
});
}
2 changes: 0 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- run: make binaries

Expand Down
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ require (
github.com/alecthomas/kong v1.2.1
github.com/asticode/go-astits v1.13.0
github.com/bluenviron/gohlslib/v2 v2.0.0
github.com/bluenviron/gortsplib/v4 v4.11.0
github.com/bluenviron/mediacommon v1.13.0
github.com/bluenviron/gortsplib/v4 v4.11.1
github.com/bluenviron/mediacommon v1.13.1
github.com/datarhei/gosrt v0.7.0
github.com/fsnotify/fsnotify v1.7.0
github.com/gin-contrib/pprof v1.5.0
github.com/gin-gonic/gin v1.10.0
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/uuid v1.6.0
Expand Down Expand Up @@ -53,7 +55,6 @@ require (
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYh
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/bluenviron/gohlslib/v2 v2.0.0 h1:qAKT1ksqJT1Cc3xRseYPSiN51vqeSY1HDMnmUgQ6e0g=
github.com/bluenviron/gohlslib/v2 v2.0.0/go.mod h1:EoDeIps+MwLfm8o9m5N5YI1XgYXGeFne2Oz1kN9hudk=
github.com/bluenviron/gortsplib/v4 v4.11.0 h1:YPhXtxZS5M6yaMWmPSXt5wmOQr8CA2m2CHcdC+hisu4=
github.com/bluenviron/gortsplib/v4 v4.11.0/go.mod h1:ssggvDYcUCozVQUonQ9NJSjg7rI8zYeRmDt3sL1yqSo=
github.com/bluenviron/mediacommon v1.13.0 h1:axejqONTDkhBxAN1q+L0nEJn/VtARmE97CF3J1Bt414=
github.com/bluenviron/mediacommon v1.13.0/go.mod h1:HDyW2CzjvhYJXtdxstdFPio3G0qSocPhqkhUt/qffec=
github.com/bluenviron/gortsplib/v4 v4.11.1 h1:yq9mCVydwRUjyc4dzGrNs7/R8DvErbZ7fzIzBPlwVl8=
github.com/bluenviron/gortsplib/v4 v4.11.1/go.mod h1:yVEgmJnwHGo3Po7dWW0aVjZGCUmOULEZp9i1IMtWqao=
github.com/bluenviron/mediacommon v1.13.1 h1:agxDtkooknxSxOO/oOpB+tEW48OLMqty1PDMC3x2n4E=
github.com/bluenviron/mediacommon v1.13.1/go.mod h1:HDyW2CzjvhYJXtdxstdFPio3G0qSocPhqkhUt/qffec=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
Expand Down Expand Up @@ -69,6 +69,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/pprof v1.5.0 h1:E/Oy7g+kNw94KfdCy3bZxQFtyDnAX2V7axRS7sNYVrU=
github.com/gin-contrib/pprof v1.5.0/go.mod h1:GqFL6LerKoCQ/RSWnkYczkTJ+tOAUVN/8sbnEtaqOKs=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
Expand Down
96 changes: 49 additions & 47 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ type API struct {
SRTServer SRTServer
Parent apiParent

httpServer *httpp.WrappedServer
httpServer *httpp.Server
mutex sync.RWMutex
}

Expand All @@ -158,77 +158,79 @@ func (a *API) Initialize() error {
router := gin.New()
router.SetTrustedProxies(a.TrustedProxies.ToTrustedProxies()) //nolint:errcheck

router.NoRoute(a.middlewareOrigin, a.middlewareAuth)
group := router.Group("/", a.middlewareOrigin, a.middlewareAuth)
router.Use(a.middlewareOrigin)
router.Use(a.middlewareAuth)

group.GET("/v3/config/global/get", a.onConfigGlobalGet)
group.PATCH("/v3/config/global/patch", a.onConfigGlobalPatch)
group := router.Group("/v3")

group.GET("/v3/config/pathdefaults/get", a.onConfigPathDefaultsGet)
group.PATCH("/v3/config/pathdefaults/patch", a.onConfigPathDefaultsPatch)
group.GET("/config/global/get", a.onConfigGlobalGet)
group.PATCH("/config/global/patch", a.onConfigGlobalPatch)

group.GET("/v3/config/paths/list", a.onConfigPathsList)
group.GET("/v3/config/paths/get/*name", a.onConfigPathsGet)
group.POST("/v3/config/paths/add/*name", a.onConfigPathsAdd)
group.PATCH("/v3/config/paths/patch/*name", a.onConfigPathsPatch)
group.POST("/v3/config/paths/replace/*name", a.onConfigPathsReplace)
group.DELETE("/v3/config/paths/delete/*name", a.onConfigPathsDelete)
group.GET("/config/pathdefaults/get", a.onConfigPathDefaultsGet)
group.PATCH("/config/pathdefaults/patch", a.onConfigPathDefaultsPatch)

group.GET("/v3/paths/list", a.onPathsList)
group.GET("/v3/paths/get/*name", a.onPathsGet)
group.GET("/config/paths/list", a.onConfigPathsList)
group.GET("/config/paths/get/*name", a.onConfigPathsGet)
group.POST("/config/paths/add/*name", a.onConfigPathsAdd)
group.PATCH("/config/paths/patch/*name", a.onConfigPathsPatch)
group.POST("/config/paths/replace/*name", a.onConfigPathsReplace)
group.DELETE("/config/paths/delete/*name", a.onConfigPathsDelete)

group.GET("/paths/list", a.onPathsList)
group.GET("/paths/get/*name", a.onPathsGet)

if !interfaceIsEmpty(a.HLSServer) {
group.GET("/v3/hlsmuxers/list", a.onHLSMuxersList)
group.GET("/v3/hlsmuxers/get/*name", a.onHLSMuxersGet)
group.GET("/hlsmuxers/list", a.onHLSMuxersList)
group.GET("/hlsmuxers/get/*name", a.onHLSMuxersGet)
}

if !interfaceIsEmpty(a.RTSPServer) {
group.GET("/v3/rtspconns/list", a.onRTSPConnsList)
group.GET("/v3/rtspconns/get/:id", a.onRTSPConnsGet)
group.GET("/v3/rtspsessions/list", a.onRTSPSessionsList)
group.GET("/v3/rtspsessions/get/:id", a.onRTSPSessionsGet)
group.POST("/v3/rtspsessions/kick/:id", a.onRTSPSessionsKick)
group.GET("/rtspconns/list", a.onRTSPConnsList)
group.GET("/rtspconns/get/:id", a.onRTSPConnsGet)
group.GET("/rtspsessions/list", a.onRTSPSessionsList)
group.GET("/rtspsessions/get/:id", a.onRTSPSessionsGet)
group.POST("/rtspsessions/kick/:id", a.onRTSPSessionsKick)
}

if !interfaceIsEmpty(a.RTSPSServer) {
group.GET("/v3/rtspsconns/list", a.onRTSPSConnsList)
group.GET("/v3/rtspsconns/get/:id", a.onRTSPSConnsGet)
group.GET("/v3/rtspssessions/list", a.onRTSPSSessionsList)
group.GET("/v3/rtspssessions/get/:id", a.onRTSPSSessionsGet)
group.POST("/v3/rtspssessions/kick/:id", a.onRTSPSSessionsKick)
group.GET("/rtspsconns/list", a.onRTSPSConnsList)
group.GET("/rtspsconns/get/:id", a.onRTSPSConnsGet)
group.GET("/rtspssessions/list", a.onRTSPSSessionsList)
group.GET("/rtspssessions/get/:id", a.onRTSPSSessionsGet)
group.POST("/rtspssessions/kick/:id", a.onRTSPSSessionsKick)
}

if !interfaceIsEmpty(a.RTMPServer) {
group.GET("/v3/rtmpconns/list", a.onRTMPConnsList)
group.GET("/v3/rtmpconns/get/:id", a.onRTMPConnsGet)
group.POST("/v3/rtmpconns/kick/:id", a.onRTMPConnsKick)
group.GET("/rtmpconns/list", a.onRTMPConnsList)
group.GET("/rtmpconns/get/:id", a.onRTMPConnsGet)
group.POST("/rtmpconns/kick/:id", a.onRTMPConnsKick)
}

if !interfaceIsEmpty(a.RTMPSServer) {
group.GET("/v3/rtmpsconns/list", a.onRTMPSConnsList)
group.GET("/v3/rtmpsconns/get/:id", a.onRTMPSConnsGet)
group.POST("/v3/rtmpsconns/kick/:id", a.onRTMPSConnsKick)
group.GET("/rtmpsconns/list", a.onRTMPSConnsList)
group.GET("/rtmpsconns/get/:id", a.onRTMPSConnsGet)
group.POST("/rtmpsconns/kick/:id", a.onRTMPSConnsKick)
}

if !interfaceIsEmpty(a.WebRTCServer) {
group.GET("/v3/webrtcsessions/list", a.onWebRTCSessionsList)
group.GET("/v3/webrtcsessions/get/:id", a.onWebRTCSessionsGet)
group.POST("/v3/webrtcsessions/kick/:id", a.onWebRTCSessionsKick)
group.GET("/webrtcsessions/list", a.onWebRTCSessionsList)
group.GET("/webrtcsessions/get/:id", a.onWebRTCSessionsGet)
group.POST("/webrtcsessions/kick/:id", a.onWebRTCSessionsKick)
}

if !interfaceIsEmpty(a.SRTServer) {
group.GET("/v3/srtconns/list", a.onSRTConnsList)
group.GET("/v3/srtconns/get/:id", a.onSRTConnsGet)
group.POST("/v3/srtconns/kick/:id", a.onSRTConnsKick)
group.GET("/srtconns/list", a.onSRTConnsList)
group.GET("/srtconns/get/:id", a.onSRTConnsGet)
group.POST("/srtconns/kick/:id", a.onSRTConnsKick)
}

group.GET("/v3/recordings/list", a.onRecordingsList)
group.GET("/v3/recordings/get/*name", a.onRecordingsGet)
group.DELETE("/v3/recordings/deletesegment", a.onRecordingDeleteSegment)
group.GET("/recordings/list", a.onRecordingsList)
group.GET("/recordings/get/*name", a.onRecordingsGet)
group.DELETE("/recordings/deletesegment", a.onRecordingDeleteSegment)

network, address := restrictnetwork.Restrict("tcp", a.Address)

a.httpServer = &httpp.WrappedServer{
a.httpServer = &httpp.Server{
Network: network,
Address: address,
ReadTimeout: time.Duration(a.ReadTimeout),
Expand Down Expand Up @@ -270,14 +272,14 @@ func (a *API) writeError(ctx *gin.Context, status int, err error) {
}

func (a *API) middlewareOrigin(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", a.AllowOrigin)
ctx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
ctx.Header("Access-Control-Allow-Origin", a.AllowOrigin)
ctx.Header("Access-Control-Allow-Credentials", "true")

// preflight requests
if ctx.Request.Method == http.MethodOptions &&
ctx.Request.Header.Get("Access-Control-Request-Method") != "" {
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH, DELETE")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
ctx.Header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PATCH, DELETE")
ctx.Header("Access-Control-Allow-Headers", "Authorization, Content-Type")
ctx.AbortWithStatus(http.StatusNoContent)
return
}
Expand Down
2 changes: 1 addition & 1 deletion internal/conf/conf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestConfFromFile(t *testing.T) {
RPICameraTextOverlay: "%Y-%m-%d %H:%M:%S - MediaMTX",
RPICameraCodec: "auto",
RPICameraIDRPeriod: 60,
RPICameraBitrate: 1000000,
RPICameraBitrate: 5000000,
RPICameraProfile: "main",
RPICameraLevel: "4.1",
RunOnDemandStartTimeout: 5 * StringDuration(time.Second),
Expand Down
14 changes: 2 additions & 12 deletions internal/conf/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (pconf *Path) setDefaults() {
pconf.RPICameraTextOverlay = "%Y-%m-%d %H:%M:%S - MediaMTX"
pconf.RPICameraCodec = "auto"
pconf.RPICameraIDRPeriod = 60
pconf.RPICameraBitrate = 1000000
pconf.RPICameraBitrate = 5000000
pconf.RPICameraProfile = "main"
pconf.RPICameraLevel = "4.1"

Expand Down Expand Up @@ -578,17 +578,7 @@ func (pconf *Path) Equal(other *Path) bool {

// HasStaticSource checks whether the path has a static source.
func (pconf Path) HasStaticSource() bool {
return strings.HasPrefix(pconf.Source, "rtsp://") ||
strings.HasPrefix(pconf.Source, "rtsps://") ||
strings.HasPrefix(pconf.Source, "rtmp://") ||
strings.HasPrefix(pconf.Source, "rtmps://") ||
strings.HasPrefix(pconf.Source, "http://") ||
strings.HasPrefix(pconf.Source, "https://") ||
strings.HasPrefix(pconf.Source, "udp://") ||
strings.HasPrefix(pconf.Source, "srt://") ||
strings.HasPrefix(pconf.Source, "whep://") ||
strings.HasPrefix(pconf.Source, "wheps://") ||
pconf.Source == "rpiCamera"
return pconf.Source != "publisher" && pconf.Source != "redirect"
}

// HasOnDemandStaticSource checks whether the path has a on demand static source.
Expand Down
2 changes: 1 addition & 1 deletion internal/core/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ func TestAPIProtocolKick(t *testing.T) {
} `json:"items"`
}
httpRequest(t, hc, http.MethodGet, "http://localhost:9997/v3/"+pa+"/list", nil, &out2)
require.Equal(t, 0, len(out2.Items))
require.Empty(t, out2.Items)
})
}
}
Expand Down
Loading

0 comments on commit 0b2679f

Please sign in to comment.