webrtc-ruby 0.1.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 +21 -0
- data/README.md +257 -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 +757 -0
- data/ext/webrtc_ruby/webrtc_ruby.h +169 -0
- data/lib/webrtc/configuration.rb +99 -0
- data/lib/webrtc/data_channel.rb +154 -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/ffi/library.rb +100 -0
- data/lib/webrtc/ice_candidate.rb +62 -0
- data/lib/webrtc/ice_transport.rb +95 -0
- data/lib/webrtc/media_stream.rb +67 -0
- data/lib/webrtc/media_stream_track.rb +83 -0
- data/lib/webrtc/peer_connection.rb +346 -0
- data/lib/webrtc/promise.rb +59 -0
- data/lib/webrtc/rtp_receiver.rb +51 -0
- data/lib/webrtc/rtp_sender.rb +85 -0
- data/lib/webrtc/rtp_transceiver.rb +34 -0
- data/lib/webrtc/sctp_transport.rb +31 -0
- data/lib/webrtc/session_description.rb +64 -0
- data/lib/webrtc/stats_report.rb +199 -0
- data/lib/webrtc/version.rb +5 -0
- data/lib/webrtc.rb +38 -0
- data/webrtc-ruby.gemspec +33 -0
- metadata +107 -0
|
@@ -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
|