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.
- checksums.yaml +7 -0
- data/.dockerignore +19 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +12 -0
- data/Dockerfile +49 -0
- data/LICENSE +201 -0
- data/README.md +264 -0
- data/Rakefile +42 -0
- data/examples/signaling_server/server.rb +200 -0
- data/examples/simple_data_channel.rb +81 -0
- data/examples/video_call.rb +152 -0
- data/ext/webrtc_ruby/CMakeLists.txt +84 -0
- data/ext/webrtc_ruby/Makefile +31 -0
- data/ext/webrtc_ruby/webrtc_ruby.c +994 -0
- data/ext/webrtc_ruby/webrtc_ruby.h +212 -0
- data/lib/webrtc/configuration.rb +99 -0
- data/lib/webrtc/data_channel.rb +216 -0
- data/lib/webrtc/dtls_transport.rb +54 -0
- data/lib/webrtc/dtmf_sender.rb +81 -0
- data/lib/webrtc/errors.rb +10 -0
- data/lib/webrtc/factory.rb +28 -0
- data/lib/webrtc/ffi/library.rb +122 -0
- data/lib/webrtc/ice_candidate.rb +63 -0
- data/lib/webrtc/ice_transport.rb +95 -0
- data/lib/webrtc/media_interfaces.rb +101 -0
- data/lib/webrtc/media_stream.rb +67 -0
- data/lib/webrtc/media_stream_track.rb +83 -0
- data/lib/webrtc/observers.rb +51 -0
- data/lib/webrtc/parity_types.rb +358 -0
- data/lib/webrtc/peer_connection.rb +577 -0
- data/lib/webrtc/promise.rb +59 -0
- data/lib/webrtc/rtp_receiver.rb +79 -0
- data/lib/webrtc/rtp_sender.rb +117 -0
- data/lib/webrtc/rtp_transceiver.rb +39 -0
- data/lib/webrtc/sctp_transport.rb +31 -0
- data/lib/webrtc/session_description.rb +65 -0
- data/lib/webrtc/stats_report.rb +199 -0
- data/lib/webrtc/version.rb +5 -0
- data/lib/webrtc/video_frame.rb +29 -0
- data/lib/webrtc.rb +43 -0
- data/webrtc-ruby.gemspec +33 -0
- metadata +113 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# WebRTC Signaling Server Example
|
|
5
|
+
#
|
|
6
|
+
# This is a conceptual example showing how to implement
|
|
7
|
+
# a WebRTC signaling server using WebSockets.
|
|
8
|
+
#
|
|
9
|
+
# In a real application, you would use a WebSocket library
|
|
10
|
+
# like 'faye-websocket' or 'websocket-eventmachine-server'.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# gem install faye-websocket puma
|
|
14
|
+
# ruby server.rb
|
|
15
|
+
|
|
16
|
+
require 'json'
|
|
17
|
+
|
|
18
|
+
# Simulated signaling server logic
|
|
19
|
+
class SignalingServer
|
|
20
|
+
def initialize
|
|
21
|
+
@rooms = {}
|
|
22
|
+
@clients = {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Handle incoming WebSocket message
|
|
26
|
+
def handle_message(client_id, message)
|
|
27
|
+
data = JSON.parse(message)
|
|
28
|
+
room_id = data['room']
|
|
29
|
+
|
|
30
|
+
case data['type']
|
|
31
|
+
when 'join'
|
|
32
|
+
join_room(client_id, room_id)
|
|
33
|
+
|
|
34
|
+
when 'offer'
|
|
35
|
+
# Forward offer to other peer in room
|
|
36
|
+
forward_to_peer(client_id, room_id, {
|
|
37
|
+
type: 'offer',
|
|
38
|
+
sdp: data['sdp']
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
when 'answer'
|
|
42
|
+
# Forward answer to other peer in room
|
|
43
|
+
forward_to_peer(client_id, room_id, {
|
|
44
|
+
type: 'answer',
|
|
45
|
+
sdp: data['sdp']
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
when 'ice-candidate'
|
|
49
|
+
# Forward ICE candidate to other peer
|
|
50
|
+
forward_to_peer(client_id, room_id, {
|
|
51
|
+
type: 'ice-candidate',
|
|
52
|
+
candidate: data['candidate'],
|
|
53
|
+
sdpMid: data['sdpMid'],
|
|
54
|
+
sdpMLineIndex: data['sdpMLineIndex']
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
when 'leave'
|
|
58
|
+
leave_room(client_id, room_id)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def join_room(client_id, room_id)
|
|
65
|
+
@rooms[room_id] ||= []
|
|
66
|
+
@rooms[room_id] << client_id
|
|
67
|
+
@clients[client_id] = room_id
|
|
68
|
+
|
|
69
|
+
# Notify if room has 2 peers (ready to connect)
|
|
70
|
+
return unless @rooms[room_id].size == 2
|
|
71
|
+
|
|
72
|
+
# Tell the first peer to create an offer
|
|
73
|
+
first_peer = @rooms[room_id].first
|
|
74
|
+
send_to_client(first_peer, { type: 'ready' })
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def leave_room(client_id, room_id)
|
|
78
|
+
@rooms[room_id]&.delete(client_id)
|
|
79
|
+
@clients.delete(client_id)
|
|
80
|
+
|
|
81
|
+
# Notify remaining peer
|
|
82
|
+
@rooms[room_id]&.each do |peer_id|
|
|
83
|
+
send_to_client(peer_id, { type: 'peer-left' })
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def forward_to_peer(from_client_id, room_id, message)
|
|
88
|
+
@rooms[room_id]&.each do |peer_id|
|
|
89
|
+
next if peer_id == from_client_id
|
|
90
|
+
|
|
91
|
+
send_to_client(peer_id, message)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def send_to_client(client_id, message)
|
|
96
|
+
# In a real implementation, this would send via WebSocket
|
|
97
|
+
puts "Sending to #{client_id}: #{message.to_json}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Example client-side pseudocode (would run in browser or Ruby client):
|
|
102
|
+
#
|
|
103
|
+
# class WebRTCClient
|
|
104
|
+
# def initialize(signaling_url, room_id)
|
|
105
|
+
# @room_id = room_id
|
|
106
|
+
# @pc = WebRTC::RTCPeerConnection.new
|
|
107
|
+
#
|
|
108
|
+
# # Connect to signaling server
|
|
109
|
+
# @ws = WebSocket.new(signaling_url)
|
|
110
|
+
#
|
|
111
|
+
# # Set up ICE candidate handling
|
|
112
|
+
# @pc.on_ice_candidate do |candidate|
|
|
113
|
+
# @ws.send({
|
|
114
|
+
# type: 'ice-candidate',
|
|
115
|
+
# room: @room_id,
|
|
116
|
+
# candidate: candidate.candidate,
|
|
117
|
+
# sdpMid: candidate.sdp_mid,
|
|
118
|
+
# sdpMLineIndex: candidate.sdp_m_line_index
|
|
119
|
+
# }.to_json)
|
|
120
|
+
# end
|
|
121
|
+
#
|
|
122
|
+
# # Handle signaling messages
|
|
123
|
+
# @ws.on_message do |msg|
|
|
124
|
+
# data = JSON.parse(msg)
|
|
125
|
+
# handle_signaling(data)
|
|
126
|
+
# end
|
|
127
|
+
# end
|
|
128
|
+
#
|
|
129
|
+
# def join
|
|
130
|
+
# @ws.send({ type: 'join', room: @room_id }.to_json)
|
|
131
|
+
# end
|
|
132
|
+
#
|
|
133
|
+
# def handle_signaling(data)
|
|
134
|
+
# case data['type']
|
|
135
|
+
# when 'ready'
|
|
136
|
+
# # We're the initiator, create offer
|
|
137
|
+
# create_and_send_offer
|
|
138
|
+
#
|
|
139
|
+
# when 'offer'
|
|
140
|
+
# # Received offer, create answer
|
|
141
|
+
# @pc.set_remote_description(type: :offer, sdp: data['sdp']).await
|
|
142
|
+
# answer = @pc.create_answer.await
|
|
143
|
+
# @pc.set_local_description(answer).await
|
|
144
|
+
#
|
|
145
|
+
# @ws.send({
|
|
146
|
+
# type: 'answer',
|
|
147
|
+
# room: @room_id,
|
|
148
|
+
# sdp: answer.sdp
|
|
149
|
+
# }.to_json)
|
|
150
|
+
#
|
|
151
|
+
# when 'answer'
|
|
152
|
+
# @pc.set_remote_description(type: :answer, sdp: data['sdp']).await
|
|
153
|
+
#
|
|
154
|
+
# when 'ice-candidate'
|
|
155
|
+
# candidate = WebRTC::RTCIceCandidate.new(
|
|
156
|
+
# candidate: data['candidate'],
|
|
157
|
+
# sdp_mid: data['sdpMid'],
|
|
158
|
+
# sdp_m_line_index: data['sdpMLineIndex']
|
|
159
|
+
# )
|
|
160
|
+
# @pc.add_ice_candidate(candidate).await
|
|
161
|
+
# end
|
|
162
|
+
# end
|
|
163
|
+
#
|
|
164
|
+
# def create_and_send_offer
|
|
165
|
+
# offer = @pc.create_offer.await
|
|
166
|
+
# @pc.set_local_description(offer).await
|
|
167
|
+
#
|
|
168
|
+
# @ws.send({
|
|
169
|
+
# type: 'offer',
|
|
170
|
+
# room: @room_id,
|
|
171
|
+
# sdp: offer.sdp
|
|
172
|
+
# }.to_json)
|
|
173
|
+
# end
|
|
174
|
+
# end
|
|
175
|
+
|
|
176
|
+
if __FILE__ == $0
|
|
177
|
+
puts 'WebRTC Signaling Server Example'
|
|
178
|
+
puts '================================'
|
|
179
|
+
puts
|
|
180
|
+
puts 'This is a conceptual example showing signaling server logic.'
|
|
181
|
+
puts 'In production, use with faye-websocket or similar WebSocket library.'
|
|
182
|
+
puts
|
|
183
|
+
puts 'Example signaling flow:'
|
|
184
|
+
puts "1. Client A joins room 'test'"
|
|
185
|
+
puts "2. Client B joins room 'test'"
|
|
186
|
+
puts '3. Server tells Client A to create offer'
|
|
187
|
+
puts '4. Client A sends offer to server'
|
|
188
|
+
puts '5. Server forwards offer to Client B'
|
|
189
|
+
puts '6. Client B creates and sends answer'
|
|
190
|
+
puts '7. Server forwards answer to Client A'
|
|
191
|
+
puts '8. Both clients exchange ICE candidates via server'
|
|
192
|
+
puts '9. P2P connection established!'
|
|
193
|
+
|
|
194
|
+
# Demo
|
|
195
|
+
server = SignalingServer.new
|
|
196
|
+
server.handle_message('client_a', '{"type":"join","room":"test"}')
|
|
197
|
+
server.handle_message('client_b', '{"type":"join","room":"test"}')
|
|
198
|
+
server.handle_message('client_a', '{"type":"offer","room":"test","sdp":"v=0..."}')
|
|
199
|
+
server.handle_message('client_b', '{"type":"answer","room":"test","sdp":"v=0..."}')
|
|
200
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Simple Data Channel Example
|
|
5
|
+
# This example demonstrates creating a data channel between two peers
|
|
6
|
+
# using the real libdatachannel WebRTC implementation.
|
|
7
|
+
|
|
8
|
+
require_relative '../lib/webrtc'
|
|
9
|
+
|
|
10
|
+
puts '=== WebRTC Ruby DataChannel Example ==='
|
|
11
|
+
puts
|
|
12
|
+
|
|
13
|
+
WebRTC.init
|
|
14
|
+
|
|
15
|
+
pc1 = WebRTC::RTCPeerConnection.new
|
|
16
|
+
pc2 = WebRTC::RTCPeerConnection.new
|
|
17
|
+
|
|
18
|
+
# Track ICE candidates for exchange after remote descriptions are set
|
|
19
|
+
pc1_candidates = []
|
|
20
|
+
pc2_candidates = []
|
|
21
|
+
|
|
22
|
+
pc1.on_ice_candidate do |candidate|
|
|
23
|
+
pc1_candidates << candidate if candidate
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
pc2.on_ice_candidate do |candidate|
|
|
27
|
+
pc2_candidates << candidate if candidate
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
pc2.on_data_channel do |dc|
|
|
31
|
+
puts "PC2: Received data channel: #{dc.label}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Create data channel on PC1
|
|
35
|
+
dc1 = pc1.create_data_channel('chat')
|
|
36
|
+
puts "Created data channel: #{dc1.label}"
|
|
37
|
+
|
|
38
|
+
dc1.on_open do
|
|
39
|
+
puts 'DataChannel opened!'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
dc1.on_message do |event|
|
|
43
|
+
puts "Received: #{event.data}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Step 1: PC1 creates offer
|
|
47
|
+
offer = pc1.create_offer.await
|
|
48
|
+
puts 'Created offer'
|
|
49
|
+
puts " SDP length: #{offer.sdp.length} bytes"
|
|
50
|
+
|
|
51
|
+
# Step 2: PC1 sets local description
|
|
52
|
+
pc1.set_local_description(offer).await
|
|
53
|
+
puts 'PC1: Set local description'
|
|
54
|
+
|
|
55
|
+
# Step 3: PC2 sets remote description (the offer)
|
|
56
|
+
pc2.set_remote_description(offer).await
|
|
57
|
+
puts 'PC2: Set remote description'
|
|
58
|
+
|
|
59
|
+
# Step 4: Now exchange ICE candidates (after remote descriptions are set)
|
|
60
|
+
puts 'Exchanging ICE candidates...'
|
|
61
|
+
pc1_candidates.each do |candidate|
|
|
62
|
+
pc2.add_ice_candidate(candidate).await
|
|
63
|
+
rescue StandardError
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
puts " Sent #{pc1_candidates.size} candidates from PC1 to PC2"
|
|
67
|
+
|
|
68
|
+
# NOTE: In a real application, you would wait for the connection to be established
|
|
69
|
+
# before sending data. For this demo, we just show the setup.
|
|
70
|
+
|
|
71
|
+
puts "\nSignaling complete!"
|
|
72
|
+
puts "DataChannel state: #{dc1.ready_state}"
|
|
73
|
+
puts ' (Channel will open when connection is established)'
|
|
74
|
+
|
|
75
|
+
# Cleanup
|
|
76
|
+
dc1.destroy
|
|
77
|
+
pc1.close
|
|
78
|
+
pc2.close
|
|
79
|
+
|
|
80
|
+
WebRTC.cleanup
|
|
81
|
+
puts "\nDone!"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# WebRTC Connection Example
|
|
5
|
+
# This example demonstrates a WebRTC connection between two peers
|
|
6
|
+
# with data channel and DTMF support simulation.
|
|
7
|
+
|
|
8
|
+
require_relative '../lib/webrtc'
|
|
9
|
+
|
|
10
|
+
puts '=== WebRTC Ruby Connection Example ==='
|
|
11
|
+
puts
|
|
12
|
+
|
|
13
|
+
# Initialize WebRTC
|
|
14
|
+
WebRTC.init
|
|
15
|
+
|
|
16
|
+
# Create two peer connections (simulating caller and callee)
|
|
17
|
+
caller_config = {
|
|
18
|
+
ice_servers: [
|
|
19
|
+
{ urls: 'stun:stun.l.google.com:19302' }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
caller_pc = WebRTC::RTCPeerConnection.new(caller_config)
|
|
24
|
+
callee_pc = WebRTC::RTCPeerConnection.new(caller_config)
|
|
25
|
+
|
|
26
|
+
puts 'Created peer connections'
|
|
27
|
+
|
|
28
|
+
# Set up ICE candidate exchange
|
|
29
|
+
caller_ice_candidates = []
|
|
30
|
+
callee_ice_candidates = []
|
|
31
|
+
|
|
32
|
+
caller_pc.on_ice_candidate do |candidate|
|
|
33
|
+
caller_ice_candidates << candidate if candidate
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
callee_pc.on_ice_candidate do |candidate|
|
|
37
|
+
callee_ice_candidates << candidate if candidate
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Monitor connection states
|
|
41
|
+
caller_pc.on_connection_state_change do |state|
|
|
42
|
+
puts "Caller connection state: #{state}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
callee_pc.on_connection_state_change do |state|
|
|
46
|
+
puts "Callee connection state: #{state}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Create data channel for signaling/messaging
|
|
50
|
+
puts "\nCreating data channel..."
|
|
51
|
+
data_channel = caller_pc.create_data_channel('messaging', ordered: true)
|
|
52
|
+
puts " Data channel created: #{data_channel.label}"
|
|
53
|
+
|
|
54
|
+
data_channel.on_open do
|
|
55
|
+
puts ' Data channel opened!'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
data_channel.on_message do |event|
|
|
59
|
+
puts " Received message: #{event.data}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Set up callee to receive data channels
|
|
63
|
+
callee_pc.on_data_channel do |dc|
|
|
64
|
+
puts "Callee received data channel: #{dc.label}"
|
|
65
|
+
dc.on_message do |event|
|
|
66
|
+
puts "Callee received: #{event.data}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Perform signaling
|
|
71
|
+
puts "\n=== Signaling Exchange ==="
|
|
72
|
+
|
|
73
|
+
# 1. Caller creates offer
|
|
74
|
+
puts "\n1. Caller creates offer..."
|
|
75
|
+
offer = caller_pc.create_offer.await
|
|
76
|
+
puts " Offer type: #{offer.type}"
|
|
77
|
+
puts " SDP length: #{offer.sdp.length} bytes"
|
|
78
|
+
|
|
79
|
+
# 2. Caller sets local description
|
|
80
|
+
puts "\n2. Caller sets local description..."
|
|
81
|
+
caller_pc.set_local_description(offer).await
|
|
82
|
+
puts ' Local description set'
|
|
83
|
+
|
|
84
|
+
# 3. Callee receives and sets remote description
|
|
85
|
+
puts "\n3. Callee sets remote description..."
|
|
86
|
+
callee_pc.set_remote_description(offer).await
|
|
87
|
+
puts ' Remote description set'
|
|
88
|
+
|
|
89
|
+
# 4. Exchange ICE candidates
|
|
90
|
+
puts "\n4. Exchanging ICE candidates..."
|
|
91
|
+
caller_ice_candidates.each do |candidate|
|
|
92
|
+
callee_pc.add_ice_candidate(candidate).await
|
|
93
|
+
rescue StandardError
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
puts " Sent #{caller_ice_candidates.size} candidates from caller to callee"
|
|
97
|
+
|
|
98
|
+
callee_ice_candidates.each do |candidate|
|
|
99
|
+
caller_pc.add_ice_candidate(candidate).await
|
|
100
|
+
rescue StandardError
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
puts " Sent #{callee_ice_candidates.size} candidates from callee to caller"
|
|
104
|
+
|
|
105
|
+
# Check status
|
|
106
|
+
puts "\n=== Connection Status ==="
|
|
107
|
+
puts "Caller signaling state: #{caller_pc.signaling_state}"
|
|
108
|
+
puts "Callee signaling state: #{callee_pc.signaling_state}"
|
|
109
|
+
puts "Data channel state: #{data_channel.ready_state}"
|
|
110
|
+
|
|
111
|
+
# Simulate DTMF (audio track simulation)
|
|
112
|
+
puts "\n=== DTMF Simulation ==="
|
|
113
|
+
audio_track = WebRTC::MediaStreamTrack.new(kind: :audio, label: 'virtual-audio')
|
|
114
|
+
sender = WebRTC::RTCRtpSender.new(track: audio_track)
|
|
115
|
+
|
|
116
|
+
if sender.dtmf
|
|
117
|
+
dtmf = sender.dtmf
|
|
118
|
+
puts "DTMF available: #{dtmf.can_insert_dtmf?}"
|
|
119
|
+
|
|
120
|
+
if dtmf.can_insert_dtmf?
|
|
121
|
+
dtmf.on_tone_change do |event|
|
|
122
|
+
puts " Playing tone: #{event.tone}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
puts 'Sending DTMF tones: 123#'
|
|
126
|
+
dtmf.insert_dtmf('123#', duration: 100, inter_tone_gap: 70)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Get statistics
|
|
131
|
+
puts "\n=== Connection Statistics ==="
|
|
132
|
+
stats = caller_pc.get_stats.await
|
|
133
|
+
puts "Stats entries: #{stats.size}"
|
|
134
|
+
stats.each do |id, stat|
|
|
135
|
+
case stat
|
|
136
|
+
when WebRTC::RTCPeerConnectionStats
|
|
137
|
+
puts ' PeerConnection Stats:'
|
|
138
|
+
puts " Data channels opened: #{stat.data_channels_opened}"
|
|
139
|
+
when WebRTC::RTCTransportStats
|
|
140
|
+
puts ' Transport Stats:'
|
|
141
|
+
puts " Bytes sent: #{stat.bytes_sent}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Cleanup
|
|
146
|
+
puts "\n=== Cleanup ==="
|
|
147
|
+
data_channel.destroy
|
|
148
|
+
caller_pc.close
|
|
149
|
+
callee_pc.close
|
|
150
|
+
WebRTC.cleanup
|
|
151
|
+
|
|
152
|
+
puts "\nExample completed!"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.14)
|
|
2
|
+
project(webrtc_ruby C)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_C_STANDARD 11)
|
|
5
|
+
set(CMAKE_C_STANDARD_REQUIRED ON)
|
|
6
|
+
|
|
7
|
+
if(NOT CMAKE_BUILD_TYPE)
|
|
8
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
9
|
+
endif()
|
|
10
|
+
|
|
11
|
+
set(CMAKE_C_FLAGS_DEBUG "-g -O0")
|
|
12
|
+
set(CMAKE_C_FLAGS_RELEASE "-O3")
|
|
13
|
+
|
|
14
|
+
add_compile_definitions(WEBRTC_RUBY_BUILDING)
|
|
15
|
+
|
|
16
|
+
if(APPLE)
|
|
17
|
+
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
|
18
|
+
endif()
|
|
19
|
+
|
|
20
|
+
# Find libdatachannel
|
|
21
|
+
set(LIBDATACHANNEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../vendor/libdatachannel")
|
|
22
|
+
|
|
23
|
+
if(EXISTS "${LIBDATACHANNEL_DIR}/build/libdatachannel.dylib" OR
|
|
24
|
+
EXISTS "${LIBDATACHANNEL_DIR}/build/libdatachannel.so")
|
|
25
|
+
message(STATUS "Found libdatachannel in vendor directory")
|
|
26
|
+
set(DATACHANNEL_INCLUDE_DIR "${LIBDATACHANNEL_DIR}/include")
|
|
27
|
+
set(DATACHANNEL_LIBRARY_DIR "${LIBDATACHANNEL_DIR}/build")
|
|
28
|
+
|
|
29
|
+
if(APPLE)
|
|
30
|
+
set(DATACHANNEL_LIBRARY "${DATACHANNEL_LIBRARY_DIR}/libdatachannel.dylib")
|
|
31
|
+
else()
|
|
32
|
+
set(DATACHANNEL_LIBRARY "${DATACHANNEL_LIBRARY_DIR}/libdatachannel.so")
|
|
33
|
+
endif()
|
|
34
|
+
else()
|
|
35
|
+
# Try pkg-config
|
|
36
|
+
find_package(PkgConfig QUIET)
|
|
37
|
+
if(PkgConfig_FOUND)
|
|
38
|
+
pkg_check_modules(DATACHANNEL libdatachannel)
|
|
39
|
+
endif()
|
|
40
|
+
|
|
41
|
+
if(NOT DATACHANNEL_FOUND)
|
|
42
|
+
message(FATAL_ERROR "libdatachannel not found. Build it first.")
|
|
43
|
+
endif()
|
|
44
|
+
endif()
|
|
45
|
+
|
|
46
|
+
add_library(webrtc_ruby SHARED
|
|
47
|
+
webrtc_ruby.c
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
target_include_directories(webrtc_ruby PUBLIC
|
|
51
|
+
${CMAKE_CURRENT_SOURCE_DIR}
|
|
52
|
+
${DATACHANNEL_INCLUDE_DIR}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
target_link_directories(webrtc_ruby PRIVATE
|
|
56
|
+
${DATACHANNEL_LIBRARY_DIR}
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
target_link_libraries(webrtc_ruby PRIVATE
|
|
60
|
+
${DATACHANNEL_LIBRARY}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if(APPLE)
|
|
64
|
+
set_target_properties(webrtc_ruby PROPERTIES
|
|
65
|
+
SUFFIX ".dylib"
|
|
66
|
+
INSTALL_RPATH "@loader_path"
|
|
67
|
+
BUILD_WITH_INSTALL_RPATH TRUE
|
|
68
|
+
)
|
|
69
|
+
elseif(WIN32)
|
|
70
|
+
set_target_properties(webrtc_ruby PROPERTIES
|
|
71
|
+
SUFFIX ".dll"
|
|
72
|
+
)
|
|
73
|
+
else()
|
|
74
|
+
set_target_properties(webrtc_ruby PROPERTIES
|
|
75
|
+
SUFFIX ".so"
|
|
76
|
+
INSTALL_RPATH "$ORIGIN"
|
|
77
|
+
BUILD_WITH_INSTALL_RPATH TRUE
|
|
78
|
+
)
|
|
79
|
+
endif()
|
|
80
|
+
|
|
81
|
+
install(TARGETS webrtc_ruby
|
|
82
|
+
LIBRARY DESTINATION lib
|
|
83
|
+
RUNTIME DESTINATION bin
|
|
84
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
CC = cc
|
|
2
|
+
CFLAGS = -Wall -Wextra -fPIC -O2 -DWEBRTC_RUBY_BUILDING
|
|
3
|
+
LDFLAGS = -shared
|
|
4
|
+
|
|
5
|
+
UNAME_S := $(shell uname -s)
|
|
6
|
+
ifeq ($(UNAME_S),Darwin)
|
|
7
|
+
TARGET = libwebrtc_ruby.dylib
|
|
8
|
+
LDFLAGS += -dynamiclib -install_name @rpath/$(TARGET)
|
|
9
|
+
else ifeq ($(OS),Windows_NT)
|
|
10
|
+
TARGET = webrtc_ruby.dll
|
|
11
|
+
else
|
|
12
|
+
TARGET = libwebrtc_ruby.so
|
|
13
|
+
endif
|
|
14
|
+
|
|
15
|
+
SOURCES = webrtc_ruby.c
|
|
16
|
+
OBJECTS = $(SOURCES:.c=.o)
|
|
17
|
+
|
|
18
|
+
BUILD_DIR = build
|
|
19
|
+
|
|
20
|
+
all: $(BUILD_DIR)/$(TARGET)
|
|
21
|
+
|
|
22
|
+
$(BUILD_DIR):
|
|
23
|
+
mkdir -p $(BUILD_DIR)
|
|
24
|
+
|
|
25
|
+
$(BUILD_DIR)/$(TARGET): $(SOURCES) | $(BUILD_DIR)
|
|
26
|
+
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(SOURCES)
|
|
27
|
+
|
|
28
|
+
clean:
|
|
29
|
+
rm -rf $(BUILD_DIR)
|
|
30
|
+
|
|
31
|
+
.PHONY: all clean
|