mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-10-05 15:06:51 +08:00
provides cancelation when a component stop responding it closes all resources
This commit is contained in:
13
FAQ.md
13
FAQ.md
@@ -1,5 +1,18 @@
|
||||
# FAQ
|
||||
|
||||
## I can't connect two tabs or browser at the same for the SRT
|
||||
|
||||
It doesn't work When I try to connect in another browser or tab, or even when I try to refresh the current page. It raises an seemingly timeout error.
|
||||
|
||||
```
|
||||
astisrt: connecting failed: astisrt: connecting failed: astisrt: Connection setup failure: connection timed out
|
||||
```
|
||||
|
||||
Apparently both `ffmpeg` and `srt-live-transmit` won't allow multiple persistent connections.
|
||||
|
||||
ref1 https://github.com/Haivision/srt/blob/master/docs/apps/srt-live-transmit.md#medium-srt
|
||||
ref2 https://github.com/asticode/go-astisrt/issues/6#issuecomment-1917076767
|
||||
|
||||
## It's not working on Firefox/Chrome/Edge.
|
||||
|
||||
[WebRTC establishes a baseline set of codecs which all compliant browsers are required to support. Some browsers may choose to allow other codecs as well.](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#supported_video_codecs)
|
||||
|
@@ -51,18 +51,15 @@ func NewSRTController(c *entities.Config, l *zap.Logger, lc fx.Lifecycle) (*SRTC
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *SRTController) Connect(params *entities.RequestParams) (*astisrt.Connection, error) {
|
||||
func (c *SRTController) Connect(cancel context.CancelFunc, params entities.RequestParams) (*astisrt.Connection, error) {
|
||||
c.l.Sugar().Infow("trying to connect srt")
|
||||
if params == nil {
|
||||
return nil, entities.ErrMissingRemoteOffer
|
||||
}
|
||||
|
||||
if err := params.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.l.Sugar().Infow("Connecting to SRT ",
|
||||
"offer", params,
|
||||
"offer", params.String(),
|
||||
)
|
||||
|
||||
conn, err := astisrt.Dial(astisrt.DialOptions{
|
||||
@@ -74,9 +71,10 @@ func (c *SRTController) Connect(params *entities.RequestParams) (*astisrt.Connec
|
||||
},
|
||||
|
||||
OnDisconnect: func(conn *astisrt.Connection, err error) {
|
||||
c.l.Sugar().Fatalw("Disconnected from SRT",
|
||||
c.l.Sugar().Infow("Canceling SRT",
|
||||
"error", err,
|
||||
)
|
||||
cancel()
|
||||
},
|
||||
|
||||
Host: params.SRTHost,
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -28,59 +27,69 @@ func NewStreamingController(c *entities.Config, l *zap.Logger) *StreamingControl
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StreamingController) Stream(srtConnection *astisrt.Connection, videoTrack *webrtc.TrackLocalStaticSample, metadataTrack *webrtc.DataChannel) {
|
||||
func (c *StreamingController) Stream(sp entities.StreamParameters) {
|
||||
r, w := io.Pipe()
|
||||
|
||||
defer r.Close()
|
||||
defer w.Close()
|
||||
defer srtConnection.Close()
|
||||
defer sp.SRTConnection.Close()
|
||||
defer sp.WebRTCConn.Close()
|
||||
defer sp.Cancel()
|
||||
|
||||
c.l.Sugar().Infow("start streaming")
|
||||
|
||||
// TODO: pick the proper transport? is it possible to get rtp instead?
|
||||
go c.readFromSRTIntoWriterPipe(srtConnection, w)
|
||||
go c.readFromSRTIntoWriterPipe(sp.SRTConnection, w)
|
||||
|
||||
dmx := astits.NewDemuxer(context.Background(), r)
|
||||
// reading from reader pipe into mpeg-ts demuxer
|
||||
dmx := astits.NewDemuxer(sp.Ctx, r)
|
||||
eia608Reader := eia608.NewEIA608Reader()
|
||||
h264PID := uint16(0)
|
||||
|
||||
// reading from reader pipe
|
||||
for {
|
||||
d, err := dmx.NextData()
|
||||
if err != nil {
|
||||
c.l.Sugar().Errorw("failed to demux mpeg ts",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
if d.PMT != nil {
|
||||
h264PID = c.captureMediaInfoAndSendToWebRTC(d, metadataTrack, h264PID)
|
||||
c.captureBitrateAndSendToWebRTC(d, metadataTrack)
|
||||
}
|
||||
|
||||
if d.PID == h264PID && d.PES != nil {
|
||||
if err = videoTrack.WriteSample(media.Sample{Data: d.PES.Data, Duration: time.Second / 30}); err != nil {
|
||||
c.l.Sugar().Errorw("failed to write a sample mpeg ts to web rtc",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
captions, err := eia608Reader.Parse(d.PES)
|
||||
select {
|
||||
case <-sp.Ctx.Done():
|
||||
c.l.Sugar().Errorw("stream was cancelled")
|
||||
return
|
||||
default:
|
||||
d, err := dmx.NextData()
|
||||
if err != nil {
|
||||
c.l.Sugar().Errorw("failed to parse eia 608",
|
||||
c.l.Sugar().Errorw("failed to demux mpeg ts",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
if captions != "" {
|
||||
captionsMsg, err := eia608.BuildCaptionsMessage(d.PES.Header.OptionalHeader.PTS, captions)
|
||||
if err != nil {
|
||||
c.l.Sugar().Errorw("failed to build captions message",
|
||||
|
||||
if d.PMT != nil {
|
||||
h264PID = c.captureMediaInfoAndSendToWebRTC(d, sp.MetadataTrack, h264PID)
|
||||
c.captureBitrateAndSendToWebRTC(d, sp.MetadataTrack)
|
||||
}
|
||||
|
||||
if d.PID == h264PID && d.PES != nil {
|
||||
// writing video from mpeg-ts into webrtc
|
||||
if err = sp.VideoTrack.WriteSample(media.Sample{Data: d.PES.Data, Duration: time.Second / 30}); err != nil {
|
||||
c.l.Sugar().Errorw("failed to write a sample mpeg ts to web rtc",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
metadataTrack.SendText(captionsMsg)
|
||||
captions, err := eia608Reader.Parse(d.PES)
|
||||
if err != nil {
|
||||
c.l.Sugar().Errorw("failed to parse eia 608",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
if captions != "" {
|
||||
captionsMsg, err := eia608.BuildCaptionsMessage(d.PES.Header.OptionalHeader.PTS, captions)
|
||||
if err != nil {
|
||||
c.l.Sugar().Errorw("failed to build captions message",
|
||||
"error", err,
|
||||
)
|
||||
break
|
||||
}
|
||||
sp.MetadataTrack.SendText(captionsMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/flavioribeiro/donut/internal/entities"
|
||||
@@ -27,7 +28,7 @@ func NewWebRTCController(
|
||||
}
|
||||
}
|
||||
|
||||
func (c *WebRTCController) CreatePeerConnection() (*webrtc.PeerConnection, error) {
|
||||
func (c *WebRTCController) CreatePeerConnection(cancel context.CancelFunc) (*webrtc.PeerConnection, error) {
|
||||
c.l.Sugar().Infow("trying to set up web rtc conn")
|
||||
|
||||
peerConnectionConfiguration := webrtc.Configuration{}
|
||||
@@ -48,6 +49,18 @@ func (c *WebRTCController) CreatePeerConnection() (*webrtc.PeerConnection, error
|
||||
}
|
||||
|
||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||
finished := connectionState == webrtc.ICEConnectionStateClosed ||
|
||||
connectionState == webrtc.ICEConnectionStateDisconnected ||
|
||||
connectionState == webrtc.ICEConnectionStateCompleted ||
|
||||
connectionState == webrtc.ICEConnectionStateFailed
|
||||
|
||||
if finished {
|
||||
c.l.Sugar().Infow("Canceling webrtc",
|
||||
"status", connectionState.String(),
|
||||
)
|
||||
cancel()
|
||||
}
|
||||
|
||||
c.l.Sugar().Infow("OnICEConnectionStateChange",
|
||||
"status", connectionState.String(),
|
||||
)
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
astisrt "github.com/asticode/go-astisrt/pkg"
|
||||
"github.com/pion/webrtc/v3"
|
||||
)
|
||||
|
||||
@@ -67,6 +69,15 @@ type Track struct {
|
||||
Type TrackType
|
||||
}
|
||||
|
||||
type StreamParameters struct {
|
||||
WebRTCConn *webrtc.PeerConnection
|
||||
Cancel context.CancelFunc
|
||||
Ctx context.Context
|
||||
SRTConnection *astisrt.Connection
|
||||
VideoTrack *webrtc.TrackLocalStaticSample
|
||||
MetadataTrack *webrtc.DataChannel
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
HTTPPort int32 `required:"true" default:"8080"`
|
||||
HTTPHost string `required:"true" default:"0.0.0.0"`
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
@@ -53,7 +54,9 @@ func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) err
|
||||
return err
|
||||
}
|
||||
|
||||
peer, err := h.webRTCController.CreatePeerConnection()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
peer, err := h.webRTCController.CreatePeerConnection(cancel)
|
||||
if err != nil {
|
||||
h.l.Sugar().Errorw("error while setting up web rtc connection",
|
||||
"error", err,
|
||||
@@ -99,7 +102,7 @@ func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) err
|
||||
return err
|
||||
}
|
||||
|
||||
srtConnection, err := h.srtController.Connect(¶ms)
|
||||
srtConnection, err := h.srtController.Connect(cancel, params)
|
||||
if err != nil {
|
||||
h.l.Sugar().Errorw("error while connecting to an srt server",
|
||||
"error", err,
|
||||
@@ -107,7 +110,14 @@ func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) err
|
||||
return err
|
||||
}
|
||||
|
||||
go h.streamingController.Stream(srtConnection, videoTrack, metadataSender)
|
||||
go h.streamingController.Stream(entities.StreamParameters{
|
||||
Cancel: cancel,
|
||||
Ctx: ctx,
|
||||
WebRTCConn: peer,
|
||||
SRTConnection: srtConnection,
|
||||
VideoTrack: videoTrack,
|
||||
MetadataTrack: metadataSender,
|
||||
})
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
Reference in New Issue
Block a user