move components to plural named packages

This commit is contained in:
Leandro Moreira
2024-01-28 07:05:31 -03:00
parent 852ad6c1ad
commit 74d84b13f2
15 changed files with 157 additions and 156 deletions

View File

@@ -1,4 +1,4 @@
package streaming package controllers
import ( import (
"context" "context"
@@ -10,18 +10,18 @@ import (
astisrt "github.com/asticode/go-astisrt/pkg" astisrt "github.com/asticode/go-astisrt/pkg"
"github.com/asticode/go-astits" "github.com/asticode/go-astits"
"github.com/flavioribeiro/donut/eia608" "github.com/flavioribeiro/donut/eia608"
"github.com/flavioribeiro/donut/internal/entity" "github.com/flavioribeiro/donut/internal/entities"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media" "github.com/pion/webrtc/v3/pkg/media"
"go.uber.org/zap" "go.uber.org/zap"
) )
type StreamingController struct { type StreamingController struct {
c *entity.Config c *entities.Config
l *zap.Logger l *zap.Logger
} }
func NewStreamingController(c *entity.Config, l *zap.Logger) *StreamingController { func NewStreamingController(c *entities.Config, l *zap.Logger) *StreamingController {
return &StreamingController{ return &StreamingController{
c: c, c: c,
l: l, l: l,
@@ -90,8 +90,8 @@ func (*StreamingController) captureBitrateAndSendToWebRTC(d *astits.DemuxerData,
for _, d := range d.PMT.ProgramDescriptors { for _, d := range d.PMT.ProgramDescriptors {
if d.MaximumBitrate != nil { if d.MaximumBitrate != nil {
bitrateInMbitsPerSecond := float32(d.MaximumBitrate.Bitrate) / float32(125000) bitrateInMbitsPerSecond := float32(d.MaximumBitrate.Bitrate) / float32(125000)
msg, _ := json.Marshal(entity.Message{ msg, _ := json.Marshal(entities.Message{
Type: entity.MessageTypeMetadata, Type: entities.MessageTypeMetadata,
Message: fmt.Sprintf("Bitrate %.2fMbps", bitrateInMbitsPerSecond), Message: fmt.Sprintf("Bitrate %.2fMbps", bitrateInMbitsPerSecond),
}) })
metadataTrack.SendText(string(msg)) metadataTrack.SendText(string(msg))
@@ -102,8 +102,8 @@ func (*StreamingController) captureBitrateAndSendToWebRTC(d *astits.DemuxerData,
func (*StreamingController) captureMediaInfoAndSendToWebRTC(d *astits.DemuxerData, metadataTrack *webrtc.DataChannel, h264PID uint16) uint16 { func (*StreamingController) captureMediaInfoAndSendToWebRTC(d *astits.DemuxerData, metadataTrack *webrtc.DataChannel, h264PID uint16) uint16 {
for _, es := range d.PMT.ElementaryStreams { for _, es := range d.PMT.ElementaryStreams {
msg, _ := json.Marshal(entity.Message{ msg, _ := json.Marshal(entities.Message{
Type: entity.MessageTypeMetadata, Type: entities.MessageTypeMetadata,
Message: es.StreamType.String(), Message: es.StreamType.String(),
}) })
metadataTrack.SendText(string(msg)) metadataTrack.SendText(string(msg))

View File

@@ -1,26 +1,26 @@
package srt package controllers
import ( import (
astisrt "github.com/asticode/go-astisrt/pkg" astisrt "github.com/asticode/go-astisrt/pkg"
"github.com/flavioribeiro/donut/internal/entity" "github.com/flavioribeiro/donut/internal/entities"
"go.uber.org/zap" "go.uber.org/zap"
) )
type SRTController struct { type SRTController struct {
c *entity.Config c *entities.Config
l *zap.Logger l *zap.Logger
} }
func NewSRTController(c *entity.Config, l *zap.Logger) *SRTController { func NewSRTController(c *entities.Config, l *zap.Logger) *SRTController {
return &SRTController{ return &SRTController{
c: c, c: c,
l: l, l: l,
} }
} }
func (c *SRTController) Connect(params *entity.RequestParams) (*astisrt.Connection, error) { func (c *SRTController) Connect(params *entities.RequestParams) (*astisrt.Connection, error) {
if params == nil { if params == nil {
return nil, entity.ErrMissingRemoteOffer return nil, entities.ErrMissingRemoteOffer
} }
if err := params.Valid(); err != nil { if err := params.Valid(); err != nil {

View File

@@ -1,16 +1,16 @@
package webrtc package controllers
import ( import (
"net" "net"
"github.com/flavioribeiro/donut/internal/entity" "github.com/flavioribeiro/donut/internal/entities"
"github.com/flavioribeiro/donut/internal/mapper" "github.com/flavioribeiro/donut/internal/mapper"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"go.uber.org/zap" "go.uber.org/zap"
) )
type WebRTCController struct { type WebRTCController struct {
c *entity.Config c *entities.Config
l *zap.Logger l *zap.Logger
iceTcp net.Listener iceTcp net.Listener
iceUdp net.PacketConn iceUdp net.PacketConn
@@ -18,7 +18,7 @@ type WebRTCController struct {
} }
func NewWebRTCController( func NewWebRTCController(
c *entity.Config, c *entities.Config,
l *zap.Logger, l *zap.Logger,
iceTcp net.Listener, iceTcp net.Listener,
iceUdp net.PacketConn, iceUdp net.PacketConn,
@@ -73,7 +73,7 @@ func (c *WebRTCController) SetupPeerConnection() error {
return nil return nil
} }
func (c *WebRTCController) CreateTrack(track entity.Track, id string, streamId string) (*webrtc.TrackLocalStaticSample, error) { func (c *WebRTCController) CreateTrack(track entities.Track, id string, streamId string) (*webrtc.TrackLocalStaticSample, error) {
codecCapability := mapper.FromTrackToRTPCodecCapability(track) codecCapability := mapper.FromTrackToRTPCodecCapability(track)
webRTCtrack, err := webrtc.NewTrackLocalStaticSample(codecCapability, id, streamId) webRTCtrack, err := webrtc.NewTrackLocalStaticSample(codecCapability, id, streamId)
if err != nil { if err != nil {
@@ -89,7 +89,7 @@ func (c *WebRTCController) CreateTrack(track entity.Track, id string, streamId s
func (c *WebRTCController) CreateDataChannel(channelID string) (*webrtc.DataChannel, error) { func (c *WebRTCController) CreateDataChannel(channelID string) (*webrtc.DataChannel, error) {
if c.peer == nil { if c.peer == nil {
// TODO: or call SetupPeerConnection? // TODO: or call SetupPeerConnection?
return nil, entity.ErrMissingWebRTCSetup return nil, entities.ErrMissingWebRTCSetup
} }
metadataSender, err := c.peer.CreateDataChannel(channelID, nil) metadataSender, err := c.peer.CreateDataChannel(channelID, nil)
@@ -102,7 +102,7 @@ func (c *WebRTCController) CreateDataChannel(channelID string) (*webrtc.DataChan
func (c *WebRTCController) SetRemoteDescription(desc webrtc.SessionDescription) error { func (c *WebRTCController) SetRemoteDescription(desc webrtc.SessionDescription) error {
if c.peer == nil { if c.peer == nil {
// TODO: or call SetupPeerConnection? // TODO: or call SetupPeerConnection?
return entity.ErrMissingWebRTCSetup return entities.ErrMissingWebRTCSetup
} }
err := c.peer.SetRemoteDescription(desc) err := c.peer.SetRemoteDescription(desc)
@@ -115,7 +115,7 @@ func (c *WebRTCController) SetRemoteDescription(desc webrtc.SessionDescription)
func (c *WebRTCController) GatheringWebRTC() (*webrtc.SessionDescription, error) { func (c *WebRTCController) GatheringWebRTC() (*webrtc.SessionDescription, error) {
if c.peer == nil { if c.peer == nil {
// TODO: or call SetupPeerConnection? // TODO: or call SetupPeerConnection?
return nil, entity.ErrMissingWebRTCSetup return nil, entities.ErrMissingWebRTCSetup
} }
c.l.Sugar().Infow("Gathering WebRTC Candidates") c.l.Sugar().Infow("Gathering WebRTC Candidates")
@@ -132,7 +132,7 @@ func (c *WebRTCController) GatheringWebRTC() (*webrtc.SessionDescription, error)
return c.peer.LocalDescription(), nil return c.peer.LocalDescription(), nil
} }
func NewWebRTCSettingsEngine(c *entity.Config, tcpListener net.Listener, udpListener net.PacketConn) webrtc.SettingEngine { func NewWebRTCSettingsEngine(c *entities.Config, tcpListener net.Listener, udpListener net.PacketConn) webrtc.SettingEngine {
settingEngine := webrtc.SettingEngine{} settingEngine := webrtc.SettingEngine{}
settingEngine.SetNAT1To1IPs(c.ICEExternalIPsDNAT, webrtc.ICECandidateTypeHost) settingEngine.SetNAT1To1IPs(c.ICEExternalIPsDNAT, webrtc.ICECandidateTypeHost)
@@ -150,7 +150,7 @@ func NewWebRTCMediaEngine() (*webrtc.MediaEngine, error) {
return mediaEngine, nil return mediaEngine, nil
} }
func NewTCPICEServer(c *entity.Config) (net.Listener, error) { func NewTCPICEServer(c *entities.Config) (net.Listener, error) {
tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{ tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{
IP: net.IP{0, 0, 0, 0}, IP: net.IP{0, 0, 0, 0},
Port: c.TCPICEPort, Port: c.TCPICEPort,
@@ -161,8 +161,7 @@ func NewTCPICEServer(c *entity.Config) (net.Listener, error) {
return tcpListener, nil return tcpListener, nil
} }
func NewUDPICEServer(c *entity.Config) (net.PacketConn, error) { func NewUDPICEServer(c *entities.Config) (net.PacketConn, error) {
udpListener, err := net.ListenUDP("udp", &net.UDPAddr{ udpListener, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.IP{0, 0, 0, 0}, IP: net.IP{0, 0, 0, 0},
Port: c.UDPICEPort, Port: c.UDPICEPort,
@@ -170,6 +169,5 @@ func NewUDPICEServer(c *entity.Config) (net.PacketConn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return udpListener, nil return udpListener, nil
} }

View File

@@ -1,4 +1,4 @@
package entity package entities
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package entity package entities
import "errors" import "errors"

View File

@@ -1,14 +1,14 @@
package mapper package mapper
import ( import (
"github.com/flavioribeiro/donut/internal/entity" "github.com/flavioribeiro/donut/internal/entities"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
func FromTrackToRTPCodecCapability(track entity.Track) webrtc.RTPCodecCapability { func FromTrackToRTPCodecCapability(track entities.Track) webrtc.RTPCodecCapability {
response := webrtc.RTPCodecCapability{} response := webrtc.RTPCodecCapability{}
if track.Type == entity.H264 { if track.Type == entities.H264 {
response.MimeType = webrtc.MimeTypeH264 response.MimeType = webrtc.MimeTypeH264
} }

View File

@@ -0,0 +1,12 @@
package handlers
import "net/http"
func SetError(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
func SetSuccessJson(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}

View File

@@ -6,7 +6,7 @@ import (
"net/http" "net/http"
astisrt "github.com/asticode/go-astisrt/pkg" astisrt "github.com/asticode/go-astisrt/pkg"
"github.com/flavioribeiro/donut/internal/entity" "github.com/flavioribeiro/donut/internal/entities"
) )
type MediaHandler struct{} type MediaHandler struct{}
@@ -16,33 +16,32 @@ func NewMediaHandler() *MediaHandler {
} }
func (m *MediaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (m *MediaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
SetCORS(w, r)
if r.Method != http.MethodGet { if r.Method != http.MethodGet {
ErrorToHTTP(w, entity.ErrHTTPGetOnly) SetError(w, entities.ErrHTTPGetOnly)
return return
} }
offer := entity.RequestParams{} params := entities.RequestParams{}
if err := json.NewDecoder(r.Body).Decode(&offer); err != nil { if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
log.Println("Connecting to SRT ", offer) log.Println("Connecting to SRT ", params)
_, err := astisrt.Dial(astisrt.DialOptions{ _, err := astisrt.Dial(astisrt.DialOptions{
ConnectionOptions: []astisrt.ConnectionOption{ ConnectionOptions: []astisrt.ConnectionOption{
astisrt.WithLatency(300), astisrt.WithLatency(300),
astisrt.WithStreamid(offer.SRTStreamID), astisrt.WithStreamid(params.SRTStreamID),
}, },
// Callback when the connection is disconnected // Callback when the connection is disconnected
OnDisconnect: func(c *astisrt.Connection, err error) { log.Fatal("Disconnected from SRT") }, OnDisconnect: func(c *astisrt.Connection, err error) { log.Fatal("Disconnected from SRT") },
Host: offer.SRTHost, Host: params.SRTHost,
Port: offer.SRTPort, Port: params.SRTPort,
}) })
if err != nil { if err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
log.Println("Connected to SRT") log.Println("Connected to SRT")

View File

@@ -4,27 +4,25 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
donutsrt "github.com/flavioribeiro/donut/internal/controller/srt" "github.com/flavioribeiro/donut/internal/controllers"
donutstreaming "github.com/flavioribeiro/donut/internal/controller/streaming" "github.com/flavioribeiro/donut/internal/entities"
donutwebrtc "github.com/flavioribeiro/donut/internal/controller/webrtc"
"github.com/flavioribeiro/donut/internal/entity"
"go.uber.org/zap" "go.uber.org/zap"
) )
type SignalingHandler struct { type SignalingHandler struct {
c *entity.Config c *entities.Config
l *zap.Logger l *zap.Logger
webRTCController *donutwebrtc.WebRTCController webRTCController *controllers.WebRTCController
srtController *donutsrt.SRTController srtController *controllers.SRTController
streamingController *donutstreaming.StreamingController streamingController *controllers.StreamingController
} }
func NewSignalingHandler( func NewSignalingHandler(
c *entity.Config, c *entities.Config,
log *zap.Logger, log *zap.Logger,
webRTCController *donutwebrtc.WebRTCController, webRTCController *controllers.WebRTCController,
srtController *donutsrt.SRTController, srtController *controllers.SRTController,
streamingController *donutstreaming.StreamingController, streamingController *controllers.StreamingController,
) *SignalingHandler { ) *SignalingHandler {
return &SignalingHandler{ return &SignalingHandler{
c: c, c: c,
@@ -36,71 +34,70 @@ func NewSignalingHandler(
} }
func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *SignalingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
SetCORS(w, r)
if r.Method != http.MethodPost { if r.Method != http.MethodPost {
ErrorToHTTP(w, entity.ErrHTTPPostOnly) SetError(w, entities.ErrHTTPPostOnly)
return return
} }
params := entity.RequestParams{} params := entities.RequestParams{}
if err := json.NewDecoder(r.Body).Decode(&params); err != nil { if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
if err := params.Valid(); err != nil { if err := params.Valid(); err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
if err := h.webRTCController.SetupPeerConnection(); err != nil { if err := h.webRTCController.SetupPeerConnection(); err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
// TODO: create tracks according with SRT available streams // TODO: create tracks according with SRT available streams
// Create a video track // Create a video track
videoTrack, err := h.webRTCController.CreateTrack( videoTrack, err := h.webRTCController.CreateTrack(
entity.Track{ entities.Track{
Type: entity.H264, Type: entities.H264,
}, "video", params.SRTStreamID, }, "video", params.SRTStreamID,
) )
if err != nil { if err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
metadataSender, err := h.webRTCController.CreateDataChannel(entity.MetadataChannelID) metadataSender, err := h.webRTCController.CreateDataChannel(entities.MetadataChannelID)
if err != nil { if err != nil {
ErrorToHTTP(w, err) SetError(w, err)
} }
if err = h.webRTCController.SetRemoteDescription(params.Offer); err != nil { if err = h.webRTCController.SetRemoteDescription(params.Offer); err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
localDescription, err := h.webRTCController.GatheringWebRTC() localDescription, err := h.webRTCController.GatheringWebRTC()
if err != nil { if err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
localOfferDescription, err := json.Marshal(*localDescription) localOfferDescription, err := json.Marshal(*localDescription)
if err != nil { if err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
srtConnection, err := h.srtController.Connect(&params) srtConnection, err := h.srtController.Connect(&params)
if err != nil { if err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
go h.streamingController.Stream(srtConnection, videoTrack, metadataSender) go h.streamingController.Stream(srtConnection, videoTrack, metadataSender)
if _, err := w.Write(localOfferDescription); err != nil { if _, err := w.Write(localOfferDescription); err != nil {
ErrorToHTTP(w, err) SetError(w, err)
return return
} }
SetSuccessJson(w) SetSuccessJson(w)

View File

@@ -1,60 +0,0 @@
package handlers
import (
"context"
"fmt"
"net"
"net/http"
"github.com/flavioribeiro/donut/internal/entity"
"go.uber.org/fx"
"go.uber.org/zap"
)
func NewHTTPServer(
c *entity.Config,
mux *http.ServeMux,
log *zap.Logger,
lc fx.Lifecycle,
) *http.Server {
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort),
Handler: mux,
}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
ln, err := net.Listen("tcp", srv.Addr)
if err != nil {
return err
}
log.Sugar().Infow(fmt.Sprintf("Starting HTTP server. Open http://%s to access the demo", srv.Addr),
"addr", srv.Addr,
)
go srv.Serve(ln)
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}
func ErrorToHTTP(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
func SetCORS(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization,X-CSRF-Token"
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
w.Header().Set("Access-Control-Expose-Headers", "Authorization")
}
}
func SetSuccessJson(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}

View File

@@ -1,16 +1,33 @@
package handlers package web
import "net/http" import (
"net/http"
"github.com/flavioribeiro/donut/internal/web/handlers"
)
func NewServeMux( func NewServeMux(
index *IndexHandler, index *handlers.IndexHandler,
signaling *SignalingHandler, signaling *handlers.SignalingHandler,
) *http.ServeMux { ) *http.ServeMux {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/", index) mux.Handle("/", index)
mux.Handle("/doSignaling", signaling) mux.Handle("/doSignaling", setCors(signaling))
return mux return mux
} }
func setCors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization,X-CSRF-Token"
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
w.Header().Set("Access-Control-Expose-Headers", "Authorization")
}
next.ServeHTTP(w, r)
})
}

41
internal/web/server.go Normal file
View File

@@ -0,0 +1,41 @@
package web
import (
"context"
"fmt"
"net"
"net/http"
"github.com/flavioribeiro/donut/internal/entities"
"go.uber.org/fx"
"go.uber.org/zap"
)
func NewHTTPServer(
c *entities.Config,
mux *http.ServeMux,
log *zap.Logger,
lc fx.Lifecycle,
) *http.Server {
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort),
Handler: mux,
}
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
ln, err := net.Listen("tcp", srv.Addr)
if err != nil {
return err
}
log.Sugar().Infow(fmt.Sprintf("Starting HTTP server. Open http://%s to access the demo", srv.Addr),
"addr", srv.Addr,
)
go srv.Serve(ln)
return nil
},
OnStop: func(ctx context.Context) error {
return srv.Shutdown(ctx)
},
})
return srv
}

37
main.go
View File

@@ -8,11 +8,10 @@ import (
"log" "log"
"net/http" "net/http"
donutsrt "github.com/flavioribeiro/donut/internal/controller/srt" "github.com/flavioribeiro/donut/internal/controllers"
donutstreaming "github.com/flavioribeiro/donut/internal/controller/streaming" "github.com/flavioribeiro/donut/internal/entities"
donutwebrtc "github.com/flavioribeiro/donut/internal/controller/webrtc" "github.com/flavioribeiro/donut/internal/web"
"github.com/flavioribeiro/donut/internal/entity" "github.com/flavioribeiro/donut/internal/web/handlers"
handlers "github.com/flavioribeiro/donut/internal/web"
"github.com/kelseyhightower/envconfig" "github.com/kelseyhightower/envconfig"
"go.uber.org/fx" "go.uber.org/fx"
@@ -24,7 +23,7 @@ func main() {
flag.BoolVar(&enableICEMux, "enable-ice-mux", false, "Enable ICE Mux on :8081") flag.BoolVar(&enableICEMux, "enable-ice-mux", false, "Enable ICE Mux on :8081")
flag.Parse() flag.Parse()
var c entity.Config var c entities.Config
err := envconfig.Process("donut", &c) err := envconfig.Process("donut", &c)
if err != nil { if err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
@@ -32,30 +31,28 @@ func main() {
c.EnableICEMux = enableICEMux c.EnableICEMux = enableICEMux
fx.New( fx.New(
// Server entry point // HTTP Server
fx.Provide(handlers.NewHTTPServer), fx.Provide(web.NewHTTPServer),
// HTTP router
fx.Provide(web.NewServeMux),
// HTTP handlers // HTTP handlers
fx.Provide(handlers.NewSignalingHandler), fx.Provide(handlers.NewSignalingHandler),
fx.Provide(handlers.NewIndexHandler), fx.Provide(handlers.NewIndexHandler),
// HTTP router
fx.Provide(handlers.NewServeMux),
// ICE mux servers // ICE mux servers
fx.Provide(donutwebrtc.NewTCPICEServer), fx.Provide(controllers.NewTCPICEServer),
fx.Provide(donutwebrtc.NewUDPICEServer), fx.Provide(controllers.NewUDPICEServer),
// WebRTC controller // Controllers
fx.Provide(donutwebrtc.NewWebRTCController), fx.Provide(controllers.NewWebRTCController),
// SRT controller fx.Provide(controllers.NewSRTController),
fx.Provide(donutsrt.NewSRTController), fx.Provide(controllers.NewStreamingController),
// Streaming controller
fx.Provide(donutstreaming.NewStreamingController),
// Logging, Config // Logging, Config
fx.Provide(zap.NewProduction), fx.Provide(zap.NewProduction),
fx.Provide(func() *entity.Config { fx.Provide(func() *entities.Config {
return &c return &c
}), }),