diff --git a/main.go b/main.go index 74514e8..b87d9ef 100644 --- a/main.go +++ b/main.go @@ -11,18 +11,22 @@ import ( "time" "github.com/Monibuca/engine/v3" + "github.com/Monibuca/plugin-webrtc/v3/webrtc" + + // "github.com/Monibuca/plugin-webrtc/v3/webrtc" "github.com/Monibuca/utils/v3" "github.com/pion/rtcp" . "github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3/pkg/media" ) -var config struct { +var config = struct { ICEServers []string PublicIP []string PortMin uint16 PortMax uint16 -} + PLI time.Duration +}{nil, nil, 0, 0, 2000} // }{[]string{ // "stun:stun.ekiga.net", @@ -88,51 +92,18 @@ type WebRTC struct { } func (rtc *WebRTC) Publish(streamPath string) bool { - // rtc.m.RegisterCodec(NewRTPCodec(RTPCodecTypeVideo, - // H264, - // 90000, - // 0, - // "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", - // DefaultPayloadTypeH264, - // new(codec.H264))) - - // rtc.m.RegisterCodec(RTPCodecParameters{ - // RTPCodecCapability: RTPCodecCapability{MimeType: "video/h264", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil}, - // PayloadType: 96, - // }, RTPCodecTypeVideo); - - //m.RegisterCodec(NewRTPPCMUCodec(DefaultPayloadTypePCMU, 8000)) - // if !strings.HasPrefix(rtc.RemoteAddr, "127.0.0.1") && !strings.HasPrefix(rtc.RemoteAddr, "[::1]") { - // rtc.s.SetNAT1To1IPs(config.PublicIP, ICECandidateTypeHost) - // } - - peerConnection, err := api.NewPeerConnection(Configuration{ - // ICEServers: []ICEServer{ - // { - // URLs: config.ICEServers, - // }, - // }, - }) - rtc.PeerConnection = peerConnection - if err != nil { - utils.Println(err) - return false - } - if _, err = peerConnection.AddTransceiverFromKind(RTPCodecTypeVideo); err != nil { + if _, err := rtc.AddTransceiverFromKind(RTPCodecTypeVideo); err != nil { if err != nil { utils.Println(err) return false } } - if err != nil { - return false - } stream := &engine.Stream{ Type: "WebRTC", StreamPath: streamPath, } if stream.Publish() { - peerConnection.OnICEConnectionStateChange(func(connectionState ICEConnectionState) { + rtc.OnICEConnectionStateChange(func(connectionState ICEConnectionState) { utils.Printf("%s Connection State has changed %s ", streamPath, connectionState.String()) switch connectionState { case ICEConnectionStateDisconnected, ICEConnectionStateFailed: @@ -140,19 +111,8 @@ func (rtc *WebRTC) Publish(streamPath string) bool { } }) //f, _ := os.OpenFile("resource/live/rtc.h264", os.O_TRUNC|os.O_WRONLY, 0666) - peerConnection.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) { - defer stream.Close() - go func() { - ticker := time.NewTicker(time.Second * 2) - select { - case <-ticker.C: - if rtcpErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); rtcpErr != nil { - fmt.Println(rtcpErr) - } - case <-stream.Done(): - return - } - }() + rtc.OnTrack(func(track *TrackRemote, receiver *RTPReceiver) { + if codec := track.Codec(); track.Kind() == RTPCodecTypeAudio { var at *engine.RTPAudio switch codec.MimeType { @@ -165,15 +125,36 @@ func (rtc *WebRTC) Publish(streamPath string) bool { default: return } - b := make([]byte, 1460) - for i, _, err := track.Read(b); err == nil; i, _, err = track.Read(b) { - at.Push(b[:i]) + for { + b := make([]byte, 1460) + if i, _, err := track.Read(b); err == nil { + at.Push(b[:i]) + } else { + return + } } } else { + go func() { + ticker := time.NewTicker(time.Millisecond * config.PLI) + for { + select { + case <-ticker.C: + if rtcpErr := rtc.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); rtcpErr != nil { + fmt.Println(rtcpErr) + } + case <-stream.Done(): + return + } + } + }() vt := stream.NewRTPVideo(7) - b := make([]byte, 1460) - for i, _, err := track.Read(b); err == nil; i, _, err = track.Read(b) { - vt.Push(b[:i]) + for { + b := make([]byte, 1460) + if i, _, err := track.Read(b); err == nil { + vt.Push(b[:i]) + } else { + return + } } } @@ -191,7 +172,6 @@ func (rtc *WebRTC) GetAnswer() ([]byte, error) { } gatherComplete := GatheringCompletePromise(rtc.PeerConnection) if err := rtc.SetLocalDescription(answer); err != nil { - utils.Println(err) return nil, err } <-gatherComplete @@ -212,7 +192,8 @@ func run() { if config.PortMin > 0 && config.PortMax > 0 { s.SetEphemeralUDPPortRange(config.PortMin, config.PortMax) } - m.RegisterDefaultCodecs() + // m.RegisterDefaultCodecs() + webrtc.RegisterCodecs(&m) api = NewAPI(WithMediaEngine(&m), WithSettingEngine(s)) http.HandleFunc("/api/webrtc/play", func(w http.ResponseWriter, r *http.Request) { utils.CORS(w, r) @@ -362,6 +343,7 @@ func run() { }) http.HandleFunc("/api/webrtc/publish", func(w http.ResponseWriter, r *http.Request) { + utils.CORS(w, r) streamPath := r.URL.Query().Get("streamPath") offer := SessionDescription{} bytes, err := ioutil.ReadAll(r.Body) @@ -371,6 +353,9 @@ func run() { return } rtc := new(WebRTC) + if rtc.PeerConnection, err = api.NewPeerConnection(Configuration{}); err != nil { + return + } if rtc.Publish(streamPath) { if err := rtc.SetRemoteDescription(offer); err != nil { utils.Println(err) @@ -379,7 +364,7 @@ func run() { if bytes, err = rtc.GetAnswer(); err == nil { w.Write(bytes) } else { - utils.Println(err) + utils.Println("GetAnswer:", err) w.Write([]byte(err.Error())) return } diff --git a/webrtc/config.go b/webrtc/config.go new file mode 100644 index 0000000..20a2869 --- /dev/null +++ b/webrtc/config.go @@ -0,0 +1,98 @@ +package webrtc + +import ( + . "github.com/pion/webrtc/v3" +) + +func RegisterCodecs(m *MediaEngine) error { + for _, codec := range []RTPCodecParameters{ + { + RTPCodecCapability: RTPCodecCapability{MimeTypePCMU, 8000, 0, "", nil}, + PayloadType: 0, + }, + { + RTPCodecCapability: RTPCodecCapability{MimeTypePCMA, 8000, 0, "", nil}, + PayloadType: 8, + }, + } { + if err := m.RegisterCodec(codec, RTPCodecTypeAudio); err != nil { + return err + } + } + videoRTCPFeedback := []RTCPFeedback{{"goog-remb", ""}, {"ccm", "fir"}, {"nack", ""}, {"nack", "pli"}} + for _, codec := range []RTPCodecParameters{ + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=96", nil}, + // PayloadType: 97, + // }, + + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=98", nil}, + // PayloadType: 99, + // }, + + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=100", nil}, + // PayloadType: 101, + // }, + + { + RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", videoRTCPFeedback}, + PayloadType: 102, + }, + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=102", nil}, + // PayloadType: 121, + // }, + + { + RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback}, + PayloadType: 127, + }, + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil}, + // PayloadType: 120, + // }, + + { + RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f", videoRTCPFeedback}, + PayloadType: 125, + }, + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=125", nil}, + // PayloadType: 107, + // }, + + { + RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f", videoRTCPFeedback}, + PayloadType: 108, + }, + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=108", nil}, + // PayloadType: 109, + // }, + + { + RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f", videoRTCPFeedback}, + PayloadType: 127, + }, + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=127", nil}, + // PayloadType: 120, + // }, + + { + RTPCodecCapability: RTPCodecCapability{MimeTypeH264, 90000, 0, "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=640032", videoRTCPFeedback}, + PayloadType: 123, + }, + // { + // RTPCodecCapability: RTPCodecCapability{"video/rtx", 90000, 0, "apt=123", nil}, + // PayloadType: 118, + // }, + } { + if err := m.RegisterCodec(codec, RTPCodecTypeVideo); err != nil { + return err + } + } + return nil +}