mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-09-26 19:11:11 +08:00
fix build with race and add faq
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
# FFmpeg/libAV is fixed on version 5.1.2 because go-astiav binding supports it.
|
||||
# see https://github.com/asticode/go-astiav/issues/27
|
||||
FROM --platform=linux/amd64 jrottenberg/ffmpeg:5.1.2-ubuntu2004 AS base
|
||||
FROM golang:1.19
|
||||
FROM golang:1.22
|
||||
|
||||
# TODO: copy only required files
|
||||
COPY --from=base / /
|
||||
@@ -48,5 +48,5 @@ ENV CGO_LDFLAGS="-L${SRT_FOLDER}/lib ${CGO_LDFLAGS}"
|
||||
|
||||
COPY . ./donut
|
||||
WORKDIR ${WD}/donut
|
||||
RUN go build -race .
|
||||
RUN go build .
|
||||
CMD ["/usr/src/app/donut/donut", "--enable-ice-mux=true"]
|
@@ -11,7 +11,7 @@
|
||||
# FFmpeg/libAV is fixed on version 5.1.2 because go-astiav binding supports it.
|
||||
# see https://github.com/asticode/go-astiav/issues/27
|
||||
FROM --platform=linux/amd64 jrottenberg/ffmpeg:5.1.2-ubuntu2004 AS base
|
||||
FROM golang:1.19
|
||||
FROM golang:1.22
|
||||
|
||||
# TODO: copy only required files
|
||||
COPY --from=base / /
|
||||
|
55
FAQ.md
55
FAQ.md
@@ -83,3 +83,58 @@ docker-compose.yml
|
||||
```yaml
|
||||
platform: "linux/amd64"
|
||||
```
|
||||
|
||||
## If you're seeing the error "checkptr: converted pointer straddles multiple allocations" when using -race
|
||||
|
||||
When the app runs using `go build -race` it stops with the error "converted pointer straddles multiple allocations". I tried to upgrade the golang image but it didn't work, so I remove the `-race` from building.
|
||||
|
||||
```
|
||||
srt-1 | connected.
|
||||
srt-1 | Accepted SRT target connection
|
||||
app-1 | fatal error: checkptr: converted pointer straddles multiple allocations
|
||||
app-1 |
|
||||
app-1 | goroutine 68 [running]:
|
||||
app-1 | runtime.throw({0xe57eb2?, 0xc00003f17c?})
|
||||
app-1 | /usr/local/go/src/runtime/panic.go:1047 +0x5d fp=0xc0001d7700 sp=0xc0001d76d0 pc=0x44febd
|
||||
app-1 | runtime.checkptrAlignment(0xc00031840d?, 0x3?, 0x7ffffa869c74?)
|
||||
app-1 | /usr/local/go/src/runtime/checkptr.go:26 +0x6c fp=0xc0001d7720 sp=0xc0001d7700 pc=0x41eacc
|
||||
app-1 | github.com/asticode/go-astisrt/pkg.(*Socket).Connect(0xc00003e150, {0xc00031840d, 0x3}, 0xc0b0?)
|
||||
app-1 | /go/pkg/mod/github.com/asticode/go-astisrt@v0.3.0/pkg/socket.go:85 +0x245 fp=0xc0001d77a0 sp=0xc0001d7720 pc=0xc58ce5
|
||||
app-1 | github.com/asticode/go-astisrt/pkg.Dial({{0xc000232ba0, 0x4, 0x4}, {0xc00031840d, 0x3}, 0xc0001c4060, 0x9c74})
|
||||
app-1 | /go/pkg/mod/github.com/asticode/go-astisrt@v0.3.0/pkg/client.go:53 +0x445 fp=0xc0001d78d8 sp=0xc0001d77a0 pc=0xc55925
|
||||
app-1 | github.com/flavioribeiro/donut/internal/controllers/streamers.(*SRTMpegTSStreamer).connect(0xc0002aae40, 0xc0000f4550, 0xc00010c050)
|
||||
app-1 | /usr/src/app/donut/internal/controllers/streamers/srt_mpegts.go:161 +0x819 fp=0xc0001d7b00 sp=0xc0001d78d8 pc=0xc61bf9
|
||||
app-1 | github.com/flavioribeiro/donut/internal/controllers/streamers.(*SRTMpegTSStreamer).Stream(0xc0002aae40, 0xc000100540)
|
||||
app-1 | /usr/src/app/donut/internal/controllers/streamers/srt_mpegts.go:55 +0xa9 fp=0xc0001d7fa8 sp=0xc0001d7b00 pc=0xc5fa29
|
||||
```
|
||||
|
||||
ref https://github.com/golang/go/issues/54690
|
||||
|
||||
## If you're seeing the error "At least one invalid signature was encountered ... GPG error: http://security." when running the app
|
||||
|
||||
If you see the error "At least one invalid signature was encountered." when running `make run`, please run `docker system prune` and try again.
|
||||
|
||||
```
|
||||
3.723 W: GPG error: http://deb.debian.org/debian bookworm InRelease: At least one invalid signature was encountered.
|
||||
3.723 E: The repository 'http://deb.debian.org/debian bookworm InRelease' is not signed.
|
||||
3.723 W: GPG error: http://deb.debian.org/debian bookworm-updates InRelease: At least one invalid signature was encountered.
|
||||
3.723 E: The repository 'http://deb.debian.org/debian bookworm-updates InRelease' is not signed.
|
||||
3.723 W: GPG error: http://deb.debian.org/debian-security bookworm-security InRelease: At least one invalid signature was encountered.
|
||||
3.723 E: The repository 'http://deb.debian.org/debian-security bookworm-security InRelease' is not signed.
|
||||
3.723 W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://archive.ubuntu.com/ubuntu focal InRelease: At least one invalid signature was encountered.
|
||||
3.723 W: An error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://security.ubuntu.com/ubuntu focal-security InRelease: At least one invalid signature was encountered.
|
||||
```
|
||||
|
||||
## If you're seeing the error "failed to copy files: userspace copy failed: write" when running the app
|
||||
|
||||
If you see the error "failed to copy files: userspace copy failed: write" when running `make run`, please run `docker system prune` and try again.
|
||||
|
||||
```
|
||||
=> CANCELED [test stage-1 6/6] RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2 4.4s
|
||||
=> ERROR [app stage-1 6/8] COPY . ./donut 4.1s
|
||||
------
|
||||
> [app stage-1 6/8] COPY . ./donut:
|
||||
------
|
||||
failed to solve: failed to copy files: userspace copy failed: write /var/lib/docker/overlay2/30zm6uywrtfed4z4wfzbf1ema/merged/usr/src/app/donut/tmp/n5.1.2/src/tests/reference.pnm: no space left on device
|
||||
make: *** [run] Error 17
|
||||
```
|
6
Makefile
6
Makefile
@@ -1,8 +1,10 @@
|
||||
run:
|
||||
docker compose stop && docker compose down && docker compose build && docker compose up origin srt app
|
||||
# in case you need to re-build it, uncomment the following line
|
||||
# docker compose stop && docker compose down && docker compose build && docker compose up origin srt app
|
||||
docker compose stop && docker compose build app && docker compose up origin srt app
|
||||
|
||||
test:
|
||||
# in case you need to re-build it
|
||||
# in case you need to re-build it, uncomment the following line
|
||||
# docker compose stop test && docker compose down test && docker compose build test && docker compose run --rm test
|
||||
docker compose stop test && docker compose down test && docker compose run --rm test
|
||||
|
||||
|
@@ -39,13 +39,10 @@ func NewLibAVFFmpeg(
|
||||
|
||||
// Match returns true when the request is for an LibAVFFmpeg prober
|
||||
func (c *LibAVFFmpeg) Match(req *entities.RequestParams) bool {
|
||||
if req.SRTHost != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return req.SRTHost != ""
|
||||
}
|
||||
|
||||
// StreamInfo connects to the SRT stream and probe N packets to discovery the media properties.
|
||||
// StreamInfo connects to the SRT stream to discovery media properties.
|
||||
func (c *LibAVFFmpeg) StreamInfo(req *entities.RequestParams) (*entities.StreamInfo, error) {
|
||||
closer := astikit.NewCloser()
|
||||
defer closer.Close()
|
||||
@@ -67,10 +64,18 @@ func (c *LibAVFFmpeg) StreamInfo(req *entities.RequestParams) (*entities.StreamI
|
||||
return nil, fmt.Errorf("mpegts: %w", entities.ErrFFmpegLibAVNotFound)
|
||||
}
|
||||
|
||||
inputURL := fmt.Sprintf("srt://%s:%d/%s", req.SRTHost, req.SRTPort, req.SRTStreamID)
|
||||
if err := inputFormatContext.OpenInput(inputURL, inputFormat, nil); err != nil {
|
||||
// ref https://ffmpeg.org/ffmpeg-all.html#srt
|
||||
d := &astiav.Dictionary{}
|
||||
// flags (the zeroed 3rd value) https://github.com/FFmpeg/FFmpeg/blob/n5.0/libavutil/dict.h#L67C9-L77
|
||||
d.Set("srt_streamid", req.SRTStreamID, 0)
|
||||
d.Set("smoother", "live", 0)
|
||||
d.Set("transtype", "live", 0)
|
||||
|
||||
inputURL := fmt.Sprintf("srt://%s:%d", req.SRTHost, req.SRTPort)
|
||||
if err := inputFormatContext.OpenInput(inputURL, inputFormat, d); err != nil {
|
||||
return nil, fmt.Errorf("error while inputFormatContext.OpenInput: %w", err)
|
||||
}
|
||||
closer.Add(inputFormatContext.CloseInput)
|
||||
|
||||
if err := inputFormatContext.FindStreamInfo(nil); err != nil {
|
||||
return nil, fmt.Errorf("error while inputFormatContext.FindStreamInfo %w", err)
|
||||
@@ -80,9 +85,9 @@ func (c *LibAVFFmpeg) StreamInfo(req *entities.RequestParams) (*entities.StreamI
|
||||
for _, is := range inputFormatContext.Streams() {
|
||||
if is.CodecParameters().MediaType() != astiav.MediaTypeAudio &&
|
||||
is.CodecParameters().MediaType() != astiav.MediaTypeVideo {
|
||||
c.l.Info("skipping media type", is.CodecParameters().MediaType())
|
||||
continue
|
||||
}
|
||||
|
||||
streams = append(streams, c.m.FromLibAVStreamToEntityStream(is))
|
||||
}
|
||||
si := entities.StreamInfo{Streams: streams}
|
||||
|
@@ -34,7 +34,7 @@ func selectProberFor(t *testing.T, req *entities.RequestParams) probers.DonutPro
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSrtMpegTs_StreamInfo(t *testing.T) {
|
||||
func TestSrtMpegTs_StreamInfo_264(t *testing.T) {
|
||||
t.Parallel()
|
||||
ffmpeg := teststreaming.FFMPEG_LIVE_SRT_MPEG_TS_H264_AAC
|
||||
|
@@ -1,195 +0,0 @@
|
||||
package probers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
astisrt "github.com/asticode/go-astisrt/pkg"
|
||||
"github.com/asticode/go-astits"
|
||||
"github.com/flavioribeiro/donut/internal/entities"
|
||||
"github.com/flavioribeiro/donut/internal/mapper"
|
||||
"go.uber.org/fx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SrtMpegTs struct {
|
||||
c *entities.Config
|
||||
l *zap.SugaredLogger
|
||||
m *mapper.Mapper
|
||||
}
|
||||
|
||||
type ResultSrtMpegTs struct {
|
||||
fx.Out
|
||||
SrtMpegTsProber DonutProber `group:"probers"`
|
||||
}
|
||||
|
||||
// NewSrtMpegTs creates a new SrtMpegTs DonutProber
|
||||
func NewSrtMpegTs(
|
||||
c *entities.Config,
|
||||
l *zap.SugaredLogger,
|
||||
m *mapper.Mapper,
|
||||
) ResultSrtMpegTs {
|
||||
return ResultSrtMpegTs{
|
||||
SrtMpegTsProber: &SrtMpegTs{
|
||||
c: c,
|
||||
l: l,
|
||||
m: m,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Match returns true when the request is for an SrtMpegTs prober
|
||||
func (c *SrtMpegTs) Match(req *entities.RequestParams) bool {
|
||||
if req.SRTHost != "" {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// StreamInfo connects to the SRT stream and probe N packets to discovery the media properties.
|
||||
func (c *SrtMpegTs) StreamInfo(req *entities.RequestParams) (*entities.StreamInfo, error) {
|
||||
streamInfoMap, err := c.streamInfoMap(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
si := &entities.StreamInfo{}
|
||||
for _, v := range streamInfoMap {
|
||||
si.Streams = append(si.Streams, v)
|
||||
}
|
||||
return si, err
|
||||
}
|
||||
|
||||
func (c *SrtMpegTs) streamInfoMap(req *entities.RequestParams) (map[entities.Codec]entities.Stream, error) {
|
||||
r, w := io.Pipe()
|
||||
defer r.Close()
|
||||
defer w.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
srtConnection, err := c.connect(cancel, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer srtConnection.Close()
|
||||
|
||||
streamInfoMap := map[entities.Codec]entities.Stream{}
|
||||
|
||||
go c.fromSRTToWriterPipe(srtConnection, w, cancel)
|
||||
|
||||
c.l.Info("probing has starting demuxing")
|
||||
|
||||
mpegTSDemuxer := astits.NewDemuxer(ctx, r)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if errors.Is(ctx.Err(), context.Canceled) {
|
||||
c.l.Infow("probing has stopped due cancellation")
|
||||
return streamInfoMap, nil
|
||||
}
|
||||
c.l.Errorw("probing has stopped due errors")
|
||||
return streamInfoMap, ctx.Err()
|
||||
default:
|
||||
stop, err := c.fillStreamInfoFromMpegTS(streamInfoMap, mpegTSDemuxer)
|
||||
if stop {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return streamInfoMap, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SrtMpegTs) fromSRTToWriterPipe(srtConnection *astisrt.Connection, w *io.PipeWriter, cancel context.CancelFunc) {
|
||||
defer cancel()
|
||||
defer w.Close()
|
||||
defer srtConnection.Close()
|
||||
|
||||
inboundMpegTsPacket := make([]byte, c.c.SRTReadBufferSizeBytes)
|
||||
c.l.Info("probing has started")
|
||||
|
||||
for i := 1; i < c.c.ProbingSize; i++ {
|
||||
n, err := srtConnection.Read(inboundMpegTsPacket)
|
||||
if err != nil {
|
||||
c.l.Errorw("str conn failed to write data to buffer",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
if _, err := w.Write(inboundMpegTsPacket[:n]); err != nil {
|
||||
c.l.Errorw("failed to write mpeg-ts into the pipe",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
c.l.Info("done probing")
|
||||
}
|
||||
|
||||
func (c *SrtMpegTs) fillStreamInfoFromMpegTS(streamInfo map[entities.Codec]entities.Stream, mpegTSDemuxer *astits.Demuxer) (bool, error) {
|
||||
mpegTSDemuxData, err := mpegTSDemuxer.NextData()
|
||||
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
c.l.Errorw("failed to demux mpeg-ts",
|
||||
"error", err,
|
||||
)
|
||||
return true, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if mpegTSDemuxData.PMT != nil {
|
||||
// TODO: add timing information
|
||||
// pts https://github.com/asticode/go-astits/blob/b0b19247aa31633650c32638fb55f597fa6e2468/cmd/astits-es-split/main.go#L206
|
||||
// https://github.com/asticode/go-astits/blob/master/packet.go#L46
|
||||
for _, es := range mpegTSDemuxData.PMT.ElementaryStreams {
|
||||
streamInfo[c.m.FromMpegTsStreamTypeToCodec(es.StreamType)] = c.m.FromStreamTypeToEntityStream(es)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// TODO: move to its own component later dup streamer.srt_mpegts, prober.srt_mpegts
|
||||
func (c *SrtMpegTs) connect(cancel context.CancelFunc, params *entities.RequestParams) (*astisrt.Connection, error) {
|
||||
c.l.Info("trying to connect srt")
|
||||
|
||||
if err := params.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.l.Infow("Connecting to SRT ",
|
||||
"offer", params.String(),
|
||||
)
|
||||
|
||||
conn, err := astisrt.Dial(astisrt.DialOptions{
|
||||
ConnectionOptions: []astisrt.ConnectionOption{
|
||||
astisrt.WithLatency(c.c.SRTConnectionLatencyMS),
|
||||
astisrt.WithStreamid(params.SRTStreamID),
|
||||
astisrt.WithCongestion("live"),
|
||||
astisrt.WithTranstype(astisrt.Transtype(astisrt.TranstypeLive)),
|
||||
},
|
||||
|
||||
OnDisconnect: func(conn *astisrt.Connection, err error) {
|
||||
c.l.Infow("Canceling SRT",
|
||||
"error", err,
|
||||
)
|
||||
cancel()
|
||||
},
|
||||
|
||||
Host: params.SRTHost,
|
||||
Port: params.SRTPort,
|
||||
})
|
||||
if err != nil {
|
||||
c.l.Errorw("failed to connect srt",
|
||||
"error", err,
|
||||
)
|
||||
return nil, err
|
||||
}
|
||||
c.l.Infow("Connected to SRT")
|
||||
return conn, nil
|
||||
}
|
@@ -45,7 +45,6 @@ func Dependencies(enableICEMux bool) fx.Option {
|
||||
fx.Provide(controllers.NewWebRTCMediaEngine),
|
||||
fx.Provide(controllers.NewWebRTCAPI),
|
||||
fx.Provide(streamers.NewSRTMpegTSStreamer),
|
||||
fx.Provide(probers.NewSrtMpegTs),
|
||||
fx.Provide(probers.NewLibAVFFmpeg),
|
||||
|
||||
fx.Provide(engine.NewDonutEngineController),
|
||||
|
Reference in New Issue
Block a user