Class: WebRTC::RTCPeerConnection

Inherits:
Object
  • Object
show all
Defined in:
lib/webrtc/peer_connection.rb

Constant Summary collapse

SIGNALING_STATES =
%i[
  stable have_local_offer have_remote_offer have_local_pranswer have_remote_pranswer closed
].freeze
ICE_GATHERING_STATES =
%i[new gathering complete].freeze
ICE_CONNECTION_STATES =
%i[new checking connected completed failed disconnected closed].freeze
CONNECTION_STATES =
%i[new connecting connected disconnected failed closed].freeze
DIRECTION_TO_NATIVE =
{
  sendonly: 1,
  recvonly: 2,
  sendrecv: 3,
  inactive: 4
}.freeze
NATIVE_TO_DIRECTION =
{
  1 => :sendonly,
  2 => :recvonly,
  3 => :sendrecv,
  4 => :inactive
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(configuration = nil, factory: nil) ⇒ RTCPeerConnection

Returns a new instance of RTCPeerConnection.



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/webrtc/peer_connection.rb', line 28

def initialize(configuration = nil, factory: nil)
  @factory = factory
  @configuration = configuration || {}
  @callbacks = {}
  @senders = []
  @receivers = []
  @transceivers = []
  @data_channels = []
  @data_channels_opened = 0
  @data_channels_closed = 0
  @ptr = create_native_peer_connection
end

Instance Attribute Details

#local_descriptionObject (readonly)

Returns the value of attribute local_description.



26
27
28
# File 'lib/webrtc/peer_connection.rb', line 26

def local_description
  @local_description
end

#ptrObject (readonly)

Returns the value of attribute ptr.



26
27
28
# File 'lib/webrtc/peer_connection.rb', line 26

def ptr
  @ptr
end

#remote_descriptionObject (readonly)

Returns the value of attribute remote_description.



26
27
28
# File 'lib/webrtc/peer_connection.rb', line 26

def remote_description
  @remote_description
end

Instance Method Details

#add_ice_candidate(candidate, observer: nil) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/webrtc/peer_connection.rb', line 210

def add_ice_candidate(candidate, observer: nil)
  raise_if_closed!
  Promise.new do
    begin
      ice = normalize_ice_candidate(candidate)
      error = FFI::Error.new
      result = FFI.webrtc_peer_connection_add_ice_candidate(@ptr, ice.to_ptr, error)
      raise OperationError, error[:message] if result != 0

      observer&.on_success(nil) if observer.respond_to?(:on_success)
      nil
    rescue StandardError => e
      observer&.on_failure(e) if observer.respond_to?(:on_failure)
      raise e
    end
  end
end

#add_track(track, *streams) ⇒ Object



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/webrtc/peer_connection.rb', line 298

def add_track(track, *streams)
  raise_if_closed!
  raise InvalidParameterError, 'track is required' unless track

  native_track_id = try_add_native_track(track.kind, :sendrecv)

  sender = RTCRtpSender.new(track: track, native_track_id: native_track_id)
  receiver = RTCRtpReceiver.new(track: MediaStreamTrack.new(kind: track.kind), native_track_id: native_track_id)
  transceiver = RTCRtpTransceiver.new(
    sender: sender,
    receiver: receiver,
    direction: :sendrecv,
    native_track_id: native_track_id
  )

  @senders << sender
  @receivers << receiver
  @transceivers << transceiver
  @callbacks[:negotiation_needed]&.call

  sender
end

#add_transceiver(track_or_kind, init = {}) ⇒ Object



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/webrtc/peer_connection.rb', line 346

def add_transceiver(track_or_kind, init = {})
  raise_if_closed!

  track = if track_or_kind.is_a?(MediaStreamTrack)
            track_or_kind
          else
            MediaStreamTrack.new(kind: track_or_kind.to_sym)
          end

  direction = (init[:direction] || :sendrecv).to_sym
  native_track_id = try_add_native_track(track.kind, direction)

  sender = RTCRtpSender.new(track: track, native_track_id: native_track_id)
  receiver = RTCRtpReceiver.new(track: MediaStreamTrack.new(kind: track.kind), native_track_id: native_track_id)
  transceiver = RTCRtpTransceiver.new(
    sender: sender,
    receiver: receiver,
    direction: direction,
    native_track_id: native_track_id
  )

  @senders << sender
  @receivers << receiver
  @transceivers << transceiver
  @callbacks[:negotiation_needed]&.call

  transceiver
end

#closeObject



61
62
63
64
65
66
# File 'lib/webrtc/peer_connection.rb', line 61

def close
  return if @ptr.nil?

  FFI.webrtc_peer_connection_destroy(@ptr)
  @ptr = nil
end

#closed?Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/webrtc/peer_connection.rb', line 68

def closed?
  @ptr.nil?
end

#connection_stateObject



56
57
58
59
# File 'lib/webrtc/peer_connection.rb', line 56

def connection_state
  state_index = FFI.webrtc_peer_connection_get_connection_state(@ptr)
  CONNECTION_STATES[state_index] || :unknown
end

#create_answer(options = {}, observer: nil) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/webrtc/peer_connection.rb', line 151

def create_answer(options = {}, observer: nil)
  raise_if_closed!
  observer ||= options.is_a?(Hash) ? options[:observer] : nil

  Promise.new do
    begin
      sdp_ptr = ::FFI::MemoryPointer.new(:pointer)
      error = FFI::Error.new
      result = FFI.webrtc_peer_connection_create_answer(@ptr, sdp_ptr, error)
      raise OperationError, error[:message] if result != 0

      description = RTCSessionDescription.from_ptr(sdp_ptr.read_pointer)
      observer&.on_success(description) if observer.respond_to?(:on_success)
      description
    rescue StandardError => e
      observer&.on_failure(e) if observer.respond_to?(:on_failure)
      raise e
    end
  end
end

#create_data_channel(label, options = {}) ⇒ Object

Raises:



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/webrtc/peer_connection.rb', line 228

def create_data_channel(label, options = {})
  raise_if_closed!

  init = options.is_a?(DataChannelInit) ? options.to_h : options
  ordered = init.fetch(:ordered, true)
  max_retransmits = init[:max_retransmits] || -1
  max_packet_life_time = init[:max_packet_life_time] || -1
  protocol = init[:protocol] || ''
  negotiated = init.fetch(:negotiated, false)
  id = init[:id] || -1

  error = FFI::Error.new
  dc_ptr = FFI.webrtc_peer_connection_create_data_channel(
    @ptr,
    label,
    ordered,
    max_retransmits,
    max_packet_life_time,
    protocol,
    negotiated,
    id,
    error
  )

  raise OperationError, error[:message] if dc_ptr.nil? || dc_ptr.null?

  channel = RTCDataChannel.new(dc_ptr, init.merge(label: label))
  track_data_channel(channel)
  channel
end

#create_offer(options = {}, observer: nil) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/webrtc/peer_connection.rb', line 130

def create_offer(options = {}, observer: nil)
  raise_if_closed!
  observer ||= options.is_a?(Hash) ? options[:observer] : nil

  Promise.new do
    begin
      sdp_ptr = ::FFI::MemoryPointer.new(:pointer)
      error = FFI::Error.new
      result = FFI.webrtc_peer_connection_create_offer(@ptr, sdp_ptr, error)
      raise OperationError, error[:message] if result != 0

      description = RTCSessionDescription.from_ptr(sdp_ptr.read_pointer)
      observer&.on_success(description) if observer.respond_to?(:on_success)
      description
    rescue StandardError => e
      observer&.on_failure(e) if observer.respond_to?(:on_failure)
      raise e
    end
  end
end

#get_configurationObject



76
77
78
# File 'lib/webrtc/peer_connection.rb', line 76

def get_configuration
  @configuration.dup
end

#get_receiversObject



338
339
340
# File 'lib/webrtc/peer_connection.rb', line 338

def get_receivers
  @receivers.dup
end

#get_sendersObject



334
335
336
# File 'lib/webrtc/peer_connection.rb', line 334

def get_senders
  @senders.dup
end

#get_stats(selector = nil, callback: nil) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/webrtc/peer_connection.rb', line 84

def get_stats(selector = nil, callback: nil)
  raise_if_closed!
  Promise.new do
    stats = {}
    total_bytes_sent = 0
    total_bytes_received = 0

    pc_stats = RTCPeerConnectionStats.new(
      data_channels_opened: @data_channels_opened,
      data_channels_closed: @data_channels_closed
    )
    stats[pc_stats.id] = pc_stats

    @data_channels.each_with_index do |channel, index|
      snapshot = channel.stats_snapshot
      total_bytes_sent += snapshot[:bytes_sent]
      total_bytes_received += snapshot[:bytes_received]

      data_channel_stats = RTCDataChannelStats.new(
        id: "data-channel-#{index}-#{channel.id || index}",
        label: channel.label,
        protocol: channel.protocol,
        data_channel_identifier: channel.id,
        state: snapshot[:state],
        messages_sent: snapshot[:messages_sent],
        bytes_sent: snapshot[:bytes_sent],
        messages_received: snapshot[:messages_received],
        bytes_received: snapshot[:bytes_received]
      )
      stats[data_channel_stats.id] = data_channel_stats
    end

    transport_stats = RTCTransportStats.new(
      bytes_sent: total_bytes_sent,
      bytes_received: total_bytes_received,
      dtls_state: sctp_transport.transport.state
    )
    stats[transport_stats.id] = transport_stats

    report = RTCStatsReport.new(stats)
    stats_response = RTCStatsResponse.new(report)
    callback&.on_stats_delivered(stats_response) if callback.respond_to?(:on_stats_delivered)
    report
  end
end

#get_transceiversObject



342
343
344
# File 'lib/webrtc/peer_connection.rb', line 342

def get_transceivers
  @transceivers.dup
end

#ice_connection_stateObject



51
52
53
54
# File 'lib/webrtc/peer_connection.rb', line 51

def ice_connection_state
  state_index = FFI.webrtc_peer_connection_get_ice_connection_state(@ptr)
  ICE_CONNECTION_STATES[state_index] || :unknown
end

#ice_gathering_stateObject



46
47
48
49
# File 'lib/webrtc/peer_connection.rb', line 46

def ice_gathering_state
  state_index = FFI.webrtc_peer_connection_get_ice_gathering_state(@ptr)
  ICE_GATHERING_STATES[state_index] || :unknown
end

#on_connection_state_change(&block) ⇒ Object



264
265
266
267
# File 'lib/webrtc/peer_connection.rb', line 264

def on_connection_state_change(&block)
  @callbacks[:connection_state_change] = block
  setup_connection_state_callback
end

#on_data_channel(&block) ⇒ Object



284
285
286
287
# File 'lib/webrtc/peer_connection.rb', line 284

def on_data_channel(&block)
  @callbacks[:data_channel] = block
  setup_data_channel_callback
end

#on_ice_candidate(&block) ⇒ Object



259
260
261
262
# File 'lib/webrtc/peer_connection.rb', line 259

def on_ice_candidate(&block)
  @callbacks[:ice_candidate] = block
  setup_ice_candidate_callback
end

#on_ice_connection_state_change(&block) ⇒ Object



279
280
281
282
# File 'lib/webrtc/peer_connection.rb', line 279

def on_ice_connection_state_change(&block)
  @callbacks[:ice_connection_state_change] = block
  setup_ice_connection_state_callback
end

#on_ice_gathering_state_change(&block) ⇒ Object



274
275
276
277
# File 'lib/webrtc/peer_connection.rb', line 274

def on_ice_gathering_state_change(&block)
  @callbacks[:ice_gathering_state_change] = block
  setup_ice_gathering_state_callback
end

#on_negotiation_needed(&block) ⇒ Object



289
290
291
# File 'lib/webrtc/peer_connection.rb', line 289

def on_negotiation_needed(&block)
  @callbacks[:negotiation_needed] = block
end

#on_signaling_state_change(&block) ⇒ Object



269
270
271
272
# File 'lib/webrtc/peer_connection.rb', line 269

def on_signaling_state_change(&block)
  @callbacks[:signaling_state_change] = block
  setup_signaling_state_callback
end

#on_track(&block) ⇒ Object



293
294
295
296
# File 'lib/webrtc/peer_connection.rb', line 293

def on_track(&block)
  @callbacks[:track] = block
  setup_track_callback
end

#remove_track(sender) ⇒ Object



321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/webrtc/peer_connection.rb', line 321

def remove_track(sender)
  raise_if_closed!
  return unless @senders.include?(sender)

  if sender.native_track_id
    error = FFI::Error.new
    FFI.webrtc_peer_connection_remove_track(@ptr, sender.native_track_id, error)
  end

  sender.replace_track(nil)
  @callbacks[:negotiation_needed]&.call
end

#sctp_transportObject



72
73
74
# File 'lib/webrtc/peer_connection.rb', line 72

def sctp_transport
  @sctp_transport ||= RTCSctpTransport.new
end

#set_configuration(configuration) ⇒ Object



80
81
82
# File 'lib/webrtc/peer_connection.rb', line 80

def set_configuration(configuration)
  @configuration = configuration || {}
end

#set_local_description(description, observer: nil) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/webrtc/peer_connection.rb', line 172

def set_local_description(description, observer: nil)
  raise_if_closed!
  Promise.new do
    begin
      desc = normalize_description(description)
      error = FFI::Error.new
      result = FFI.webrtc_peer_connection_set_local_description(@ptr, desc.to_ptr, error)
      raise OperationError, error[:message] if result != 0

      @local_description = desc
      observer&.on_success(nil) if observer.respond_to?(:on_success)
      nil
    rescue StandardError => e
      observer&.on_failure(e) if observer.respond_to?(:on_failure)
      raise e
    end
  end
end

#set_remote_description(description, observer: nil) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/webrtc/peer_connection.rb', line 191

def set_remote_description(description, observer: nil)
  raise_if_closed!
  Promise.new do
    begin
      desc = normalize_description(description)
      error = FFI::Error.new
      result = FFI.webrtc_peer_connection_set_remote_description(@ptr, desc.to_ptr, error)
      raise OperationError, error[:message] if result != 0

      @remote_description = desc
      observer&.on_success(nil) if observer.respond_to?(:on_success)
      nil
    rescue StandardError => e
      observer&.on_failure(e) if observer.respond_to?(:on_failure)
      raise e
    end
  end
end

#signaling_stateObject



41
42
43
44
# File 'lib/webrtc/peer_connection.rb', line 41

def signaling_state
  state_index = FFI.webrtc_peer_connection_get_signaling_state(@ptr)
  SIGNALING_STATES[state_index] || :unknown
end