mirror of
https://github.com/hsnks100/liveflow.git
synced 2025-09-26 20:21:12 +08:00
support off the record mp4, mkv (#10)
This commit is contained in:
@@ -72,6 +72,7 @@ Start streaming by choosing from the following broadcast options:
|
||||
|
||||
- **HLS:**
|
||||
- URL: `http://127.0.0.1:8044/hls/test/master.m3u8`
|
||||
- Viewer: `http://127.0.0.1:8044/m3u8player.html?streamid=test`
|
||||
|
||||
- **WHEP:**
|
||||
- URL: `http://127.0.0.1:8044/`
|
||||
|
@@ -6,3 +6,7 @@ llhls = false
|
||||
disk_ram = true
|
||||
[docker]
|
||||
mode = false
|
||||
[mp4]
|
||||
record=false
|
||||
[ebml]
|
||||
record=false
|
@@ -5,6 +5,8 @@ type Config struct {
|
||||
RTMP RTMP `mapstructure:"rtmp"`
|
||||
Service Service `mapstructure:"service"`
|
||||
Docker DockerConfig `mapstructure:"docker"`
|
||||
MP4 MP4 `mapstructure:"mp4"`
|
||||
EBML EBML `mapstructure:"ebml"`
|
||||
}
|
||||
|
||||
type RTMP struct {
|
||||
@@ -20,3 +22,11 @@ type Service struct {
|
||||
type DockerConfig struct {
|
||||
Mode bool `mapstructure:"mode"`
|
||||
}
|
||||
|
||||
type MP4 struct {
|
||||
Record bool `mapstructure:"record"`
|
||||
}
|
||||
|
||||
type EBML struct {
|
||||
Record bool `mapstructure:"record"`
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@@ -4,6 +4,7 @@ go 1.21
|
||||
|
||||
require (
|
||||
github.com/asticode/go-astiav v0.19.0
|
||||
github.com/asticode/go-astits v1.13.0
|
||||
github.com/at-wat/ebml-go v0.17.1
|
||||
github.com/bluenviron/gohlslib v1.4.0
|
||||
github.com/deepch/vdk v0.0.27
|
||||
@@ -25,12 +26,12 @@ require (
|
||||
require (
|
||||
github.com/abema/go-mp4 v1.2.0 // indirect
|
||||
github.com/asticode/go-astikit v0.43.0 // indirect
|
||||
github.com/asticode/go-astits v1.13.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bluenviron/mediacommon v1.11.1-0.20240525122142-20163863aa75 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -77,6 +78,7 @@ require (
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
4
go.sum
4
go.sum
@@ -30,6 +30,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -248,6 +250,8 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
43
main.go
43
main.go
@@ -10,11 +10,11 @@ import (
|
||||
"liveflow/media/streamer/egress/whep"
|
||||
"liveflow/media/streamer/ingress/whip"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // pprof을 사용하기 위한 패키지
|
||||
"strconv"
|
||||
|
||||
_ "net/http/pprof" // pprof을 사용하기 위한 패키지
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/pion/webrtc/v3"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -63,11 +63,16 @@ func main() {
|
||||
api.HideBanner = true
|
||||
hlsHub := hlshub.NewHLSHub()
|
||||
hlsHandler := httpsrv.NewHandler(hlsHub)
|
||||
hlsRoute := api.Group("/hls", middleware.CORSWithConfig(middleware.CORSConfig{
|
||||
AllowOrigins: []string{"*"}, // Adjust origins as necessary
|
||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodOptions},
|
||||
}))
|
||||
api.GET("/prometheus", echo.WrapHandler(promhttp.Handler()))
|
||||
api.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux))
|
||||
api.GET("/hls/:streamID/master.m3u8", hlsHandler.HandleMasterM3U8)
|
||||
api.GET("/hls/:streamID/:playlistName/stream.m3u8", hlsHandler.HandleM3U8)
|
||||
api.GET("/hls/:streamID/:playlistName/:resourceName", hlsHandler.HandleM3U8)
|
||||
// Enable CORS only for /hls routes
|
||||
hlsRoute.GET("/:streamID/master.m3u8", hlsHandler.HandleMasterM3U8)
|
||||
hlsRoute.GET("/:streamID/:playlistName/stream.m3u8", hlsHandler.HandleM3U8)
|
||||
hlsRoute.GET("/:streamID/:playlistName/:resourceName", hlsHandler.HandleM3U8)
|
||||
whipServer := whip.NewWHIP(whip.WHIPArgs{
|
||||
Hub: hub,
|
||||
Tracks: tracks,
|
||||
@@ -82,19 +87,23 @@ func main() {
|
||||
// ingress 의 rtmp, whip 서비스로부터 streamID를 받아 Service, ContainerMP4, WHEP 서비스 시작
|
||||
for source := range hub.SubscribeToStreamID() {
|
||||
log.Infof(ctx, "New streamID received: %s", source.StreamID())
|
||||
mp4 := mp4.NewMP4(mp4.MP4Args{
|
||||
Hub: hub,
|
||||
})
|
||||
err = mp4.Start(ctx, source)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "failed to start mp4: %v", err)
|
||||
if conf.MP4.Record {
|
||||
mp4 := mp4.NewMP4(mp4.MP4Args{
|
||||
Hub: hub,
|
||||
})
|
||||
err = mp4.Start(ctx, source)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "failed to start mp4: %v", err)
|
||||
}
|
||||
}
|
||||
webmStarter := webm.NewWEBM(webm.WebMArgs{
|
||||
Hub: hub,
|
||||
})
|
||||
err = webmStarter.Start(ctx, source)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "failed to start webm: %v", err)
|
||||
if conf.EBML.Record {
|
||||
webmStarter := webm.NewWEBM(webm.WebMArgs{
|
||||
Hub: hub,
|
||||
})
|
||||
err = webmStarter.Start(ctx, source)
|
||||
if err != nil {
|
||||
log.Errorf(ctx, "failed to start webm: %v", err)
|
||||
}
|
||||
}
|
||||
hls := hls.NewHLS(hls.HLSArgs{
|
||||
Hub: hub,
|
||||
|
@@ -70,7 +70,8 @@ func (h *HLS) Start(ctx context.Context, source hub.Source) error {
|
||||
fields.SourceName: source.Name(),
|
||||
})
|
||||
log.Info(ctx, "start hls")
|
||||
log.Info(ctx, "view url: ", fmt.Sprintf("http://127.0.0.1:%d/hls/%s/master.m3u8", h.port, source.StreamID()))
|
||||
log.Info(ctx, "view url: ",
|
||||
fmt.Sprintf("http://localhost:8044/m3u8player.html?streamid=%s", source.StreamID()))
|
||||
|
||||
sub := h.hub.Subscribe(source.StreamID())
|
||||
go func() {
|
||||
|
@@ -56,7 +56,7 @@ func NewWHIP(args WHIPArgs) *WHIP {
|
||||
|
||||
func (r *WHIP) RegisterRoute() {
|
||||
whipServer := r.echo
|
||||
whipServer.Static("/", ".")
|
||||
whipServer.Static("/", "static")
|
||||
whipServer.POST("/whip", r.whipHandler)
|
||||
whipServer.POST("/whep", r.whepHandler)
|
||||
}
|
||||
|
41
static/m3u8player.html
Normal file
41
static/m3u8player.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@mux/mux-video@0"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>M3U8 Player</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<mux-video
|
||||
id="hls-player"
|
||||
prefer-playback="mse"
|
||||
controls
|
||||
autoplay
|
||||
muted
|
||||
>
|
||||
</mux-video>
|
||||
|
||||
<script>
|
||||
// Function to get the query parameters from the URL
|
||||
function getQueryParam(param) {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
return urlParams.get(param);
|
||||
}
|
||||
|
||||
// Get streamid from the URL query parameters
|
||||
const streamId = getQueryParam('streamid');
|
||||
|
||||
if (streamId) {
|
||||
// Update the src dynamically
|
||||
const host = window.location.protocol + '//' + window.location.host;
|
||||
const player = document.getElementById('hls-player');
|
||||
player.src = `${host}/hls/${streamId}/master.m3u8`;
|
||||
} else {
|
||||
console.error('Stream ID not provided in the URL');
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user