webrtc-ruby 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +19 -0
  3. data/.rspec +3 -0
  4. data/CHANGELOG.md +12 -0
  5. data/Dockerfile +49 -0
  6. data/LICENSE +201 -0
  7. data/README.md +264 -0
  8. data/Rakefile +42 -0
  9. data/examples/signaling_server/server.rb +200 -0
  10. data/examples/simple_data_channel.rb +81 -0
  11. data/examples/video_call.rb +152 -0
  12. data/ext/webrtc_ruby/CMakeLists.txt +84 -0
  13. data/ext/webrtc_ruby/Makefile +31 -0
  14. data/ext/webrtc_ruby/webrtc_ruby.c +994 -0
  15. data/ext/webrtc_ruby/webrtc_ruby.h +212 -0
  16. data/lib/webrtc/configuration.rb +99 -0
  17. data/lib/webrtc/data_channel.rb +216 -0
  18. data/lib/webrtc/dtls_transport.rb +54 -0
  19. data/lib/webrtc/dtmf_sender.rb +81 -0
  20. data/lib/webrtc/errors.rb +10 -0
  21. data/lib/webrtc/factory.rb +28 -0
  22. data/lib/webrtc/ffi/library.rb +122 -0
  23. data/lib/webrtc/ice_candidate.rb +63 -0
  24. data/lib/webrtc/ice_transport.rb +95 -0
  25. data/lib/webrtc/media_interfaces.rb +101 -0
  26. data/lib/webrtc/media_stream.rb +67 -0
  27. data/lib/webrtc/media_stream_track.rb +83 -0
  28. data/lib/webrtc/observers.rb +51 -0
  29. data/lib/webrtc/parity_types.rb +358 -0
  30. data/lib/webrtc/peer_connection.rb +577 -0
  31. data/lib/webrtc/promise.rb +59 -0
  32. data/lib/webrtc/rtp_receiver.rb +79 -0
  33. data/lib/webrtc/rtp_sender.rb +117 -0
  34. data/lib/webrtc/rtp_transceiver.rb +39 -0
  35. data/lib/webrtc/sctp_transport.rb +31 -0
  36. data/lib/webrtc/session_description.rb +65 -0
  37. data/lib/webrtc/stats_report.rb +199 -0
  38. data/lib/webrtc/version.rb +5 -0
  39. data/lib/webrtc/video_frame.rb +29 -0
  40. data/lib/webrtc.rb +43 -0
  41. data/webrtc-ruby.gemspec +33 -0
  42. metadata +113 -0
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ class RTCRtpSender
5
+ attr_reader :track, :transport, :dtmf, :native_track_id
6
+
7
+ def initialize(options = {})
8
+ @track = options[:track]
9
+ @transport = options[:transport]
10
+ @ptr = options[:ptr]
11
+ @native_track_id = options[:native_track_id]
12
+ @parameters = default_parameters
13
+ @dtmf = create_dtmf_sender
14
+ end
15
+
16
+ private def create_dtmf_sender
17
+ return nil unless @track&.kind == :audio
18
+
19
+ RTCDTMFSender.new(sender: self)
20
+ end
21
+
22
+ public
23
+
24
+ def self.get_capabilities(kind)
25
+ typed = get_typed_capabilities(kind)
26
+ {
27
+ codecs: typed.codecs.map do |codec|
28
+ { mime_type: codec.mime_type, clock_rate: codec.clock_rate, channels: codec.channels }
29
+ end,
30
+ header_extensions: typed.header_extensions.map(&:uri)
31
+ }
32
+ end
33
+
34
+ def self.get_typed_capabilities(kind)
35
+ RTCRtpCapabilities.new(
36
+ codecs: default_codecs(kind).map do |codec|
37
+ RTCRtpCodecCapability.new(
38
+ mime_type: codec[:mime_type],
39
+ clock_rate: codec[:clock_rate],
40
+ channels: codec[:channels]
41
+ )
42
+ end,
43
+ header_extensions: []
44
+ )
45
+ end
46
+
47
+ def get_parameters
48
+ @parameters.dup
49
+ end
50
+
51
+ def get_typed_parameters
52
+ RTCRtpSendParameters.new(
53
+ transaction_id: @parameters[:transaction_id],
54
+ encodings: @parameters[:encodings],
55
+ header_extensions: @parameters[:header_extensions],
56
+ rtcp: RTCRtcpParameters.new(
57
+ cname: @parameters.dig(:rtcp, :cname).to_s,
58
+ reduced_size: !!@parameters.dig(:rtcp, :reduced_size)
59
+ ),
60
+ codecs: @parameters[:codecs]
61
+ )
62
+ end
63
+
64
+ def set_parameters(parameters)
65
+ @parameters = parameters.respond_to?(:to_h) ? parameters.to_h : parameters
66
+ Promise.resolve(nil)
67
+ end
68
+
69
+ def replace_track(new_track)
70
+ Promise.new do
71
+ @track = new_track
72
+ nil
73
+ end
74
+ end
75
+
76
+ def set_streams(*streams)
77
+ @streams = streams
78
+ end
79
+
80
+ def get_stats
81
+ Promise.resolve({})
82
+ end
83
+
84
+ private
85
+
86
+ def default_parameters
87
+ {
88
+ transaction_id: '',
89
+ encodings: [],
90
+ header_extensions: [],
91
+ rtcp: { cname: '', reduced_size: false },
92
+ codecs: []
93
+ }
94
+ end
95
+
96
+ def self.default_codecs(kind)
97
+ case kind.to_sym
98
+ when :audio
99
+ [
100
+ { mime_type: 'audio/opus', clock_rate: 48_000, channels: 2 },
101
+ { mime_type: 'audio/PCMU', clock_rate: 8000, channels: 1 },
102
+ { mime_type: 'audio/PCMA', clock_rate: 8000, channels: 1 }
103
+ ]
104
+ when :video
105
+ [
106
+ { mime_type: 'video/VP8', clock_rate: 90_000 },
107
+ { mime_type: 'video/VP9', clock_rate: 90_000 },
108
+ { mime_type: 'video/H264', clock_rate: 90_000 }
109
+ ]
110
+ else
111
+ []
112
+ end
113
+ end
114
+
115
+ public
116
+ end
117
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ class RTCRtpTransceiver
5
+ DIRECTIONS = %i[sendrecv sendonly recvonly inactive stopped].freeze
6
+
7
+ attr_reader :mid, :sender, :receiver, :stopped, :current_direction, :native_track_id
8
+ attr_accessor :direction
9
+
10
+ def initialize(options = {})
11
+ @mid = options[:mid]
12
+ @sender = options[:sender] || RTCRtpSender.new
13
+ @receiver = options[:receiver] || RTCRtpReceiver.new
14
+ @direction = options[:direction] || :sendrecv
15
+ @current_direction = nil
16
+ @stopped = false
17
+ @ptr = options[:ptr]
18
+ @native_track_id = options[:native_track_id]
19
+ end
20
+
21
+ def stop
22
+ @stopped = true
23
+ @direction = :stopped
24
+ @sender&.replace_track(nil)
25
+ end
26
+
27
+ def set_codec_preferences(codecs)
28
+ @codec_preferences = codecs
29
+ end
30
+
31
+ def stopped?
32
+ @stopped
33
+ end
34
+ end
35
+
36
+ RTPTransceiver = RTCRtpTransceiver
37
+ RTPTransceiverDirection = RTCRtpTransceiver::DIRECTIONS
38
+ RTCRtpTransceiverDirection = RTCRtpTransceiver::DIRECTIONS
39
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ class RTCSctpTransport
5
+ STATES = %i[connecting connected closed].freeze
6
+
7
+ attr_reader :transport, :max_message_size, :max_channels, :state
8
+
9
+ def initialize(options = {})
10
+ @transport = options[:transport] || RTCDtlsTransport.new
11
+ @state = :connecting
12
+ @max_message_size = options[:max_message_size] || 262_144
13
+ @max_channels = options[:max_channels] || 65_535
14
+ @callbacks = {}
15
+ @ptr = options[:ptr]
16
+ end
17
+
18
+ def on_state_change(&block)
19
+ @callbacks[:state_change] = block
20
+ end
21
+
22
+ private
23
+
24
+ def set_state(new_state)
25
+ return if @state == new_state
26
+
27
+ @state = new_state
28
+ @callbacks[:state_change]&.call
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ class RTCSessionDescription
5
+ TYPES = %i[offer answer pranswer rollback].freeze
6
+
7
+ attr_reader :type, :sdp
8
+
9
+ def initialize(init = {})
10
+ init = init.to_h if init.respond_to?(:to_h)
11
+ @type = normalize_type(init[:type])
12
+ @sdp = init[:sdp] || ''
13
+ @ptr = nil
14
+
15
+ validate!
16
+ end
17
+
18
+ def self.from_ptr(ptr)
19
+ return nil if ptr.nil? || ptr.null?
20
+
21
+ type_str = FFI.webrtc_session_description_get_type(ptr)
22
+ sdp_str = FFI.webrtc_session_description_get_sdp(ptr)
23
+
24
+ desc = new(type: type_str&.to_sym, sdp: sdp_str)
25
+ desc.instance_variable_set(:@ptr, ptr)
26
+ desc
27
+ end
28
+
29
+ def to_ptr
30
+ return @ptr if @ptr && !@ptr.null?
31
+
32
+ error = FFI::Error.new
33
+ @ptr = FFI.webrtc_session_description_create(type.to_s, sdp, error)
34
+ raise OperationError, error[:message] if error[:code] != 0
35
+
36
+ @ptr
37
+ end
38
+
39
+ def to_h
40
+ { type: type, sdp: sdp }
41
+ end
42
+
43
+ def release
44
+ return unless @ptr && !@ptr.null?
45
+
46
+ FFI.webrtc_session_description_destroy(@ptr)
47
+ @ptr = nil
48
+ end
49
+
50
+ private
51
+
52
+ def normalize_type(type)
53
+ return nil if type.nil?
54
+
55
+ type.to_sym
56
+ end
57
+
58
+ def validate!
59
+ return if type.nil?
60
+ return if TYPES.include?(type)
61
+
62
+ raise InvalidParameterError, "Invalid session description type: #{type}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ class RTCStatsReport
5
+ include Enumerable
6
+
7
+ def initialize(stats = {})
8
+ @stats = stats
9
+ end
10
+
11
+ def [](id)
12
+ @stats[id]
13
+ end
14
+
15
+ def each(&block)
16
+ @stats.each(&block)
17
+ end
18
+
19
+ def size
20
+ @stats.size
21
+ end
22
+
23
+ def keys
24
+ @stats.keys
25
+ end
26
+
27
+ def values
28
+ @stats.values
29
+ end
30
+
31
+ def get(id)
32
+ @stats[id]
33
+ end
34
+
35
+ def has?(id)
36
+ @stats.key?(id)
37
+ end
38
+
39
+ def to_h
40
+ @stats.dup
41
+ end
42
+ end
43
+
44
+ class RTCStats
45
+ TYPES = %i[
46
+ codec inbound_rtp outbound_rtp remote_inbound_rtp remote_outbound_rtp
47
+ media_source csrc peer_connection data_channel transceiver sender
48
+ receiver transport candidate_pair local_candidate remote_candidate
49
+ certificate ice_server
50
+ ].freeze
51
+
52
+ attr_reader :id, :timestamp, :type
53
+
54
+ def initialize(options = {})
55
+ @id = options[:id] || generate_id
56
+ @timestamp = options[:timestamp] || Time.now.to_f * 1000
57
+ @type = options[:type]
58
+ end
59
+
60
+ def to_h
61
+ {
62
+ id: @id,
63
+ timestamp: @timestamp,
64
+ type: @type
65
+ }
66
+ end
67
+
68
+ private
69
+
70
+ def generate_id
71
+ "stats-#{SecureRandom.uuid}"
72
+ end
73
+ end
74
+
75
+ class RTCInboundRtpStreamStats < RTCStats
76
+ attr_reader :ssrc, :kind, :packets_received, :bytes_received, :packets_lost, :jitter, :frames_decoded,
77
+ :frames_dropped
78
+
79
+ def initialize(options = {})
80
+ super(options.merge(type: :inbound_rtp))
81
+ @ssrc = options[:ssrc]
82
+ @kind = options[:kind]
83
+ @packets_received = options[:packets_received] || 0
84
+ @bytes_received = options[:bytes_received] || 0
85
+ @packets_lost = options[:packets_lost] || 0
86
+ @jitter = options[:jitter] || 0.0
87
+ @frames_decoded = options[:frames_decoded] || 0
88
+ @frames_dropped = options[:frames_dropped] || 0
89
+ end
90
+
91
+ def to_h
92
+ super.merge(
93
+ ssrc: @ssrc,
94
+ kind: @kind,
95
+ packetsReceived: @packets_received,
96
+ bytesReceived: @bytes_received,
97
+ packetsLost: @packets_lost,
98
+ jitter: @jitter,
99
+ framesDecoded: @frames_decoded,
100
+ framesDropped: @frames_dropped
101
+ )
102
+ end
103
+ end
104
+
105
+ class RTCOutboundRtpStreamStats < RTCStats
106
+ attr_reader :ssrc, :kind, :packets_sent, :bytes_sent, :frames_encoded, :target_bitrate, :frames_per_second
107
+
108
+ def initialize(options = {})
109
+ super(options.merge(type: :outbound_rtp))
110
+ @ssrc = options[:ssrc]
111
+ @kind = options[:kind]
112
+ @packets_sent = options[:packets_sent] || 0
113
+ @bytes_sent = options[:bytes_sent] || 0
114
+ @frames_encoded = options[:frames_encoded] || 0
115
+ @target_bitrate = options[:target_bitrate]
116
+ @frames_per_second = options[:frames_per_second]
117
+ end
118
+
119
+ def to_h
120
+ super.merge(
121
+ ssrc: @ssrc,
122
+ kind: @kind,
123
+ packetsSent: @packets_sent,
124
+ bytesSent: @bytes_sent,
125
+ framesEncoded: @frames_encoded,
126
+ targetBitrate: @target_bitrate,
127
+ framesPerSecond: @frames_per_second
128
+ )
129
+ end
130
+ end
131
+
132
+ class RTCTransportStats < RTCStats
133
+ attr_reader :bytes_sent, :bytes_received, :dtls_state, :selected_candidate_pair_id
134
+
135
+ def initialize(options = {})
136
+ super(options.merge(type: :transport))
137
+ @bytes_sent = options[:bytes_sent] || 0
138
+ @bytes_received = options[:bytes_received] || 0
139
+ @dtls_state = options[:dtls_state] || :new
140
+ @selected_candidate_pair_id = options[:selected_candidate_pair_id]
141
+ end
142
+
143
+ def to_h
144
+ super.merge(
145
+ bytesSent: @bytes_sent,
146
+ bytesReceived: @bytes_received,
147
+ dtlsState: @dtls_state,
148
+ selectedCandidatePairId: @selected_candidate_pair_id
149
+ )
150
+ end
151
+ end
152
+
153
+ class RTCPeerConnectionStats < RTCStats
154
+ attr_reader :data_channels_opened, :data_channels_closed
155
+
156
+ def initialize(options = {})
157
+ super(options.merge(type: :peer_connection))
158
+ @data_channels_opened = options[:data_channels_opened] || 0
159
+ @data_channels_closed = options[:data_channels_closed] || 0
160
+ end
161
+
162
+ def to_h
163
+ super.merge(
164
+ dataChannelsOpened: @data_channels_opened,
165
+ dataChannelsClosed: @data_channels_closed
166
+ )
167
+ end
168
+ end
169
+
170
+ class RTCDataChannelStats < RTCStats
171
+ attr_reader :label, :protocol, :data_channel_identifier, :state, :messages_sent, :bytes_sent, :messages_received,
172
+ :bytes_received
173
+
174
+ def initialize(options = {})
175
+ super(options.merge(type: :data_channel))
176
+ @label = options[:label]
177
+ @protocol = options[:protocol]
178
+ @data_channel_identifier = options[:data_channel_identifier]
179
+ @state = options[:state] || :connecting
180
+ @messages_sent = options[:messages_sent] || 0
181
+ @bytes_sent = options[:bytes_sent] || 0
182
+ @messages_received = options[:messages_received] || 0
183
+ @bytes_received = options[:bytes_received] || 0
184
+ end
185
+
186
+ def to_h
187
+ super.merge(
188
+ label: @label,
189
+ protocol: @protocol,
190
+ dataChannelIdentifier: @data_channel_identifier,
191
+ state: @state,
192
+ messagesSent: @messages_sent,
193
+ bytesSent: @bytes_sent,
194
+ messagesReceived: @messages_received,
195
+ bytesReceived: @bytes_received
196
+ )
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebRTC
4
+ class I420Buffer
5
+ attr_reader :width, :height, :stride_y, :stride_u, :stride_v
6
+ attr_accessor :data_y, :data_u, :data_v
7
+
8
+ def initialize(width:, height:, data_y: nil, data_u: nil, data_v: nil, stride_y: nil, stride_u: nil, stride_v: nil)
9
+ @width = width
10
+ @height = height
11
+ @stride_y = stride_y || width
12
+ @stride_u = stride_u || (width / 2)
13
+ @stride_v = stride_v || (width / 2)
14
+ @data_y = data_y || "\x00".b * (@stride_y * @height)
15
+ @data_u = data_u || "\x80".b * (@stride_u * (@height / 2))
16
+ @data_v = data_v || "\x80".b * (@stride_v * (@height / 2))
17
+ end
18
+ end
19
+
20
+ class VideoFrame
21
+ attr_reader :buffer, :timestamp_us, :rotation
22
+
23
+ def initialize(buffer:, timestamp_us: nil, rotation: 0)
24
+ @buffer = buffer
25
+ @timestamp_us = timestamp_us || (Time.now.to_f * 1_000_000).to_i
26
+ @rotation = rotation
27
+ end
28
+ end
29
+ end
data/lib/webrtc.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ require_relative 'webrtc/version'
6
+ require_relative 'webrtc/errors'
7
+ require_relative 'webrtc/ffi/library'
8
+ require_relative 'webrtc/promise'
9
+ require_relative 'webrtc/parity_types'
10
+ require_relative 'webrtc/observers'
11
+ require_relative 'webrtc/session_description'
12
+ require_relative 'webrtc/ice_candidate'
13
+ require_relative 'webrtc/media_stream_track'
14
+ require_relative 'webrtc/media_stream'
15
+ require_relative 'webrtc/media_interfaces'
16
+ require_relative 'webrtc/video_frame'
17
+ require_relative 'webrtc/rtp_sender'
18
+ require_relative 'webrtc/rtp_receiver'
19
+ require_relative 'webrtc/rtp_transceiver'
20
+ require_relative 'webrtc/ice_transport'
21
+ require_relative 'webrtc/dtls_transport'
22
+ require_relative 'webrtc/sctp_transport'
23
+ require_relative 'webrtc/stats_report'
24
+ require_relative 'webrtc/dtmf_sender'
25
+ require_relative 'webrtc/configuration'
26
+ require_relative 'webrtc/data_channel'
27
+ require_relative 'webrtc/peer_connection'
28
+ require_relative 'webrtc/factory'
29
+
30
+ module WebRTC
31
+ class << self
32
+ def init
33
+ result = FFI.webrtc_init
34
+ raise InitializationError, 'Failed to initialize WebRTC' unless result.zero?
35
+
36
+ true
37
+ end
38
+
39
+ def cleanup
40
+ FFI.webrtc_cleanup
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/webrtc/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'webrtc-ruby'
7
+ spec.version = WebRTC::VERSION
8
+ spec.authors = ['Yudai Takada']
9
+ spec.email = ['t.yudai92@gmail.com']
10
+
11
+ spec.summary = 'WebRTC bindings for Ruby, providing real-time audio, video, and data channel capabilities.'
12
+ spec.description = 'WebRTC (Web Real-Time Communication) bindings for Ruby, providing real-time audio, video, and data channel capabilities.'
13
+ spec.homepage = 'https://github.com/ydah/webrtc-ruby'
14
+ spec.license = 'Apache-2.0'
15
+ spec.required_ruby_version = '>= 3.1.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = spec.homepage
19
+ spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
20
+
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (File.expand_path(f) == __FILE__) ||
24
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
25
+ end
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_dependency 'concurrent-ruby', '~> 1.2'
32
+ spec.add_dependency 'ffi', '~> 1.15'
33
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webrtc-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: concurrent-ruby
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ffi
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.15'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.15'
40
+ description: WebRTC (Web Real-Time Communication) bindings for Ruby, providing real-time
41
+ audio, video, and data channel capabilities.
42
+ email:
43
+ - t.yudai92@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".dockerignore"
49
+ - ".rspec"
50
+ - CHANGELOG.md
51
+ - Dockerfile
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - examples/signaling_server/server.rb
56
+ - examples/simple_data_channel.rb
57
+ - examples/video_call.rb
58
+ - ext/webrtc_ruby/CMakeLists.txt
59
+ - ext/webrtc_ruby/Makefile
60
+ - ext/webrtc_ruby/webrtc_ruby.c
61
+ - ext/webrtc_ruby/webrtc_ruby.h
62
+ - lib/webrtc.rb
63
+ - lib/webrtc/configuration.rb
64
+ - lib/webrtc/data_channel.rb
65
+ - lib/webrtc/dtls_transport.rb
66
+ - lib/webrtc/dtmf_sender.rb
67
+ - lib/webrtc/errors.rb
68
+ - lib/webrtc/factory.rb
69
+ - lib/webrtc/ffi/library.rb
70
+ - lib/webrtc/ice_candidate.rb
71
+ - lib/webrtc/ice_transport.rb
72
+ - lib/webrtc/media_interfaces.rb
73
+ - lib/webrtc/media_stream.rb
74
+ - lib/webrtc/media_stream_track.rb
75
+ - lib/webrtc/observers.rb
76
+ - lib/webrtc/parity_types.rb
77
+ - lib/webrtc/peer_connection.rb
78
+ - lib/webrtc/promise.rb
79
+ - lib/webrtc/rtp_receiver.rb
80
+ - lib/webrtc/rtp_sender.rb
81
+ - lib/webrtc/rtp_transceiver.rb
82
+ - lib/webrtc/sctp_transport.rb
83
+ - lib/webrtc/session_description.rb
84
+ - lib/webrtc/stats_report.rb
85
+ - lib/webrtc/version.rb
86
+ - lib/webrtc/video_frame.rb
87
+ - webrtc-ruby.gemspec
88
+ homepage: https://github.com/ydah/webrtc-ruby
89
+ licenses:
90
+ - Apache-2.0
91
+ metadata:
92
+ homepage_uri: https://github.com/ydah/webrtc-ruby
93
+ source_code_uri: https://github.com/ydah/webrtc-ruby
94
+ changelog_uri: https://github.com/ydah/webrtc-ruby/blob/main/CHANGELOG.md
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 3.1.0
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 4.0.6
110
+ specification_version: 4
111
+ summary: WebRTC bindings for Ruby, providing real-time audio, video, and data channel
112
+ capabilities.
113
+ test_files: []