diff --git a/client/src/neko/base.ts b/client/src/neko/base.ts index d8a40b3f..f331892c 100644 --- a/client/src/neko/base.ts +++ b/client/src/neko/base.ts @@ -2,7 +2,7 @@ import EventEmitter from 'eventemitter3' import { OPCODE } from './data' import { EVENT, WebSocketEvents } from './events' -import { WebSocketMessages, WebSocketPayloads, SignalProvidePayload } from './messages' +import { WebSocketMessages, WebSocketPayloads, SignalProvidePayload, SignalCandidatePayload } from './messages' export interface BaseEvents { info: (...message: any[]) => void @@ -211,8 +211,8 @@ export abstract class BaseClient extends EventEmitter { } this._peer.ontrack = this.onTrack.bind(this) - this._peer.addTransceiver('audio', { direction: 'recvonly' }) - this._peer.addTransceiver('video', { direction: 'recvonly' }) + this._peer.addTransceiver('audio', { direction: 'sendrecv' }) + this._peer.addTransceiver('video', { direction: 'sendrecv' }) this._channel = this._peer.createDataChannel('data') this._channel.onerror = this.onError.bind(this) @@ -246,6 +246,15 @@ export abstract class BaseClient extends EventEmitter { this.createPeer(sdp, lite, ice) return } + if (event === EVENT.SIGNAL.CANDIDATE) { + const { data } = payload as SignalCandidatePayload + let candidate: RTCIceCandidate = JSON.parse(data) + this._peer!.addIceCandidate(candidate) + + return + } + + // @ts-ignore if (typeof this[event] === 'function') { diff --git a/client/src/neko/events.ts b/client/src/neko/events.ts index b2bb0435..beb55b7a 100644 --- a/client/src/neko/events.ts +++ b/client/src/neko/events.ts @@ -14,6 +14,7 @@ export const EVENT = { SIGNAL: { ANSWER: 'signal/answer', PROVIDE: 'signal/provide', + CANDIDATE: 'signal/candidate' }, MEMBER: { LIST: 'member/list', @@ -78,7 +79,7 @@ export type ControlEvents = export type SystemEvents = typeof EVENT.SYSTEM.DISCONNECT export type MemberEvents = typeof EVENT.MEMBER.LIST | typeof EVENT.MEMBER.CONNECTED | typeof EVENT.MEMBER.DISCONNECTED -export type SignalEvents = typeof EVENT.SIGNAL.ANSWER | typeof EVENT.SIGNAL.PROVIDE +export type SignalEvents = typeof EVENT.SIGNAL.ANSWER | typeof EVENT.SIGNAL.PROVIDE | typeof EVENT.SIGNAL.CANDIDATE export type ChatEvents = typeof EVENT.CHAT.MESSAGE | typeof EVENT.CHAT.EMOTE export type ScreenEvents = typeof EVENT.SCREEN.CONFIGURATIONS | typeof EVENT.SCREEN.RESOLUTION | typeof EVENT.SCREEN.SET diff --git a/client/src/neko/messages.ts b/client/src/neko/messages.ts index 3530c7c3..276ddf75 100644 --- a/client/src/neko/messages.ts +++ b/client/src/neko/messages.ts @@ -15,6 +15,7 @@ export type WebSocketMessages = | WebSocketMessage | SignalProvideMessage | SignalAnswerMessage + | SignalCandidateMessage | MemberListMessage | MemberConnectMessage | MemberDisconnectMessage @@ -26,6 +27,7 @@ export type WebSocketMessages = export type WebSocketPayloads = | SignalProvidePayload | SignalAnswerPayload + | SignalCandidatePayload | MemberListPayload | Member | ControlPayload @@ -78,6 +80,14 @@ export interface SignalAnswerPayload { displayname: string } +// signal/candidate +export interface SignalCandidateMessage extends WebSocketMessage, SignalCandidatePayload { + event: typeof EVENT.SIGNAL.CANDIDATE +} +export interface SignalCandidatePayload { + data: string +} + /* MEMBER MESSAGES/PAYLOADS */ diff --git a/server/internal/gst/gst.go b/server/internal/gst/gst.go index d7c8291e..2281d001 100644 --- a/server/internal/gst/gst.go +++ b/server/internal/gst/gst.go @@ -10,10 +10,9 @@ import "C" import ( "fmt" "sync" + "time" "unsafe" - "github.com/pion/webrtc/v2" - "n.eko.moe/neko/internal/types" ) @@ -80,13 +79,13 @@ func CreateRTMPPipeline(pipelineDevice string, pipelineDisplay string, pipelineS } // CreateAppPipeline creates a GStreamer Pipeline -func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc string) (*Pipeline, error) { +func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc string, bitrate string) (*Pipeline, error) { pipelineStr := " ! appsink name=appsink" var clockRate float32 switch codecName { - case webrtc.VP8: + case "VP8": // https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html?gi-language=c // gstreamer1.0-plugins-good // vp8enc error-resilient=partitions keyframe-max-dist=10 auto-alt-ref=true cpu-used=5 deadline=1 @@ -99,9 +98,9 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri if pipelineSrc != "" { pipelineStr = fmt.Sprintf(pipelineSrc+pipelineStr, pipelineDevice) } else { - pipelineStr = fmt.Sprintf(videoSrc+"vp8enc cpu-used=8 threads=2 deadline=1 error-resilient=partitions keyframe-max-dist=10 auto-alt-ref=true"+pipelineStr, pipelineDevice) + pipelineStr = fmt.Sprintf(videoSrc+"vp8enc cpu-used=-5 threads=4 deadline=1 error-resilient=partitions keyframe-max-dist=30 auto-alt-ref=true"+pipelineStr, pipelineDevice) } - case webrtc.VP9: + case "VP9": // https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html?gi-language=c // gstreamer1.0-plugins-good // vp9enc @@ -117,7 +116,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri } else { pipelineStr = fmt.Sprintf(videoSrc+"vp9enc"+pipelineStr, pipelineDevice) } - case webrtc.H264: + case "H264": // https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html?gi-language=c#openh264enc // gstreamer1.0-plugins-bad // openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 @@ -130,14 +129,21 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri if pipelineSrc != "" { pipelineStr = fmt.Sprintf(pipelineSrc+pipelineStr, pipelineDevice) } else { - var h264Str string - h264Str = "openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 ! video/x-h264,stream-format=byte-stream" + var h264Str string + h264Str = "openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 ! video/x-h264,stream-format=byte-stream" + if bitrate != "" { + h264Str = "openh264enc multi-thread=4 complexity=high bitrate=" + bitrate + "000 max-bitrate=" + bitrate + "999 ! video/x-h264,stream-format=byte-stream" + } // https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c // gstreamer1.0-plugins-ugly // video/x-raw,format=I420 ! x264enc bframes=0 key-int-max=60 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream if err := CheckPlugins([]string{"openh264"}); err != nil { - h264Str = "video/x-raw,format=I420 ! x264enc bframes=0 key-int-max=60 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream" + + h264Str = "video/x-raw,format=I420 ! x264enc threads=4 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream" + if bitrate != "" { + h264Str = "video/x-raw,format=I420 ! x264enc threads=4 bitrate=" + bitrate + " byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream" + } if err := CheckPlugins([]string{"x264"}); err != nil { return nil, err @@ -145,7 +151,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri } pipelineStr = fmt.Sprintf(videoSrc+h264Str+pipelineStr, pipelineDevice) } - case webrtc.Opus: + case "Opus": // https://gstreamer.freedesktop.org/documentation/opus/opusenc.html // gstreamer1.0-plugins-base // opusenc @@ -160,7 +166,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri } else { pipelineStr = fmt.Sprintf(audioSrc+"opusenc"+pipelineStr, pipelineDevice) } - case webrtc.G722: + case "G722": // https://gstreamer.freedesktop.org/documentation/libav/avenc_g722.html?gi-language=c // gstreamer1.0-libav // avenc_g722 @@ -175,7 +181,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri } else { pipelineStr = fmt.Sprintf(audioSrc+"avenc_g722"+pipelineStr, pipelineDevice) } - case webrtc.PCMU: + case "PCMU": // https://gstreamer.freedesktop.org/documentation/mulaw/mulawenc.html?gi-language=c // gstreamer1.0-plugins-good // audio/x-raw, rate=8000 ! mulawenc @@ -190,7 +196,7 @@ func CreateAppPipeline(codecName string, pipelineDevice string, pipelineSrc stri } else { pipelineStr = fmt.Sprintf(audioSrc+"audio/x-raw, rate=8000 ! mulawenc"+pipelineStr, pipelineDevice) } - case webrtc.PCMA: + case "PCMA": // https://gstreamer.freedesktop.org/documentation/alaw/alawenc.html?gi-language=c // gstreamer1.0-plugins-good // audio/x-raw, rate=8000 ! alawenc @@ -270,8 +276,7 @@ func goHandlePipelineBuffer(buffer unsafe.Pointer, bufferLen C.int, duration C.i pipelinesLock.Unlock() if ok { - samples := uint32(pipeline.ClockRate * (float32(duration) / 1000000000)) - pipeline.Sample <- types.Sample{Data: C.GoBytes(buffer, bufferLen), Samples: samples} + pipeline.Sample <- types.Sample{Data: C.GoBytes(buffer, bufferLen), Timestamp: time.Now(), Duration: time.Duration(duration)} } else { fmt.Printf("discarding buffer, no pipeline with id %d", int(pipelineID)) } diff --git a/server/internal/remote/manager.go b/server/internal/remote/manager.go index 3ed2f005..de3f1981 100644 --- a/server/internal/remote/manager.go +++ b/server/internal/remote/manager.go @@ -135,6 +135,7 @@ func (manager *RemoteManager) createPipelines() { manager.config.VideoCodec, manager.config.Display, manager.config.VideoParams, + manager.config.Bitrate, ) if err != nil { manager.logger.Panic().Err(err).Msg("unable to create video pipeline") @@ -144,6 +145,7 @@ func (manager *RemoteManager) createPipelines() { manager.config.AudioCodec, manager.config.Device, manager.config.AudioParams, + "", ) if err != nil { manager.logger.Panic().Err(err).Msg("unable to create audio pipeline") @@ -174,6 +176,7 @@ func (manager *RemoteManager) ChangeResolution(width int, height int, rate int) manager.config.VideoCodec, manager.config.Display, manager.config.VideoParams, + manager.config.Bitrate, ) if err != nil { manager.logger.Panic().Err(err).Msg("unable to create new video pipeline") diff --git a/server/internal/session/session.go b/server/internal/session/session.go index b9733a78..790b8e30 100644 --- a/server/internal/session/session.go +++ b/server/internal/session/session.go @@ -120,6 +120,16 @@ func (session *Session) SignalAnswer(sdp string) error { return session.peer.SignalAnswer(sdp) } +func (session *Session) SignalCandidate(data string) error { + if session.socket == nil { + return nil + } + return session.socket.Send(&message.SignalCandidate{ + Event: event.SIGNAL_CANDIDATE, + Data: data, + }); +} + func (session *Session) destroy() error { if session.socket != nil { if err := session.socket.Destroy(); err != nil { diff --git a/server/internal/types/config/remote.go b/server/internal/types/config/remote.go index 045a840b..f0309d9e 100644 --- a/server/internal/types/config/remote.go +++ b/server/internal/types/config/remote.go @@ -4,7 +4,6 @@ import ( "regexp" "strconv" - "github.com/pion/webrtc/v2" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -19,6 +18,7 @@ type Remote struct { ScreenWidth int ScreenHeight int ScreenRate int + Bitrate string } func (Remote) Init(cmd *cobra.Command) error { @@ -47,6 +47,12 @@ func (Remote) Init(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().String("bitrate", "", "set this video bitrate when possible") + if err := viper.BindPFlag("bitrate", cmd.PersistentFlags().Lookup("bitrate")); err != nil { + return err + } + + // video codecs cmd.PersistentFlags().Bool("vp8", false, "use VP8 video codec") if err := viper.BindPFlag("vp8", cmd.PersistentFlags().Lookup("vp8")); err != nil { @@ -88,24 +94,24 @@ func (Remote) Init(cmd *cobra.Command) error { } func (s *Remote) Set() { - videoCodec := webrtc.VP8 + videoCodec := "VP8" if viper.GetBool("vp8") { - videoCodec = webrtc.VP8 + videoCodec = "VP8" } else if viper.GetBool("vp9") { - videoCodec = webrtc.VP9 + videoCodec = "VP9" } else if viper.GetBool("h264") { - videoCodec = webrtc.H264 + videoCodec = "H264" } - audioCodec := webrtc.Opus + audioCodec := "Opus" if viper.GetBool("opus") { - audioCodec = webrtc.Opus + audioCodec = "Opus" } else if viper.GetBool("g722") { - audioCodec = webrtc.G722 + audioCodec = "G722" } else if viper.GetBool("pcmu") { - audioCodec = webrtc.PCMU + audioCodec = "PCMU" } else if viper.GetBool("pcma") { - audioCodec = webrtc.PCMA + audioCodec = "PCMA" } s.Device = viper.GetString("device") @@ -114,6 +120,7 @@ func (s *Remote) Set() { s.Display = viper.GetString("display") s.VideoCodec = videoCodec s.VideoParams = viper.GetString("video") + s.Bitrate = viper.GetString("bitrate") s.ScreenWidth = 1280 s.ScreenHeight = 720 diff --git a/server/internal/types/event/events.go b/server/internal/types/event/events.go index b97c49c8..00c355e7 100644 --- a/server/internal/types/event/events.go +++ b/server/internal/types/event/events.go @@ -5,8 +5,10 @@ const ( ) const ( - SIGNAL_ANSWER = "signal/answer" - SIGNAL_PROVIDE = "signal/provide" + SIGNAL_ANSWER = "signal/answer" + SIGNAL_OFFER = "signal/offer" + SIGNAL_PROVIDE = "signal/provide" + SIGNAL_CANDIDATE = "signal/candidate" ) const ( diff --git a/server/internal/types/message/messages.go b/server/internal/types/message/messages.go index fcf04f30..56d116e7 100644 --- a/server/internal/types/message/messages.go +++ b/server/internal/types/message/messages.go @@ -27,6 +27,11 @@ type SignalAnswer struct { SDP string `json:"sdp"` } +type SignalCandidate struct { + Event string `json:"event"` + Data string `json:"data"` +} + type MembersList struct { Event string `json:"event"` Memebers []*types.Member `json:"members"` diff --git a/server/internal/types/session.go b/server/internal/types/session.go index acdcac64..7940f812 100644 --- a/server/internal/types/session.go +++ b/server/internal/types/session.go @@ -24,6 +24,7 @@ type Session interface { Write(v interface{}) error Send(v interface{}) error SignalAnswer(sdp string) error + SignalCandidate(data string) error } type SessionManager interface { diff --git a/server/internal/types/webrtc.go b/server/internal/types/webrtc.go index d0da61e7..80c2f1a1 100644 --- a/server/internal/types/webrtc.go +++ b/server/internal/types/webrtc.go @@ -1,8 +1,13 @@ package types +import ( + "time" +) + type Sample struct { - Data []byte - Samples uint32 + Data []byte + Timestamp time.Time + Duration time.Duration } type WebRTCManager interface { diff --git a/server/internal/webrtc/handle.go b/server/internal/webrtc/handle.go index 35c4b235..56d0b260 100644 --- a/server/internal/webrtc/handle.go +++ b/server/internal/webrtc/handle.go @@ -5,7 +5,7 @@ import ( "encoding/binary" "strconv" - "github.com/pion/webrtc/v2" + "github.com/pion/webrtc/v3" ) const OP_MOVE = 0x01 diff --git a/server/internal/webrtc/peer.go b/server/internal/webrtc/peer.go index f82c4fa7..9ca5797c 100644 --- a/server/internal/webrtc/peer.go +++ b/server/internal/webrtc/peer.go @@ -3,7 +3,7 @@ package webrtc import ( "sync" - "github.com/pion/webrtc/v2" + "github.com/pion/webrtc/v3" ) type Peer struct { diff --git a/server/internal/webrtc/webrtc.go b/server/internal/webrtc/webrtc.go index b658ecb8..2fd4e7f2 100644 --- a/server/internal/webrtc/webrtc.go +++ b/server/internal/webrtc/webrtc.go @@ -1,13 +1,15 @@ package webrtc import ( + "encoding/json" "fmt" "io" - "math/rand" "strings" - "github.com/pion/webrtc/v2" - "github.com/pion/webrtc/v2/pkg/media" + "github.com/pion/interceptor" + "github.com/pion/rtcp" + "github.com/pion/webrtc/v3" + "github.com/pion/webrtc/v3/pkg/media" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -26,10 +28,10 @@ func New(sessions types.SessionManager, remote types.RemoteManager, config *conf type WebRTCManager struct { logger zerolog.Logger - videoTrack *webrtc.Track - audioTrack *webrtc.Track - videoCodec *webrtc.RTPCodec - audioCodec *webrtc.RTPCodec + videoTrack *webrtc.TrackLocalStaticSample + audioTrack *webrtc.TrackLocalStaticSample + videoCodec webrtc.RTPCodecParameters + audioCodec webrtc.RTPCodecParameters sessions types.SessionManager remote types.RemoteManager config *config.WebRTC @@ -97,39 +99,31 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri settings.SetEphemeralUDPPortRange(manager.config.EphemeralMin, manager.config.EphemeralMax) settings.SetNAT1To1IPs(manager.config.NAT1To1IPs, webrtc.ICECandidateTypeHost) + settings.SetSRTPReplayProtectionWindow(512) // Create MediaEngine based off sdp engine := webrtc.MediaEngine{} - engine.RegisterCodec(manager.audioCodec) - engine.RegisterCodec(manager.videoCodec) + engine.RegisterCodec(manager.audioCodec, webrtc.RTPCodecTypeAudio) + engine.RegisterCodec(manager.videoCodec, webrtc.RTPCodecTypeVideo) + + i := &interceptor.Registry{} + if err := webrtc.RegisterDefaultInterceptors(&engine, i); err != nil { + return "", manager.config.ICELite, manager.config.ICEServers, err + } // Create API with MediaEngine and SettingEngine - api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings)) + api := webrtc.NewAPI(webrtc.WithMediaEngine(&engine), webrtc.WithSettingEngine(settings), webrtc.WithInterceptorRegistry(i)) // Create new peer connection connection, err := api.NewPeerConnection(*configuration) if err != nil { return "", manager.config.ICELite, manager.config.ICEServers, err } - - if _, err = connection.AddTransceiverFromTrack(manager.videoTrack, webrtc.RtpTransceiverInit{ - Direction: webrtc.RTPTransceiverDirectionSendonly, - }); err != nil { - return "", manager.config.ICELite, manager.config.ICEServers, err - } - - if _, err = connection.AddTransceiverFromTrack(manager.audioTrack, webrtc.RtpTransceiverInit{ - Direction: webrtc.RTPTransceiverDirectionSendonly, - }); err != nil { - return "", manager.config.ICELite, manager.config.ICEServers, err - } - - description, err := connection.CreateOffer(nil) - if err != nil { - return "", manager.config.ICELite, manager.config.ICEServers, err - } - + negotiated := true + connection.CreateDataChannel("data", &webrtc.DataChannelInit{ + Negotiated: &negotiated, + }) connection.OnDataChannel(func(d *webrtc.DataChannel) { d.OnMessage(func(msg webrtc.DataChannelMessage) { if err = manager.handle(id, msg); err != nil { @@ -138,7 +132,31 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri }) }) - connection.SetLocalDescription(description) + // Set the handler for ICE connection state + // This will notify you when the peer has connected/disconnected + connection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { + fmt.Printf("Connection State has changed %s \n", connectionState.String()) + }) + + rtpSender, viderr := connection.AddTrack(manager.videoTrack) + if viderr != nil { + return "", manager.config.ICELite, manager.config.ICEServers, viderr + } + + if _, err = connection.AddTrack(manager.audioTrack); err != nil { + return "", manager.config.ICELite, manager.config.ICEServers, err + } + + description, err := connection.CreateOffer(nil) + if err != nil { + return "", manager.config.ICELite, manager.config.ICEServers, err + } + + err = connection.SetLocalDescription(description) + if err != nil { + panic(err) + } + connection.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { switch state { case webrtc.PeerConnectionStateDisconnected: @@ -156,6 +174,47 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri } }) + connection.OnICECandidate(func(i *webrtc.ICECandidate) { + if i != nil { + candidateString, err := json.Marshal(i.ToJSON()) + if err != nil { + manager.logger.Info().Msg("error") + return + } + + if err = session.SignalCandidate(string(candidateString));err != nil { + manager.logger.Info().Msg("err") + return + } + } + }) + + + // Read incoming RTCP packets + // Before these packets are retuned they are processed by interceptors. For things + // like NACK this needs to be called. + go func() { + rtcpBuf := make([]byte, 1500) + for { + n, _, rtcpErr := rtpSender.Read(rtcpBuf) + if rtcpErr != nil { + return + } + ps, err := rtcp.Unmarshal(rtcpBuf[:n]) + if err != nil { + log.Printf("Unmarshal RTCP: %v", err) + continue + } + for _, p := range ps { + switch p.(type) { + case *rtcp.TransportLayerNack: + manager.logger.Info().Msg("got a nack") + } + } + } + }() + + if err := session.SetPeer(&Peer{ id: id, api: api, @@ -171,30 +230,40 @@ func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (stri return description.SDP, manager.config.ICELite, manager.config.ICEServers, nil } -func (m *WebRTCManager) createTrack(codecName string) (*webrtc.Track, *webrtc.RTPCodec, error) { - var codec *webrtc.RTPCodec +func (m *WebRTCManager) createTrack(codecName string) (*webrtc.TrackLocalStaticSample, webrtc.RTPCodecParameters, error) { + var codec webrtc.RTPCodecParameters + var fb []webrtc.RTCPFeedback + var fba []webrtc.RTCPFeedback + fb = []webrtc.RTCPFeedback{ + {"goog-remb", ""}, + {"nack", ""}, + {"nack", "pli"}, + {"ccm", "fir"}, + } + fba = []webrtc.RTCPFeedback{} + switch codecName { - case webrtc.VP8: - codec = webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000) - case webrtc.VP9: - codec = webrtc.NewRTPVP9Codec(webrtc.DefaultPayloadTypeVP9, 90000) - case webrtc.H264: - codec = webrtc.NewRTPH264Codec(webrtc.DefaultPayloadTypeH264, 90000) - case webrtc.Opus: - codec = webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000) - case webrtc.G722: - codec = webrtc.NewRTPG722Codec(webrtc.DefaultPayloadTypeG722, 8000) - case webrtc.PCMU: - codec = webrtc.NewRTPPCMUCodec(webrtc.DefaultPayloadTypePCMU, 8000) - case webrtc.PCMA: - codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000) + case "VP8": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "video/VP8", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: fb}, PayloadType: 96,} + case "VP9": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "video/VP9", ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: fb}, PayloadType: 98,} + case "H264": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "video/H264", ClockRate: 90000, Channels: 0, SDPFmtpLine: "level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f", RTCPFeedback: fb}, PayloadType: 102,} + case "Opus": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "audio/opus", ClockRate: 48000, Channels: 2, SDPFmtpLine: "", RTCPFeedback: fba}, PayloadType: 111,} + case "G722": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "audio/G722", ClockRate: 8000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: fba}, PayloadType: 9,} + case "PCMU": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "audio/PCMU", ClockRate: 8000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: fba}, PayloadType: 0,} + case "PCMA": + codec = webrtc.RTPCodecParameters{ RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: "audio/PCMA", ClockRate: 8000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: fba}, PayloadType: 8,} default: - return nil, nil, fmt.Errorf("unknown codec %s", codecName) + return nil, codec, fmt.Errorf("unknown codec %s", codecName) } - track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec) + track, err := webrtc.NewTrackLocalStaticSample(codec.RTPCodecCapability, "stream", "stream") if err != nil { - return nil, nil, err + return nil, codec, err } return track, codec, nil