ability to use ICE or ICELite
This commit is contained in:
parent
853dd14386
commit
04033b664b
@ -62,8 +62,8 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
|||||||
this._ws = new WebSocket(`${url}ws?password=${password}`)
|
this._ws = new WebSocket(`${url}ws?password=${password}`)
|
||||||
this.emit('debug', `connecting to ${this._ws.url}`)
|
this.emit('debug', `connecting to ${this._ws.url}`)
|
||||||
this._ws.onmessage = this.onMessage.bind(this)
|
this._ws.onmessage = this.onMessage.bind(this)
|
||||||
this._ws.onerror = (event) => this.onError.bind(this)
|
this._ws.onerror = event => this.onError.bind(this)
|
||||||
this._ws.onclose = (event) => this.onDisconnected.bind(this, new Error('websocket closed'))
|
this._ws.onclose = event => this.onDisconnected.bind(this, new Error('websocket closed'))
|
||||||
this._timeout = setTimeout(this.onTimeout.bind(this), 15000)
|
this._timeout = setTimeout(this.onTimeout.bind(this), 15000)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.onDisconnected(err)
|
this.onDisconnected(err)
|
||||||
@ -156,7 +156,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
|||||||
this._ws!.send(JSON.stringify({ event, ...payload }))
|
this._ws!.send(JSON.stringify({ event, ...payload }))
|
||||||
}
|
}
|
||||||
|
|
||||||
public createPeer(sdp: string) {
|
public createPeer(sdp: string, lite: boolean, servers: string[]) {
|
||||||
this.emit('debug', `creating peer`)
|
this.emit('debug', `creating peer`)
|
||||||
if (!this.socketOpen) {
|
if (!this.socketOpen) {
|
||||||
this.emit(
|
this.emit(
|
||||||
@ -173,16 +173,21 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._peer = new RTCPeerConnection()
|
this._peer = new RTCPeerConnection()
|
||||||
|
if (lite !== true) {
|
||||||
|
this._peer = new RTCPeerConnection({
|
||||||
|
iceServers: [{ urls: servers }],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
this._peer.onconnectionstatechange = (event) => {
|
this._peer.onconnectionstatechange = event => {
|
||||||
this.emit('debug', `peer connection state changed`, this._peer ? this._peer.connectionState : undefined)
|
this.emit('debug', `peer connection state changed`, this._peer ? this._peer.connectionState : undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._peer.onsignalingstatechange = (event) => {
|
this._peer.onsignalingstatechange = event => {
|
||||||
this.emit('debug', `peer signaling state changed`, this._peer ? this._peer.signalingState : undefined)
|
this.emit('debug', `peer signaling state changed`, this._peer ? this._peer.signalingState : undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
this._peer.oniceconnectionstatechange = (event) => {
|
this._peer.oniceconnectionstatechange = event => {
|
||||||
this._state = this._peer!.iceConnectionState
|
this._state = this._peer!.iceConnectionState
|
||||||
|
|
||||||
this.emit('debug', `peer ice connection state changed: ${this._peer!.iceConnectionState}`)
|
this.emit('debug', `peer ice connection state changed: ${this._peer!.iceConnectionState}`)
|
||||||
@ -217,7 +222,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
|||||||
this._peer.setRemoteDescription({ type: 'offer', sdp })
|
this._peer.setRemoteDescription({ type: 'offer', sdp })
|
||||||
this._peer
|
this._peer
|
||||||
.createAnswer()
|
.createAnswer()
|
||||||
.then((d) => {
|
.then(d => {
|
||||||
this._peer!.setLocalDescription(d)
|
this._peer!.setLocalDescription(d)
|
||||||
this._ws!.send(
|
this._ws!.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@ -227,7 +232,7 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.catch((err) => this.emit('error', err))
|
.catch(err => this.emit('error', err))
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMessage(e: MessageEvent) {
|
private onMessage(e: MessageEvent) {
|
||||||
@ -236,9 +241,9 @@ export abstract class BaseClient extends EventEmitter<BaseEvents> {
|
|||||||
this.emit('debug', `received websocket event ${event} ${payload ? `with payload: ` : ''}`, payload)
|
this.emit('debug', `received websocket event ${event} ${payload ? `with payload: ` : ''}`, payload)
|
||||||
|
|
||||||
if (event === EVENT.SIGNAL.PROVIDE) {
|
if (event === EVENT.SIGNAL.PROVIDE) {
|
||||||
const { sdp, id } = payload as SignalProvidePayload
|
const { sdp, lite, ice, id } = payload as SignalProvidePayload
|
||||||
this._id = id
|
this._id = id
|
||||||
this.createPeer(sdp)
|
this.createPeer(sdp, lite, ice)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,8 @@ export interface SignalProvideMessage extends WebSocketMessage, SignalProvidePay
|
|||||||
}
|
}
|
||||||
export interface SignalProvidePayload {
|
export interface SignalProvidePayload {
|
||||||
id: string
|
id: string
|
||||||
|
lite: boolean
|
||||||
|
ice: string[]
|
||||||
sdp: string
|
sdp: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ type WebRTC struct {
|
|||||||
AudioCodec string
|
AudioCodec string
|
||||||
AudioParams string
|
AudioParams string
|
||||||
Display string
|
Display string
|
||||||
|
ICELite bool
|
||||||
|
ICEServers []string
|
||||||
VideoCodec string
|
VideoCodec string
|
||||||
VideoParams string
|
VideoParams string
|
||||||
EphemeralMin uint16
|
EphemeralMin uint16
|
||||||
@ -99,6 +101,16 @@ func (WebRTC) Init(cmd *cobra.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().Bool("icelite", false, "")
|
||||||
|
if err := viper.BindPFlag("icelite", cmd.PersistentFlags().Lookup("icelite")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.PersistentFlags().StringSlice("iceserver", []string{"stun:stun.l.google.com:19302"}, "")
|
||||||
|
if err := viper.BindPFlag("iceserver", cmd.PersistentFlags().Lookup("iceserver")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +135,9 @@ func (s *WebRTC) Set() {
|
|||||||
audioCodec = webrtc.PCMA
|
audioCodec = webrtc.PCMA
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.ICELite = viper.GetBool("icelite")
|
||||||
|
s.ICEServers = viper.GetStringSlice("iceserver")
|
||||||
|
|
||||||
s.Device = viper.GetString("device")
|
s.Device = viper.GetString("device")
|
||||||
s.AudioCodec = audioCodec
|
s.AudioCodec = audioCodec
|
||||||
s.AudioParams = viper.GetString("aparams")
|
s.AudioParams = viper.GetString("aparams")
|
||||||
|
@ -17,6 +17,8 @@ type SignalProvide struct {
|
|||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
SDP string `json:"sdp"`
|
SDP string `json:"sdp"`
|
||||||
|
Lite bool `json:"lite"`
|
||||||
|
ICE []string `json:"ice"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignalAnswer struct {
|
type SignalAnswer struct {
|
||||||
|
@ -8,7 +8,7 @@ type Sample struct {
|
|||||||
type WebRTCManager interface {
|
type WebRTCManager interface {
|
||||||
Start()
|
Start()
|
||||||
Shutdown() error
|
Shutdown() error
|
||||||
CreatePeer(id string, session Session) (string, error)
|
CreatePeer(id string, session Session) (string, bool, []string, error)
|
||||||
ChangeScreenSize(width int, height int, rate int) error
|
ChangeScreenSize(width int, height int, rate int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,8 +8,12 @@ import (
|
|||||||
|
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
id string
|
id string
|
||||||
|
api *webrtc.API
|
||||||
|
engine *webrtc.MediaEngine
|
||||||
manager *WebRTCManager
|
manager *WebRTCManager
|
||||||
|
settings *webrtc.SettingEngine
|
||||||
connection *webrtc.PeerConnection
|
connection *webrtc.PeerConnection
|
||||||
|
configuration *webrtc.Configuration
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"n.eko.moe/neko/internal/gst"
|
"n.eko.moe/neko/internal/gst"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pipelineSrc string) (*gst.Pipeline, *webrtc.Track, error) {
|
func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pipelineSrc string) (*gst.Pipeline, *webrtc.Track, *webrtc.RTPCodec, error) {
|
||||||
pipeline, err := gst.CreatePipeline(
|
pipeline, err := gst.CreatePipeline(
|
||||||
codecName,
|
codecName,
|
||||||
pipelineDevice,
|
pipelineDevice,
|
||||||
@ -16,7 +16,7 @@ func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pip
|
|||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var codec *webrtc.RTPCodec
|
var codec *webrtc.RTPCodec
|
||||||
@ -36,14 +36,13 @@ func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pip
|
|||||||
case webrtc.PCMA:
|
case webrtc.PCMA:
|
||||||
codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000)
|
codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000)
|
||||||
default:
|
default:
|
||||||
return nil, nil, fmt.Errorf("unknown codec %s", codecName)
|
return nil, nil, nil, fmt.Errorf("unknown codec %s", codecName)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.engine.RegisterCodec(codec)
|
|
||||||
track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec)
|
track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipeline, track, nil
|
return pipeline, track, codec, nil
|
||||||
}
|
}
|
||||||
|
@ -18,53 +18,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func New(sessions types.SessionManager, config *config.WebRTC) *WebRTCManager {
|
func New(sessions types.SessionManager, config *config.WebRTC) *WebRTCManager {
|
||||||
logger := log.With().Str("module", "webrtc").Logger()
|
|
||||||
settings := webrtc.SettingEngine{
|
|
||||||
LoggerFactory: loggerFactory{
|
|
||||||
logger: logger,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.SetLite(true)
|
|
||||||
settings.SetEphemeralUDPPortRange(config.EphemeralMin, config.EphemeralMax)
|
|
||||||
settings.SetNAT1To1IPs(config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
|
|
||||||
|
|
||||||
// Create MediaEngine based off sdp
|
|
||||||
engine := webrtc.MediaEngine{}
|
|
||||||
engine.RegisterDefaultCodecs()
|
|
||||||
|
|
||||||
// Create API with MediaEngine and SettingEngine
|
|
||||||
api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings))
|
|
||||||
|
|
||||||
return &WebRTCManager{
|
return &WebRTCManager{
|
||||||
logger: logger,
|
logger: log.With().Str("module", "webrtc").Logger(),
|
||||||
settings: settings,
|
|
||||||
cleanup: time.NewTicker(1 * time.Second),
|
cleanup: time.NewTicker(1 * time.Second),
|
||||||
shutdown: make(chan bool),
|
shutdown: make(chan bool),
|
||||||
sessions: sessions,
|
sessions: sessions,
|
||||||
engine: engine,
|
|
||||||
config: config,
|
config: config,
|
||||||
api: api,
|
|
||||||
configuration: &webrtc.Configuration{
|
|
||||||
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebRTCManager struct {
|
type WebRTCManager struct {
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
settings webrtc.SettingEngine
|
|
||||||
engine webrtc.MediaEngine
|
|
||||||
api *webrtc.API
|
|
||||||
videoTrack *webrtc.Track
|
videoTrack *webrtc.Track
|
||||||
audioTrack *webrtc.Track
|
audioTrack *webrtc.Track
|
||||||
videoPipeline *gst.Pipeline
|
videoPipeline *gst.Pipeline
|
||||||
audioPipeline *gst.Pipeline
|
audioPipeline *gst.Pipeline
|
||||||
|
videoCodec *webrtc.RTPCodec
|
||||||
|
audioCodec *webrtc.RTPCodec
|
||||||
sessions types.SessionManager
|
sessions types.SessionManager
|
||||||
cleanup *time.Ticker
|
cleanup *time.Ticker
|
||||||
config *config.WebRTC
|
config *config.WebRTC
|
||||||
shutdown chan bool
|
shutdown chan bool
|
||||||
configuration *webrtc.Configuration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WebRTCManager) Start() {
|
func (m *WebRTCManager) Start() {
|
||||||
@ -79,12 +53,12 @@ func (m *WebRTCManager) Start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
m.videoPipeline, m.videoTrack, err = m.createTrack(m.config.VideoCodec, m.config.Display, m.config.VideoParams)
|
m.videoPipeline, m.videoTrack, m.videoCodec, err = m.createTrack(m.config.VideoCodec, m.config.Display, m.config.VideoParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Panic().Err(err).Msg("unable to start webrtc manager")
|
m.logger.Panic().Err(err).Msg("unable to start webrtc manager")
|
||||||
}
|
}
|
||||||
|
|
||||||
m.audioPipeline, m.audioTrack, err = m.createTrack(m.config.AudioCodec, m.config.Device, m.config.AudioParams)
|
m.audioPipeline, m.audioTrack, m.audioCodec, err = m.createTrack(m.config.AudioCodec, m.config.Device, m.config.AudioParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Panic().Err(err).Msg("unable to start webrtc manager")
|
m.logger.Panic().Err(err).Msg("unable to start webrtc manager")
|
||||||
}
|
}
|
||||||
@ -133,10 +107,12 @@ func (m *WebRTCManager) Start() {
|
|||||||
Str("video_codec", m.config.VideoCodec).
|
Str("video_codec", m.config.VideoCodec).
|
||||||
Str("audio_device", m.config.Device).
|
Str("audio_device", m.config.Device).
|
||||||
Str("audio_codec", m.config.AudioCodec).
|
Str("audio_codec", m.config.AudioCodec).
|
||||||
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", m.config.EphemeralMin, m.config.EphemeralMax)).
|
|
||||||
Str("nat_ips", strings.Join(m.config.NAT1To1IPs, ",")).
|
|
||||||
Str("audio_pipeline_src", m.audioPipeline.Src).
|
Str("audio_pipeline_src", m.audioPipeline.Src).
|
||||||
Str("video_pipeline_src", m.videoPipeline.Src).
|
Str("video_pipeline_src", m.videoPipeline.Src).
|
||||||
|
Str("ice_lite", fmt.Sprintf("%t", m.config.ICELite)).
|
||||||
|
Str("ice_servers", strings.Join(m.config.ICEServers, ",")).
|
||||||
|
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", m.config.EphemeralMin, m.config.EphemeralMax)).
|
||||||
|
Str("nat_ips", strings.Join(m.config.NAT1To1IPs, ",")).
|
||||||
Msgf("webrtc streaming")
|
Msgf("webrtc streaming")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,28 +125,62 @@ func (m *WebRTCManager) Shutdown() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, error) {
|
func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bool, []string, error) {
|
||||||
|
configuration := &webrtc.Configuration{
|
||||||
|
ICEServers: []webrtc.ICEServer{
|
||||||
|
{
|
||||||
|
URLs: m.config.ICEServers,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
|
||||||
|
}
|
||||||
|
|
||||||
|
settings := webrtc.SettingEngine{
|
||||||
|
LoggerFactory: loggerFactory{
|
||||||
|
logger: m.logger,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.config.ICELite {
|
||||||
|
configuration = &webrtc.Configuration{
|
||||||
|
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
|
||||||
|
}
|
||||||
|
settings.SetLite(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.SetEphemeralUDPPortRange(m.config.EphemeralMin, m.config.EphemeralMax)
|
||||||
|
settings.SetNAT1To1IPs(m.config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
|
||||||
|
|
||||||
|
// Create MediaEngine based off sdp
|
||||||
|
engine := webrtc.MediaEngine{}
|
||||||
|
// engine.RegisterDefaultCodecs()
|
||||||
|
engine.RegisterCodec(m.audioCodec)
|
||||||
|
engine.RegisterCodec(m.videoCodec)
|
||||||
|
|
||||||
|
// Create API with MediaEngine and SettingEngine
|
||||||
|
api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings))
|
||||||
|
|
||||||
// Create new peer connection
|
// Create new peer connection
|
||||||
connection, err := m.api.NewPeerConnection(*m.configuration)
|
connection, err := api.NewPeerConnection(*configuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", m.config.ICELite, m.config.ICEServers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = connection.AddTransceiverFromTrack(m.videoTrack, webrtc.RtpTransceiverInit{
|
if _, err = connection.AddTransceiverFromTrack(m.videoTrack, webrtc.RtpTransceiverInit{
|
||||||
Direction: webrtc.RTPTransceiverDirectionSendonly,
|
Direction: webrtc.RTPTransceiverDirectionSendonly,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return "", err
|
return "", m.config.ICELite, m.config.ICEServers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = connection.AddTransceiverFromTrack(m.audioTrack, webrtc.RtpTransceiverInit{
|
if _, err = connection.AddTransceiverFromTrack(m.audioTrack, webrtc.RtpTransceiverInit{
|
||||||
Direction: webrtc.RTPTransceiverDirectionSendonly,
|
Direction: webrtc.RTPTransceiverDirectionSendonly,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return "", err
|
return "", m.config.ICELite, m.config.ICEServers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
description, err := connection.CreateOffer(nil)
|
description, err := connection.CreateOffer(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", m.config.ICELite, m.config.ICEServers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.OnDataChannel(func(d *webrtc.DataChannel) {
|
connection.OnDataChannel(func(d *webrtc.DataChannel) {
|
||||||
@ -201,13 +211,17 @@ func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, er
|
|||||||
|
|
||||||
if err := session.SetPeer(&Peer{
|
if err := session.SetPeer(&Peer{
|
||||||
id: id,
|
id: id,
|
||||||
|
api: api,
|
||||||
|
engine: &engine,
|
||||||
manager: m,
|
manager: m,
|
||||||
|
settings: &settings,
|
||||||
connection: connection,
|
connection: connection,
|
||||||
|
configuration: configuration,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return "", err
|
return "", m.config.ICELite, m.config.ICEServers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return description.SDP, nil
|
return description.SDP, m.config.ICELite, m.config.ICEServers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WebRTCManager) ChangeScreenSize(width int, height int, rate int) error {
|
func (m *WebRTCManager) ChangeScreenSize(width int, height int, rate int) error {
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (h *MessageHandler) signalProvide(id string, session types.Session) error {
|
func (h *MessageHandler) signalProvide(id string, session types.Session) error {
|
||||||
sdp, err := h.webrtc.CreatePeer(id, session)
|
sdp, lite, ice, err := h.webrtc.CreatePeer(id, session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -16,6 +16,8 @@ func (h *MessageHandler) signalProvide(id string, session types.Session) error {
|
|||||||
Event: event.SIGNAL_PROVIDE,
|
Event: event.SIGNAL_PROVIDE,
|
||||||
ID: id,
|
ID: id,
|
||||||
SDP: sdp,
|
SDP: sdp,
|
||||||
|
Lite: lite,
|
||||||
|
ICE: ice,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user