mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
b94f2ab339 | |||
9c79c8516b | |||
ad03549f8c | |||
5cc8eb1ce6 | |||
d19ff754b2 | |||
fb0f903e2e | |||
6628297580 | |||
ccc05b9999 | |||
fb2f480f92 | |||
cca0ac859a | |||
9fbc7c6ea8 | |||
83d65c805a | |||
0a5cef331d | |||
7c1714f83c | |||
f3024d0552 | |||
bcd1972270 | |||
57501b1739 | |||
cfe8a0e9c6 | |||
10061b3d4b | |||
52959ee700 | |||
0e86d6e3f1 | |||
62675e4c21 | |||
eafe86e4c0 | |||
a2b2126930 | |||
b9a663de75 | |||
f5faeba7b0 | |||
98f02c5195 | |||
d8ab5b4820 | |||
b569e78c02 | |||
4eac16e053 | |||
bb13b4bd37 | |||
aabb435fc7 | |||
3d7764c1e9 | |||
37687c3cd6 | |||
63f10303e0 | |||
d656d739f3 | |||
dbc706b69d | |||
1985088f4f | |||
ccbaa8beda | |||
642d304af9 | |||
df2102497d | |||
ad676815bd | |||
4bd40799fd | |||
4d1d1fa6fe | |||
50f61b19aa | |||
b7dbe7cdd9 | |||
7bfd731ed3 | |||
4da8b8c9a3 | |||
0382067d92 | |||
325230404a | |||
2a721015f8 | |||
5752b17a6f | |||
878d15b746 | |||
26d240e3ba | |||
ca7fc8b26f | |||
35cb9ea4be | |||
ec42736a2f | |||
b4f7c506da | |||
a577bb9004 | |||
bfd858ab13 | |||
18fe326090 |
7
.github/workflows/build-gnutls.yml
vendored
7
.github/workflows/build-gnutls.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Build and test with GnuTLS
|
name: Build with GnuTLS
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -7,7 +7,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -31,9 +31,6 @@ jobs:
|
|||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
- name: cmake
|
- name: cmake
|
||||||
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
|
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
|
||||||
env:
|
|
||||||
# hack to bypass EPERM issue on sendto()
|
|
||||||
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
|
|
||||||
- name: make
|
- name: make
|
||||||
run: (cd build; make -j2)
|
run: (cd build; make -j2)
|
||||||
- name: test
|
- name: test
|
||||||
|
4
.github/workflows/build-nice.yml
vendored
4
.github/workflows/build-nice.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Build and test with libnice
|
name: Build with libnice
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -7,7 +7,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
22
.github/workflows/build-openssl.yml
vendored
22
.github/workflows/build-openssl.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
name: Build and test with OpenSSL
|
name: Build with OpenSSL
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -7,7 +7,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@ -34,9 +34,23 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
OPENSSL_ROOT_DIR: /usr/local/opt/openssl
|
OPENSSL_ROOT_DIR: /usr/local/opt/openssl
|
||||||
OPENSSL_LIBRARIES: /usr/local/opt/openssl/lib
|
OPENSSL_LIBRARIES: /usr/local/opt/openssl/lib
|
||||||
# hack to bypass EPERM issue on sendto()
|
|
||||||
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
|
|
||||||
- name: make
|
- name: make
|
||||||
run: (cd build; make -j2)
|
run: (cd build; make -j2)
|
||||||
- name: test
|
- name: test
|
||||||
run: ./build/tests
|
run: ./build/tests
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
- name: submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
- name: cmake
|
||||||
|
run: cmake -B build -G "NMake Makefiles" -DUSE_JUICE=1 -DUSE_GNUTLS=0
|
||||||
|
- name: nmake
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
nmake
|
||||||
|
- name: test
|
||||||
|
run: build/tests.exe
|
||||||
|
|
||||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,6 +1,6 @@
|
|||||||
[submodule "deps/plog"]
|
[submodule "deps/plog"]
|
||||||
path = deps/plog
|
path = deps/plog
|
||||||
url = https://github.com/SergiusTheBest/plog
|
url = https://github.com/paullouisageneau/plog
|
||||||
[submodule "usrsctp"]
|
[submodule "usrsctp"]
|
||||||
path = deps/usrsctp
|
path = deps/usrsctp
|
||||||
url = https://github.com/sctplab/usrsctp.git
|
url = https://github.com/sctplab/usrsctp.git
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.7)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
project(libdatachannel
|
project(libdatachannel
|
||||||
DESCRIPTION "WebRTC DataChannels Library"
|
DESCRIPTION "WebRTC DataChannels Library"
|
||||||
VERSION 0.6.2
|
VERSION 0.6.3
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
||||||
@ -21,6 +21,8 @@ if(WIN32)
|
|||||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
add_definitions(-DNOMINMAX)
|
add_definitions(-DNOMINMAX)
|
||||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
add_definitions(-D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -205,7 +207,11 @@ set_target_properties(datachannel-tests PROPERTIES
|
|||||||
CXX_STANDARD 17)
|
CXX_STANDARD 17)
|
||||||
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
|
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
|
||||||
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
target_link_libraries(datachannel-tests datachannel nlohmann_json::nlohmann_json)
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel-tests datachannel-static) # DLL exports only the C API
|
||||||
|
else()
|
||||||
|
target_link_libraries(datachannel-tests datachannel)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Benchmark
|
# Benchmark
|
||||||
add_executable(datachannel-benchmark test/benchmark.cpp)
|
add_executable(datachannel-benchmark test/benchmark.cpp)
|
||||||
@ -215,7 +221,11 @@ set_target_properties(datachannel-benchmark PROPERTIES
|
|||||||
set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
|
set_target_properties(datachannel-benchmark PROPERTIES OUTPUT_NAME benchmark)
|
||||||
target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
|
target_compile_definitions(datachannel-benchmark PRIVATE BENCHMARK_MAIN=1)
|
||||||
target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel-benchmark PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
target_link_libraries(datachannel-benchmark datachannel)
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel-benchmark datachannel-static) # DLL exports only the C API
|
||||||
|
else()
|
||||||
|
target_link_libraries(datachannel-benchmark datachannel)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
||||||
|
192
Jamfile
192
Jamfile
@ -3,6 +3,12 @@ import feature : feature ;
|
|||||||
project libdatachannel ;
|
project libdatachannel ;
|
||||||
path-constant CWD : . ;
|
path-constant CWD : . ;
|
||||||
|
|
||||||
|
feature gnutls : off on : composite propagated ;
|
||||||
|
feature.compose <gnutls>off
|
||||||
|
: <define>USE_GNUTLS=0 ;
|
||||||
|
feature.compose <gnutls>on
|
||||||
|
: <define>USE_GNUTLS=1 ;
|
||||||
|
|
||||||
lib libdatachannel
|
lib libdatachannel
|
||||||
: # sources
|
: # sources
|
||||||
[ glob ./src/*.cpp ]
|
[ glob ./src/*.cpp ]
|
||||||
@ -12,25 +18,25 @@ lib libdatachannel
|
|||||||
<define>USE_JUICE=1
|
<define>USE_JUICE=1
|
||||||
<define>RTC_ENABLE_MEDIA=0
|
<define>RTC_ENABLE_MEDIA=0
|
||||||
<define>RTC_ENABLE_WEBSOCKET=0
|
<define>RTC_ENABLE_WEBSOCKET=0
|
||||||
|
<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
|
||||||
|
<toolset>msvc:<define>NOMINMAX
|
||||||
|
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||||
|
<toolset>msvc:<define>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
|
||||||
<library>/libdatachannel//usrsctp
|
<library>/libdatachannel//usrsctp
|
||||||
<library>/libdatachannel//juice
|
<library>/libdatachannel//juice
|
||||||
<library>/libdatachannel//plog
|
<library>/libdatachannel//plog
|
||||||
|
<gnutls>on:<library>gnutls
|
||||||
|
<gnutls>off:<library>ssl
|
||||||
|
<gnutls>off:<library>crypto
|
||||||
: # default build
|
: # default build
|
||||||
<link>static
|
<link>static
|
||||||
: # usage requirements
|
: # usage requirements
|
||||||
<include>./include
|
<include>./include
|
||||||
<library>/libdatachannel//plog
|
<library>/libdatachannel//plog
|
||||||
<cxxflags>-pthread
|
<toolset>gcc:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
||||||
<toolset>gcc:<cxxflags>"-Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
<toolset>clang:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
||||||
<toolset>clang:<cxxflags>"-Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
|
||||||
;
|
;
|
||||||
|
|
||||||
feature gnutls : off on : composite propagated ;
|
|
||||||
feature.compose <gnutls>off
|
|
||||||
: <define>USE_GNUTLS=0 ;
|
|
||||||
feature.compose <gnutls>on
|
|
||||||
: <define>USE_GNUTLS=1 ;
|
|
||||||
|
|
||||||
alias plog
|
alias plog
|
||||||
: # no sources
|
: # no sources
|
||||||
: # no build requirements
|
: # no build requirements
|
||||||
@ -48,41 +54,183 @@ alias usrsctp
|
|||||||
<library>libusrsctp.a
|
<library>libusrsctp.a
|
||||||
;
|
;
|
||||||
|
|
||||||
|
alias usrsctp
|
||||||
|
: # no sources
|
||||||
|
: <toolset>msvc
|
||||||
|
: # no default build
|
||||||
|
: # usage requirements
|
||||||
|
<include>./deps/usrsctp/usrsctplib
|
||||||
|
<library>usrsctp.lib
|
||||||
|
;
|
||||||
|
|
||||||
alias juice
|
alias juice
|
||||||
: # no sources
|
: # no sources
|
||||||
: # no build requirements
|
: # no build requirements
|
||||||
: # no default build
|
: # no default build
|
||||||
: # usage requirements
|
: # usage requirements
|
||||||
<include>./deps/libjuice/include
|
<include>./deps/libjuice/include
|
||||||
<library>libjuice.a
|
<library>libjuice-static.a
|
||||||
|
<gnutls>on:<library>nettle
|
||||||
|
;
|
||||||
|
|
||||||
|
alias juice
|
||||||
|
: # no sources
|
||||||
|
: <toolset>msvc
|
||||||
|
: # no default build
|
||||||
|
: # usage requirements
|
||||||
|
<include>./deps/libjuice/include
|
||||||
|
<library>juice-static.lib
|
||||||
;
|
;
|
||||||
|
|
||||||
make libusrsctp.a : : @make_libusrsctp ;
|
make libusrsctp.a : : @make_libusrsctp ;
|
||||||
|
make usrsctp.lib : : @make_libusrsctp_msvc ;
|
||||||
|
|
||||||
actions make_libusrsctp
|
actions make_libusrsctp
|
||||||
{
|
{
|
||||||
(cd $(CWD)/deps/usrsctp && \
|
(cd $(CWD)/deps/usrsctp && mkdir build && cd build && cmake -DCMAKE_C_FLAGS="-fPIC" .. && make -j2 usrsctp-static)
|
||||||
./bootstrap && \
|
cp $(CWD)/deps/usrsctp/build/usrsctplib/libusrsctp.a $(<)
|
||||||
./configure --enable-static --disable-debug CFLAGS="-fPIC -Wno-address-of-packed-member" && \
|
}
|
||||||
make)
|
actions make_libusrsctp_msvc
|
||||||
cp $(CWD)/deps/usrsctp/usrsctplib/.libs/libusrsctp.a $(<)
|
{
|
||||||
|
SET OLDD=%CD%
|
||||||
|
cd $(CWD)/deps/usrsctp
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -G "Visual Studio 16 2019" ..
|
||||||
|
cd build
|
||||||
|
msbuild usrsctplib.sln /property:Configuration=Release
|
||||||
|
cd %OLDD%
|
||||||
|
cp $(CWD)/deps/usrsctp/build/usrsctplib/Release/usrsctp.lib $(<)
|
||||||
}
|
}
|
||||||
|
|
||||||
make libjuice.a : : @make_libjuice ;
|
make libjuice-static.a : : @make_libjuice ;
|
||||||
|
make juice-static.lib : : @make_libjuice_msvc ;
|
||||||
|
|
||||||
rule make_libjuice ( targets * : sources * : properties * )
|
rule make_libjuice ( targets * : sources * : properties * )
|
||||||
{
|
{
|
||||||
if <crypto>gnutls in $(properties)
|
if <gnutls>on in $(properties)
|
||||||
{
|
{
|
||||||
MAKEOPTS on $(targets) = "USE_NETTLE=1" ;
|
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=1" ;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
local OPENSSL_INCLUDE = [ feature.get-values <openssl-include> : $(properties) ] ;
|
||||||
|
|
||||||
|
if <target-os>darwin in $(properties) && $(OPENSSL_INCLUDE) = ""
|
||||||
{
|
{
|
||||||
MAKEOPTS on $(targets) = "USE_NETTLE=0" ;
|
# on macOS, default to pick up openssl from the homebrew installation
|
||||||
|
# brew install openssl
|
||||||
|
OPENSSL_INCLUDE = /usr/local/opt/openssl/include ;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=0" ;
|
||||||
|
if $(OPENSSL_INCLUDE) != ""
|
||||||
|
{
|
||||||
|
CMAKEOPTS on $(targets) += " -DOPENSSL_ROOT_DIR=$(OPENSSL_INCLUDE)/.." ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actions make_libjuice
|
actions make_libjuice
|
||||||
{
|
{
|
||||||
(cd $(CWD)/deps/libjuice && make $(MAKEOPTS))
|
(cd $(CWD)/deps/libjuice && mkdir build && cd build && cmake -DCMAKE_C_FLAGS="-fPIC" $(CMAKEOPTS) .. && make -j2 juice-static)
|
||||||
cp $(CWD)/deps/libjuice/libjuice.a $(<)
|
cp $(CWD)/deps/libjuice/build/libjuice-static.a $(<)
|
||||||
|
}
|
||||||
|
rule make_libjuice_msvc ( targets * : sources * : properties * )
|
||||||
|
{
|
||||||
|
if <gnutls>on in $(properties)
|
||||||
|
{
|
||||||
|
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=1" ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CMAKEOPTS on $(targets) = "-DUSE_NETTLE=0" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions make_libjuice_msvc
|
||||||
|
{
|
||||||
|
SET OLDD=%CD%
|
||||||
|
cd $(CWD)/deps/libjuice
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -G "Visual Studio 16 2019" $(CMAKEOPTS) ..
|
||||||
|
msbuild libjuice.sln /property:Configuration=Release
|
||||||
|
cd %OLDD%
|
||||||
|
cp $(CWD)/deps/libjuice/build/Release/juice-static.lib $(<)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# the search path to pick up the openssl libraries from. This is the <search>
|
||||||
|
# property of those libraries
|
||||||
|
rule openssl-lib-path ( properties * )
|
||||||
|
{
|
||||||
|
local OPENSSL_LIB = [ feature.get-values <openssl-lib> : $(properties) ] ;
|
||||||
|
|
||||||
|
if <target-os>darwin in $(properties) && $(OPENSSL_LIB) = ""
|
||||||
|
{
|
||||||
|
# on macOS, default to pick up openssl from the homebrew installation
|
||||||
|
# brew install openssl
|
||||||
|
OPENSSL_LIB = /usr/local/opt/openssl/lib ;
|
||||||
|
}
|
||||||
|
else if <target-os>windows in $(properties) && $(OPENSSL_LIB) = ""
|
||||||
|
{
|
||||||
|
# on windows, assume openssl is installed to c:\OpenSSL-Win32
|
||||||
|
if <address-model>64 in $(properties)
|
||||||
|
{ OPENSSL_LIB = c:\\OpenSSL-Win64\\lib ; }
|
||||||
|
else
|
||||||
|
{ OPENSSL_LIB = c:\\OpenSSL-Win32\\lib ; }
|
||||||
|
}
|
||||||
|
|
||||||
|
local result ;
|
||||||
|
result += <search>$(OPENSSL_LIB) ;
|
||||||
|
return $(result) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# the include path to pick up openssl headers from. This is the
|
||||||
|
# usage-requirement for the openssl-related libraries
|
||||||
|
rule openssl-include-path ( properties * )
|
||||||
|
{
|
||||||
|
local OPENSSL_INCLUDE = [ feature.get-values <openssl-include> : $(properties) ] ;
|
||||||
|
|
||||||
|
if <target-os>darwin in $(properties) && $(OPENSSL_INCLUDE) = ""
|
||||||
|
{
|
||||||
|
# on macOS, default to pick up openssl from the homebrew installation
|
||||||
|
# brew install openssl
|
||||||
|
OPENSSL_INCLUDE = /usr/local/opt/openssl/include ;
|
||||||
|
}
|
||||||
|
else if <target-os>windows in $(properties) && $(OPENSSL_INCLUDE) = ""
|
||||||
|
{
|
||||||
|
# on windows, assume openssl is installed to c:\OpenSSL-Win32
|
||||||
|
if <address-model>64 in $(properties)
|
||||||
|
{ OPENSSL_INCLUDE = c:\\OpenSSL-Win64\\include ; }
|
||||||
|
else
|
||||||
|
{ OPENSSL_INCLUDE = c:\\OpenSSL-Win32\\include ; }
|
||||||
|
}
|
||||||
|
|
||||||
|
local result ;
|
||||||
|
result += <include>$(OPENSSL_INCLUDE) ;
|
||||||
|
return $(result) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
# libraries for OpenSSL on Windows
|
||||||
|
lib advapi32 : : <name>advapi32 ;
|
||||||
|
lib user32 : : <name>user32 ;
|
||||||
|
lib shell32 : : <name>shell32 ;
|
||||||
|
lib gdi32 : : <name>gdi32 ;
|
||||||
|
lib bcrypt : : <name>bcrypt ;
|
||||||
|
lib z : : <link>shared <name>z ;
|
||||||
|
alias ssl-deps : advapi32 user32 shell32 gdi32 ;
|
||||||
|
|
||||||
|
# OpenSSL on Windows
|
||||||
|
lib crypto : ssl-deps : <toolset>msvc <openssl-version>1.1 <name>libcrypto
|
||||||
|
<conditional>@openssl-lib-path : : <conditional>@openssl-include-path ;
|
||||||
|
lib ssl : ssl-deps : <toolset>msvc <openssl-version>1.1 <name>libssl <use>crypto
|
||||||
|
<conditional>@openssl-lib-path : : <conditional>@openssl-include-path ;
|
||||||
|
|
||||||
|
# OpenSSL on other platforms
|
||||||
|
lib crypto : : <name>crypto <use>z <conditional>@openssl-lib-path : :
|
||||||
|
<conditional>@openssl-include-path ;
|
||||||
|
lib ssl : : <name>ssl <use>crypto <conditional>@openssl-lib-path : :
|
||||||
|
<conditional>@openssl-include-path ;
|
||||||
|
|
||||||
|
# GnuTLS
|
||||||
|
lib gnutls : : <link>shared <name>gnutls ;
|
||||||
|
lib nettle : : <link>shared <name>nettle ;
|
||||||
|
|
||||||
|
42
README.md
42
README.md
@ -44,31 +44,55 @@ Features:
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
- GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/
|
- GnuTLS: https://www.gnutls.org/ or OpenSSL: https://www.openssl.org/
|
||||||
|
|
||||||
Optional:
|
Optional dependencies:
|
||||||
- libnice: https://nice.freedesktop.org/ (substituable with libjuice)
|
- libnice: https://nice.freedesktop.org/ (substituable with libjuice)
|
||||||
- libSRTP: https://github.com/cisco/libsrtp
|
- libSRTP: https://github.com/cisco/libsrtp (only necessary for media transport)
|
||||||
|
|
||||||
Submodules:
|
Submodules:
|
||||||
- libjuice: https://github.com/paullouisageneau/libjuice
|
- libjuice: https://github.com/paullouisageneau/libjuice
|
||||||
- usrsctp: https://github.com/sctplab/usrsctp
|
- usrsctp: https://github.com/sctplab/usrsctp
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
### Building with CMake (preferred)
|
|
||||||
|
### Clone repository and submodules
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
$ git clone https://github.com/paullouisageneau/libdatachannel.git
|
||||||
|
$ cd libdatachannel
|
||||||
$ git submodule update --init --recursive
|
$ git submodule update --init --recursive
|
||||||
$ mkdir build
|
|
||||||
$ cd build
|
|
||||||
$ cmake -DUSE_JUICE=1 -DUSE_GNUTLS=1 ..
|
|
||||||
$ make
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Building directly with Make
|
### Building with CMake
|
||||||
|
|
||||||
|
The CMake library targets `libdatachannel` and `libdatachannel-static` respectively correspond to the shared and static libraries. On Windows, the DLL resulting from the shared library build only exposes the C API, use the static library for the C++ API. The default target will build tests and examples.
|
||||||
|
|
||||||
|
#### POSIX-compliant operating systems (including Linux and Apple macOS)
|
||||||
|
```bash
|
||||||
|
$ cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
|
||||||
|
$ cd build
|
||||||
|
$ make -j2
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Microsoft Windows with MinGW cross-compilation
|
||||||
|
```bash
|
||||||
|
$ cmake -B build -DUSE_JUICE=1 -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-x86_64-w64-mingw32.cmake # replace with your toolchain file
|
||||||
|
$ cd build
|
||||||
|
$ make -j2
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Microsoft Windows with Microsoft Visual C++
|
||||||
|
```bash
|
||||||
|
$ cmake -B build -G "NMake Makefiles" -DUSE_JUICE=1
|
||||||
|
$ cd build
|
||||||
|
$ nmake
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building directly with Make (Linux only)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git submodule update --init --recursive
|
|
||||||
$ make USE_JUICE=1 USE_GNUTLS=1
|
$ make USE_JUICE=1 USE_GNUTLS=1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: 833897ef91...c6566d3c6f
2
deps/plog
vendored
2
deps/plog
vendored
Submodule deps/plog updated: 47883f0609...afb6f6f0e8
2
deps/usrsctp
vendored
2
deps/usrsctp
vendored
Submodule deps/usrsctp updated: aa10d60bc2...fdc4d2b99b
@ -7,5 +7,11 @@ add_executable(datachannel-client main.cpp)
|
|||||||
set_target_properties(datachannel-client PROPERTIES
|
set_target_properties(datachannel-client PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
OUTPUT_NAME client)
|
OUTPUT_NAME client)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel-client datachannel-static) # DLL exports only the C API
|
||||||
|
else()
|
||||||
|
target_link_libraries(datachannel-client datachannel)
|
||||||
|
endif()
|
||||||
target_link_libraries(datachannel-client datachannel nlohmann_json)
|
target_link_libraries(datachannel-client datachannel nlohmann_json)
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ string randomId(size_t length) {
|
|||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
||||||
string id(length, '0');
|
string id(length, '0');
|
||||||
default_random_engine rng(random_device{}());
|
default_random_engine rng(random_device{}());
|
||||||
uniform_int_distribution<int> dist(0, characters.size() - 1);
|
uniform_int_distribution<int> dist(0, int(characters.size() - 1));
|
||||||
generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); });
|
generate(id.begin(), id.end(), [&]() { return characters.at(dist(rng)); });
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.5.1)
|
|||||||
project(offerer C)
|
project(offerer C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_FLAGS "-Wall -g -O2")
|
|
||||||
|
|
||||||
add_executable(datachannel-copy-paste-capi-offerer offerer.c)
|
add_executable(datachannel-copy-paste-capi-offerer offerer.c)
|
||||||
set_target_properties(datachannel-copy-paste-capi-offerer PROPERTIES
|
set_target_properties(datachannel-copy-paste-capi-offerer PROPERTIES
|
||||||
@ -13,3 +12,4 @@ add_executable(datachannel-copy-paste-capi-answerer answerer.c)
|
|||||||
set_target_properties(datachannel-copy-paste-capi-answerer PROPERTIES
|
set_target_properties(datachannel-copy-paste-capi-answerer PROPERTIES
|
||||||
OUTPUT_NAME answerer)
|
OUTPUT_NAME answerer)
|
||||||
target_link_libraries(datachannel-copy-paste-capi-answerer datachannel)
|
target_link_libraries(datachannel-copy-paste-capi-answerer datachannel)
|
||||||
|
|
||||||
|
@ -41,30 +41,22 @@ typedef struct {
|
|||||||
bool connected;
|
bool connected;
|
||||||
} Peer;
|
} Peer;
|
||||||
|
|
||||||
Peer *peer = NULL;
|
|
||||||
|
|
||||||
static void dataChannelCallback(int dc, void *ptr);
|
static void dataChannelCallback(int dc, void *ptr);
|
||||||
|
|
||||||
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
|
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
|
||||||
|
|
||||||
static void candidateCallback(const char *cand, const char *mid, void *ptr);
|
static void candidateCallback(const char *cand, const char *mid, void *ptr);
|
||||||
|
|
||||||
static void stateChangeCallback(rtcState state, void *ptr);
|
static void stateChangeCallback(rtcState state, void *ptr);
|
||||||
|
|
||||||
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
|
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
|
||||||
|
|
||||||
static void closedCallback(void *ptr);
|
static void closedCallback(void *ptr);
|
||||||
|
|
||||||
static void messageCallback(const char *message, int size, void *ptr);
|
static void messageCallback(const char *message, int size, void *ptr);
|
||||||
|
|
||||||
static void deletePeer(Peer *peer);
|
static void deletePeer(Peer *peer);
|
||||||
|
|
||||||
int all_space(const char *str);
|
|
||||||
char* state_print(rtcState state);
|
char* state_print(rtcState state);
|
||||||
char* rtcGatheringState_print(rtcState state);
|
char *rtcGatheringState_print(rtcGatheringState state);
|
||||||
|
|
||||||
|
int all_space(const char *str);
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
rtcInitLogger(RTC_LOG_DEBUG);
|
rtcInitLogger(RTC_LOG_DEBUG, NULL);
|
||||||
|
|
||||||
// Create peer
|
// Create peer
|
||||||
rtcConfiguration config;
|
rtcConfiguration config;
|
||||||
@ -259,17 +251,7 @@ static void dataChannelCallback(int dc, void *ptr) {
|
|||||||
printf("DataChannel %s: Received with label \"%s\"\n", "answerer", buffer);
|
printf("DataChannel %s: Received with label \"%s\"\n", "answerer", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int all_space(const char *str) {
|
char *state_print(rtcState state) {
|
||||||
while (*str) {
|
|
||||||
if (!isspace(*str++)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* state_print(rtcState state) {
|
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case RTC_NEW:
|
case RTC_NEW:
|
||||||
@ -297,8 +279,8 @@ char* state_print(rtcState state) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* rtcGatheringState_print(rtcState state) {
|
char *rtcGatheringState_print(rtcGatheringState state) {
|
||||||
char* str = NULL;
|
char *str = NULL;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case RTC_GATHERING_NEW:
|
case RTC_GATHERING_NEW:
|
||||||
str = "RTC_GATHERING_NEW";
|
str = "RTC_GATHERING_NEW";
|
||||||
@ -315,3 +297,13 @@ char* rtcGatheringState_print(rtcState state) {
|
|||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int all_space(const char *str) {
|
||||||
|
while (*str) {
|
||||||
|
if (!isspace(*str++)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
// Simple POSIX getline() implementation
|
// Simple POSIX getline() implementation
|
||||||
// This code is public domain
|
// This code is public domain
|
||||||
|
|
||||||
#include "malloc.h"
|
#include <malloc.h>
|
||||||
#include "stdio.h"
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
int getline(char **lineptr, size_t *n, FILE *stream) {
|
||||||
if (!lineptr || !stream || !n)
|
if (!lineptr || !stream || !n)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
|||||||
*n = 128;
|
*n = 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t pos = 0;
|
int pos = 0;
|
||||||
while(c != EOF) {
|
while(c != EOF) {
|
||||||
if (pos + 1 >= *n) {
|
if (pos + 1 >= *n) {
|
||||||
size_t new_size = *n + (*n >> 2);
|
size_t new_size = *n + (*n >> 2);
|
||||||
@ -45,4 +46,3 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
|
|||||||
(*lineptr)[pos] = '\0';
|
(*lineptr)[pos] = '\0';
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
|||||||
#include <unistd.h> // for sleep
|
#include <unistd.h> // for sleep
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char* state_print(rtcState state);
|
|
||||||
char* rtcGatheringState_print(rtcState state);
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
rtcState state;
|
rtcState state;
|
||||||
rtcGatheringState gatheringState;
|
rtcGatheringState gatheringState;
|
||||||
@ -44,28 +41,22 @@ typedef struct {
|
|||||||
bool connected;
|
bool connected;
|
||||||
} Peer;
|
} Peer;
|
||||||
|
|
||||||
Peer *peer = NULL;
|
|
||||||
|
|
||||||
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
|
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
|
||||||
|
|
||||||
static void candidateCallback(const char *cand, const char *mid, void *ptr);
|
static void candidateCallback(const char *cand, const char *mid, void *ptr);
|
||||||
|
|
||||||
static void stateChangeCallback(rtcState state, void *ptr);
|
static void stateChangeCallback(rtcState state, void *ptr);
|
||||||
|
|
||||||
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
|
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
|
||||||
|
|
||||||
static void openCallback(void *ptr);
|
static void openCallback(void *ptr);
|
||||||
|
|
||||||
static void closedCallback(void *ptr);
|
static void closedCallback(void *ptr);
|
||||||
|
|
||||||
static void messageCallback(const char *message, int size, void *ptr);
|
static void messageCallback(const char *message, int size, void *ptr);
|
||||||
|
|
||||||
static void deletePeer(Peer *peer);
|
static void deletePeer(Peer *peer);
|
||||||
|
|
||||||
|
char *state_print(rtcState state);
|
||||||
|
char *rtcGatheringState_print(rtcGatheringState state);
|
||||||
|
|
||||||
int all_space(const char *str);
|
int all_space(const char *str);
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
rtcInitLogger(RTC_LOG_DEBUG);
|
rtcInitLogger(RTC_LOG_DEBUG, NULL);
|
||||||
|
|
||||||
// Create peer
|
// Create peer
|
||||||
rtcConfiguration config;
|
rtcConfiguration config;
|
||||||
@ -263,17 +254,7 @@ static void deletePeer(Peer *peer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int all_space(const char *str) {
|
char *state_print(rtcState state) {
|
||||||
while (*str) {
|
|
||||||
if (!isspace(*str++)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* state_print(rtcState state) {
|
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case RTC_NEW:
|
case RTC_NEW:
|
||||||
@ -299,11 +280,10 @@ char* state_print(rtcState state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
return str;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* rtcGatheringState_print(rtcState state) {
|
char *rtcGatheringState_print(rtcGatheringState state) {
|
||||||
char* str = NULL;
|
char *str = NULL;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case RTC_GATHERING_NEW:
|
case RTC_GATHERING_NEW:
|
||||||
str = "RTC_GATHERING_NEW";
|
str = "RTC_GATHERING_NEW";
|
||||||
@ -320,3 +300,13 @@ char* rtcGatheringState_print(rtcState state) {
|
|||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int all_space(const char *str) {
|
||||||
|
while (*str) {
|
||||||
|
if (!isspace(*str++)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
@ -4,11 +4,19 @@ add_executable(datachannel-copy-paste-offerer offerer.cpp)
|
|||||||
set_target_properties(datachannel-copy-paste-offerer PROPERTIES
|
set_target_properties(datachannel-copy-paste-offerer PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
OUTPUT_NAME offerer)
|
OUTPUT_NAME offerer)
|
||||||
target_link_libraries(datachannel-copy-paste-offerer datachannel)
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel-copy-paste-offerer datachannel-static) # DLL exports only the C API
|
||||||
|
else()
|
||||||
|
target_link_libraries(datachannel-copy-paste-offerer datachannel)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(datachannel-copy-paste-answerer answerer.cpp)
|
add_executable(datachannel-copy-paste-answerer answerer.cpp)
|
||||||
set_target_properties(datachannel-copy-paste-answerer PROPERTIES
|
set_target_properties(datachannel-copy-paste-answerer PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
OUTPUT_NAME answerer)
|
OUTPUT_NAME answerer)
|
||||||
target_link_libraries(datachannel-copy-paste-answerer datachannel)
|
if(WIN32)
|
||||||
|
target_link_libraries(datachannel-copy-paste-answerer datachannel-static) # DLL exports only the C API
|
||||||
|
else()
|
||||||
|
target_link_libraries(datachannel-copy-paste-answerer datachannel)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@ -108,8 +108,8 @@ template <typename Iterator> bool DataChannel::sendBuffer(Iterator first, Iterat
|
|||||||
auto message = std::make_shared<Message>(size);
|
auto message = std::make_shared<Message>(size);
|
||||||
auto pos = message->begin();
|
auto pos = message->begin();
|
||||||
for (Iterator it = first; it != last; ++it) {
|
for (Iterator it = first; it != last; ++it) {
|
||||||
auto [bytes, size] = to_bytes(*it);
|
auto [bytes, len] = to_bytes(*it);
|
||||||
pos = std::copy(bytes, bytes + size, pos);
|
pos = std::copy(bytes, bytes + len, pos);
|
||||||
}
|
}
|
||||||
return outgoing(message);
|
return outgoing(message);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,11 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#ifdef _WIN32
|
||||||
|
#define RTC_EXPORT __declspec(dllexport)
|
||||||
// libdatachannel C API
|
#else
|
||||||
|
#define RTC_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef RTC_ENABLE_MEDIA
|
#ifndef RTC_ENABLE_MEDIA
|
||||||
#define RTC_ENABLE_MEDIA 1
|
#define RTC_ENABLE_MEDIA 1
|
||||||
@ -35,6 +37,10 @@ extern "C" {
|
|||||||
#define RTC_ENABLE_WEBSOCKET 1
|
#define RTC_ENABLE_WEBSOCKET 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// libdatachannel C API
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RTC_NEW = 0,
|
RTC_NEW = 0,
|
||||||
RTC_CONNECTING = 1,
|
RTC_CONNECTING = 1,
|
||||||
@ -71,71 +77,72 @@ typedef struct {
|
|||||||
uint16_t portRangeEnd;
|
uint16_t portRangeEnd;
|
||||||
} rtcConfiguration;
|
} rtcConfiguration;
|
||||||
|
|
||||||
typedef void (*dataChannelCallbackFunc)(int dc, void *ptr);
|
typedef void (*rtcLogCallbackFunc)(rtcLogLevel level, const char *message);
|
||||||
typedef void (*descriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
|
typedef void (*rtcDataChannelCallbackFunc)(int dc, void *ptr);
|
||||||
typedef void (*candidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
|
typedef void (*rtcDescriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
|
||||||
typedef void (*stateChangeCallbackFunc)(rtcState state, void *ptr);
|
typedef void (*rtcCandidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
|
||||||
typedef void (*gatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
|
typedef void (*rtcStateChangeCallbackFunc)(rtcState state, void *ptr);
|
||||||
typedef void (*openCallbackFunc)(void *ptr);
|
typedef void (*rtcGatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
|
||||||
typedef void (*closedCallbackFunc)(void *ptr);
|
typedef void (*rtcOpenCallbackFunc)(void *ptr);
|
||||||
typedef void (*errorCallbackFunc)(const char *error, void *ptr);
|
typedef void (*rtcClosedCallbackFunc)(void *ptr);
|
||||||
typedef void (*messageCallbackFunc)(const char *message, int size, void *ptr);
|
typedef void (*rtcErrorCallbackFunc)(const char *error, void *ptr);
|
||||||
typedef void (*bufferedAmountLowCallbackFunc)(void *ptr);
|
typedef void (*rtcMessageCallbackFunc)(const char *message, int size, void *ptr);
|
||||||
typedef void (*availableCallbackFunc)(void *ptr);
|
typedef void (*rtcBufferedAmountLowCallbackFunc)(void *ptr);
|
||||||
|
typedef void (*rtcAvailableCallbackFunc)(void *ptr);
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
void rtcInitLogger(rtcLogLevel level);
|
RTC_EXPORT void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb); // NULL cb to log to stdout
|
||||||
|
|
||||||
// User pointer
|
// User pointer
|
||||||
void rtcSetUserPointer(int id, void *ptr);
|
RTC_EXPORT void rtcSetUserPointer(int id, void *ptr);
|
||||||
|
|
||||||
// PeerConnection
|
// PeerConnection
|
||||||
int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
|
RTC_EXPORT int rtcCreatePeerConnection(const rtcConfiguration *config); // returns pc id
|
||||||
int rtcDeletePeerConnection(int pc);
|
RTC_EXPORT int rtcDeletePeerConnection(int pc);
|
||||||
|
|
||||||
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb);
|
RTC_EXPORT int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb);
|
||||||
int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb);
|
RTC_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
|
||||||
int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb);
|
RTC_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
|
||||||
int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb);
|
RTC_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
|
||||||
int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb);
|
RTC_EXPORT int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb);
|
||||||
|
|
||||||
int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
|
RTC_EXPORT int rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
|
||||||
int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
|
RTC_EXPORT int rtcAddRemoteCandidate(int pc, const char *cand, const char *mid);
|
||||||
|
|
||||||
int rtcGetLocalAddress(int pc, char *buffer, int size);
|
RTC_EXPORT int rtcGetLocalAddress(int pc, char *buffer, int size);
|
||||||
int rtcGetRemoteAddress(int pc, char *buffer, int size);
|
RTC_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
|
||||||
|
|
||||||
// DataChannel
|
// DataChannel
|
||||||
int rtcCreateDataChannel(int pc, const char *label); // returns dc id
|
RTC_EXPORT int rtcCreateDataChannel(int pc, const char *label); // returns dc id
|
||||||
int rtcDeleteDataChannel(int dc);
|
RTC_EXPORT int rtcDeleteDataChannel(int dc);
|
||||||
|
|
||||||
int rtcGetDataChannelLabel(int dc, char *buffer, int size);
|
RTC_EXPORT int rtcGetDataChannelLabel(int dc, char *buffer, int size);
|
||||||
|
|
||||||
// WebSocket
|
// WebSocket
|
||||||
#if RTC_ENABLE_WEBSOCKET
|
#if RTC_ENABLE_WEBSOCKET
|
||||||
int rtcCreateWebSocket(const char *url); // returns ws id
|
RTC_EXPORT int rtcCreateWebSocket(const char *url); // returns ws id
|
||||||
int rtcDeleteWebsocket(int ws);
|
RTC_EXPORT int rtcDeleteWebsocket(int ws);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// DataChannel and WebSocket common API
|
// DataChannel and WebSocket common API
|
||||||
int rtcSetOpenCallback(int id, openCallbackFunc cb);
|
RTC_EXPORT int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb);
|
||||||
int rtcSetClosedCallback(int id, closedCallbackFunc cb);
|
RTC_EXPORT int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb);
|
||||||
int rtcSetErrorCallback(int id, errorCallbackFunc cb);
|
RTC_EXPORT int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb);
|
||||||
int rtcSetMessageCallback(int id, messageCallbackFunc cb);
|
RTC_EXPORT int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb);
|
||||||
int rtcSendMessage(int id, const char *data, int size);
|
RTC_EXPORT int rtcSendMessage(int id, const char *data, int size);
|
||||||
|
|
||||||
int rtcGetBufferedAmount(int id); // total size buffered to send
|
RTC_EXPORT int rtcGetBufferedAmount(int id); // total size buffered to send
|
||||||
int rtcSetBufferedAmountLowThreshold(int id, int amount);
|
RTC_EXPORT int rtcSetBufferedAmountLowThreshold(int id, int amount);
|
||||||
int rtcSetBufferedAmountLowCallback(int id, bufferedAmountLowCallbackFunc cb);
|
RTC_EXPORT int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb);
|
||||||
|
|
||||||
// DataChannel and WebSocket common extended API
|
// DataChannel and WebSocket common extended API
|
||||||
int rtcGetAvailableAmount(int id); // total size available to receive
|
RTC_EXPORT int rtcGetAvailableAmount(int id); // total size available to receive
|
||||||
int rtcSetAvailableCallback(int id, availableCallbackFunc cb);
|
RTC_EXPORT int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb);
|
||||||
int rtcReceiveMessage(int id, char *buffer, int *size);
|
RTC_EXPORT int rtcReceiveMessage(int id, char *buffer, int *size);
|
||||||
|
|
||||||
// Optional preload and cleanup
|
// Optional preload and cleanup
|
||||||
void rtcPreload();
|
RTC_EXPORT void rtcPreload(void);
|
||||||
void rtcCleanup();
|
RTC_EXPORT void rtcCleanup(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
@ -42,7 +42,7 @@ string to_base64(const binary &data) {
|
|||||||
i += 3;
|
i += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
int left = data.size() - i;
|
int left = int(data.size() - i);
|
||||||
if (left) {
|
if (left) {
|
||||||
auto d0 = to_integer<uint8_t>(data[i]);
|
auto d0 = to_integer<uint8_t>(data[i]);
|
||||||
out += tab[d0 >> 2];
|
out += tab[d0 >> 2];
|
||||||
|
@ -98,8 +98,8 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
// Rewrite the candidate
|
// Rewrite the candidate
|
||||||
char nodebuffer[MAX_NUMERICNODE_LEN];
|
char nodebuffer[MAX_NUMERICNODE_LEN];
|
||||||
char servbuffer[MAX_NUMERICSERV_LEN];
|
char servbuffer[MAX_NUMERICSERV_LEN];
|
||||||
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
|
if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
|
||||||
servbuffer, MAX_NUMERICSERV_LEN,
|
MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
|
||||||
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||||
const char sp{' '};
|
const char sp{' '};
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
|
@ -132,14 +132,14 @@ namespace rtc {
|
|||||||
|
|
||||||
Certificate::Certificate(string crt_pem, string key_pem) {
|
Certificate::Certificate(string crt_pem, string key_pem) {
|
||||||
BIO *bio = BIO_new(BIO_s_mem());
|
BIO *bio = BIO_new(BIO_s_mem());
|
||||||
BIO_write(bio, crt_pem.data(), crt_pem.size());
|
BIO_write(bio, crt_pem.data(), int(crt_pem.size()));
|
||||||
mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);
|
mX509 = shared_ptr<X509>(PEM_read_bio_X509(bio, nullptr, 0, 0), X509_free);
|
||||||
BIO_free(bio);
|
BIO_free(bio);
|
||||||
if (!mX509)
|
if (!mX509)
|
||||||
throw std::invalid_argument("Unable to import certificate PEM");
|
throw std::invalid_argument("Unable to import certificate PEM");
|
||||||
|
|
||||||
bio = BIO_new(BIO_s_mem());
|
bio = BIO_new(BIO_s_mem());
|
||||||
BIO_write(bio, key_pem.data(), key_pem.size());
|
BIO_write(bio, key_pem.data(), int(key_pem.size()));
|
||||||
mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);
|
mPKey = shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio, nullptr, 0, 0), EVP_PKEY_free);
|
||||||
BIO_free(bio);
|
BIO_free(bio);
|
||||||
if (!mPKey)
|
if (!mPKey)
|
||||||
@ -233,8 +233,8 @@ namespace {
|
|||||||
// Helper function roughly equivalent to std::async with policy std::launch::async
|
// Helper function roughly equivalent to std::async with policy std::launch::async
|
||||||
// since std::async might be unreliable on some platforms (e.g. Mingw32 on Windows)
|
// since std::async might be unreliable on some platforms (e.g. Mingw32 on Windows)
|
||||||
template <class F, class... Args>
|
template <class F, class... Args>
|
||||||
std::future<std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>> thread_call(F &&f,
|
std::future<std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>>
|
||||||
Args &&... args) {
|
thread_call(F &&f, Args &&... args) {
|
||||||
using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
|
using R = std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>;
|
||||||
std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...));
|
std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...));
|
||||||
std::future<R> future = task.get_future();
|
std::future<R> future = task.get_future();
|
||||||
|
@ -186,8 +186,8 @@ void DataChannel::open(shared_ptr<SctpTransport> transport) {
|
|||||||
open.channelType = mReliability->type;
|
open.channelType = mReliability->type;
|
||||||
open.priority = htons(0);
|
open.priority = htons(0);
|
||||||
open.reliabilityParameter = htonl(reliabilityParameter);
|
open.reliabilityParameter = htonl(reliabilityParameter);
|
||||||
open.labelLength = htons(mLabel.size());
|
open.labelLength = htons(uint16_t(mLabel.size()));
|
||||||
open.protocolLength = htons(mProtocol.size());
|
open.protocolLength = htons(uint16_t(mProtocol.size()));
|
||||||
|
|
||||||
auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
|
auto end = reinterpret_cast<char *>(buffer.data() + sizeof(OpenMessage));
|
||||||
std::copy(mLabel.begin(), mLabel.end(), end);
|
std::copy(mLabel.begin(), mLabel.end(), end);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
using std::size_t;
|
using std::size_t;
|
||||||
using std::string;
|
using std::string;
|
||||||
|
using std::chrono::system_clock;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
mData.mid = "data";
|
mData.mid = "data";
|
||||||
hintType(type);
|
hintType(type);
|
||||||
|
|
||||||
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
|
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||||
std::default_random_engine generator(seed);
|
std::default_random_engine generator(seed);
|
||||||
std::uniform_int_distribution<uint32_t> uniform;
|
std::uniform_int_distribution<uint32_t> uniform;
|
||||||
mSessionId = std::to_string(uniform(generator));
|
mSessionId = std::to_string(uniform(generator));
|
||||||
@ -112,7 +113,8 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
if (match_prefix(value, "sha-256 ")) {
|
if (match_prefix(value, "sha-256 ")) {
|
||||||
mFingerprint = value.substr(8);
|
mFingerprint = value.substr(8);
|
||||||
std::transform(mFingerprint->begin(), mFingerprint->end(),
|
std::transform(mFingerprint->begin(), mFingerprint->end(),
|
||||||
mFingerprint->begin(), [](char c) { return std::toupper(c); });
|
mFingerprint->begin(),
|
||||||
|
[](char c) { return char(std::toupper(c)); });
|
||||||
} else {
|
} else {
|
||||||
PLOG_WARNING << "Unknown SDP fingerprint type: " << value;
|
PLOG_WARNING << "Unknown SDP fingerprint type: " << value;
|
||||||
}
|
}
|
||||||
@ -185,11 +187,11 @@ std::vector<Candidate> Description::extractCandidates() {
|
|||||||
bool Description::hasMedia() const { return !mMedia.empty(); }
|
bool Description::hasMedia() const { return !mMedia.empty(); }
|
||||||
|
|
||||||
void Description::addMedia(const Description &source) {
|
void Description::addMedia(const Description &source) {
|
||||||
for (auto [mid, media] : source.mMedia)
|
for (auto p : source.mMedia)
|
||||||
if (mid != mData.mid)
|
if (p.first != mData.mid)
|
||||||
mMedia.emplace(mid, media);
|
mMedia.emplace(std::move(p));
|
||||||
else
|
else
|
||||||
PLOG_WARNING << "Media mid \"" << mid << "\" is the same as data mid, ignoring";
|
PLOG_WARNING << "Media mid \"" << p.first << "\" is the same as data mid, ignoring";
|
||||||
}
|
}
|
||||||
|
|
||||||
Description::operator string() const { return generateSdp("\r\n"); }
|
Description::operator string() const { return generateSdp("\r\n"); }
|
||||||
@ -210,8 +212,8 @@ string Description::generateSdp(const string &eol) const {
|
|||||||
// see Negotiating Media Multiplexing Using the Session Description Protocol
|
// see Negotiating Media Multiplexing Using the Session Description Protocol
|
||||||
// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54
|
// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54
|
||||||
sdp << "a=group:BUNDLE";
|
sdp << "a=group:BUNDLE";
|
||||||
for (const auto &[mid, _] : mMedia)
|
for (const auto &m : mMedia)
|
||||||
sdp << " " << mid;
|
sdp << " " << m.first; // mid
|
||||||
sdp << " " << mData.mid << eol;
|
sdp << " " << mData.mid << eol;
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
@ -230,12 +232,13 @@ string Description::generateSdp(const string &eol) const {
|
|||||||
if (!mMedia.empty()) {
|
if (!mMedia.empty()) {
|
||||||
// Lip-sync
|
// Lip-sync
|
||||||
sdp << "a=group:LS";
|
sdp << "a=group:LS";
|
||||||
for (const auto &[mid, _] : mMedia)
|
for (const auto &m : mMedia)
|
||||||
sdp << " " << mid;
|
sdp << " " << m.first; // mid
|
||||||
sdp << eol;
|
sdp << eol;
|
||||||
|
|
||||||
// Descriptions and attributes
|
// Descriptions and attributes
|
||||||
for (const auto &[_, media] : mMedia) {
|
for (const auto &m : mMedia) {
|
||||||
|
const auto &media = m.second;
|
||||||
sdp << "m=" << media.type << ' ' << 0 << ' ' << media.description << eol;
|
sdp << "m=" << media.type << ' ' << 0 << ' ' << media.description << eol;
|
||||||
sdp << "c=IN IP4 0.0.0.0" << eol;
|
sdp << "c=IN IP4 0.0.0.0" << eol;
|
||||||
sdp << "a=bundle-only" << eol;
|
sdp << "a=bundle-only" << eol;
|
||||||
@ -250,7 +253,7 @@ string Description::generateSdp(const string &eol) const {
|
|||||||
sdp << "a=ice-ufrag:" << mIceUfrag << eol;
|
sdp << "a=ice-ufrag:" << mIceUfrag << eol;
|
||||||
sdp << "a=ice-pwd:" << mIcePwd << eol;
|
sdp << "a=ice-pwd:" << mIcePwd << eol;
|
||||||
sdp << "a=setup:" << roleToString(mRole) << eol;
|
sdp << "a=setup:" << roleToString(mRole) << eol;
|
||||||
sdp << "a=dtls-id:1" << eol;
|
sdp << "a=tls-id:1" << eol;
|
||||||
if (mFingerprint)
|
if (mFingerprint)
|
||||||
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
||||||
|
|
||||||
|
@ -24,6 +24,14 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#if !USE_GNUTLS
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h> // for timeval
|
||||||
|
#else
|
||||||
|
#include <sys/time.h> // for timeval
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
@ -300,7 +308,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
|||||||
PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
|
PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!(mCtx = SSL_CTX_new(DTLS_method())))
|
mCtx = SSL_CTX_new(DTLS_method());
|
||||||
|
if (!mCtx)
|
||||||
throw std::runtime_error("Failed to create SSL context");
|
throw std::runtime_error("Failed to create SSL context");
|
||||||
|
|
||||||
openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),
|
openssl::check(SSL_CTX_set_cipher_list(mCtx, "ALL:!LOW:!EXP:!RC4:!MD5:@STRENGTH"),
|
||||||
@ -324,7 +333,8 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
|||||||
|
|
||||||
openssl::check(SSL_CTX_check_private_key(mCtx), "SSL local private key check failed");
|
openssl::check(SSL_CTX_check_private_key(mCtx), "SSL local private key check failed");
|
||||||
|
|
||||||
if (!(mSsl = SSL_new(mCtx)))
|
mSsl = SSL_new(mCtx);
|
||||||
|
if (!mSsl)
|
||||||
throw std::runtime_error("Failed to create SSL instance");
|
throw std::runtime_error("Failed to create SSL instance");
|
||||||
|
|
||||||
SSL_set_ex_data(mSsl, TransportExIndex, this);
|
SSL_set_ex_data(mSsl, TransportExIndex, this);
|
||||||
@ -334,7 +344,9 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
|||||||
else
|
else
|
||||||
SSL_set_accept_state(mSsl);
|
SSL_set_accept_state(mSsl);
|
||||||
|
|
||||||
if (!(mInBio = BIO_new(BIO_s_mem())) || !(mOutBio = BIO_new(BioMethods)))
|
mInBio = BIO_new(BIO_s_mem());
|
||||||
|
mOutBio = BIO_new(BioMethods);
|
||||||
|
if (!mInBio || !mOutBio)
|
||||||
throw std::runtime_error("Failed to create BIO");
|
throw std::runtime_error("Failed to create BIO");
|
||||||
|
|
||||||
BIO_set_mem_eof_return(mInBio, BIO_EOF);
|
BIO_set_mem_eof_return(mInBio, BIO_EOF);
|
||||||
@ -382,7 +394,7 @@ bool DtlsTransport::send(message_ptr message) {
|
|||||||
|
|
||||||
PLOG_VERBOSE << "Send size=" << message->size();
|
PLOG_VERBOSE << "Send size=" << message->size();
|
||||||
|
|
||||||
int ret = SSL_write(mSsl, message->data(), message->size());
|
int ret = SSL_write(mSsl, message->data(), int(message->size()));
|
||||||
return openssl::check(mSsl, ret);
|
return openssl::check(mSsl, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,11 +428,11 @@ void DtlsTransport::runRecvLoop() {
|
|||||||
// Process pending messages
|
// Process pending messages
|
||||||
while (!mIncomingQueue.empty()) {
|
while (!mIncomingQueue.empty()) {
|
||||||
auto message = *mIncomingQueue.pop();
|
auto message = *mIncomingQueue.pop();
|
||||||
BIO_write(mInBio, message->data(), message->size());
|
BIO_write(mInBio, message->data(), int(message->size()));
|
||||||
|
|
||||||
if (state() == State::Connecting) {
|
if (state() == State::Connecting) {
|
||||||
// Continue the handshake
|
// Continue the handshake
|
||||||
int ret = SSL_do_handshake(mSsl);
|
ret = SSL_do_handshake(mSsl);
|
||||||
if (!openssl::check(mSsl, ret, "Handshake failed"))
|
if (!openssl::check(mSsl, ret, "Handshake failed"))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -434,7 +446,7 @@ void DtlsTransport::runRecvLoop() {
|
|||||||
postHandshake();
|
postHandshake();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int ret = SSL_read(mSsl, buffer, bufferSize);
|
ret = SSL_read(mSsl, buffer, bufferSize);
|
||||||
if (!openssl::check(mSsl, ret))
|
if (!openssl::check(mSsl, ret))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -447,7 +459,7 @@ void DtlsTransport::runRecvLoop() {
|
|||||||
std::optional<milliseconds> duration;
|
std::optional<milliseconds> duration;
|
||||||
if (state() == State::Connecting) {
|
if (state() == State::Connecting) {
|
||||||
// Warning: This function breaks the usual return value convention
|
// Warning: This function breaks the usual return value convention
|
||||||
int ret = DTLSv1_handle_timeout(mSsl);
|
ret = DTLSv1_handle_timeout(mSsl);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
throw std::runtime_error("Handshake timeout"); // write BIO can't fail
|
throw std::runtime_error("Handshake timeout"); // write BIO can't fail
|
||||||
} else if (ret > 0) {
|
} else if (ret > 0) {
|
||||||
@ -486,7 +498,7 @@ void DtlsTransport::runRecvLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DtlsTransport::CertificateCallback(int preverify_ok, X509_STORE_CTX *ctx) {
|
int DtlsTransport::CertificateCallback(int /*preverify_ok*/, X509_STORE_CTX *ctx) {
|
||||||
SSL *ssl =
|
SSL *ssl =
|
||||||
static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
|
static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
|
||||||
DtlsTransport *t =
|
DtlsTransport *t =
|
||||||
@ -535,7 +547,7 @@ int DtlsTransport::BioMethodWrite(BIO *bio, const char *in, int inl) {
|
|||||||
return inl; // can't fail
|
return inl; // can't fail
|
||||||
}
|
}
|
||||||
|
|
||||||
long DtlsTransport::BioMethodCtrl(BIO *bio, int cmd, long num, void *ptr) {
|
long DtlsTransport::BioMethodCtrl(BIO * /*bio*/, int cmd, long /*num*/, void * /*ptr*/) {
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case BIO_CTRL_FLUSH:
|
case BIO_CTRL_FLUSH:
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -40,6 +40,7 @@ using namespace std::chrono_literals;
|
|||||||
|
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
using std::weak_ptr;
|
using std::weak_ptr;
|
||||||
|
using std::chrono::system_clock;
|
||||||
|
|
||||||
#if USE_JUICE
|
#if USE_JUICE
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
|||||||
|
|
||||||
// Randomize servers order
|
// Randomize servers order
|
||||||
std::vector<IceServer> servers = config.iceServers;
|
std::vector<IceServer> servers = config.iceServers;
|
||||||
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
|
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||||
std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
|
std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
|
||||||
|
|
||||||
// Pick a STUN server (TURN support is not implemented in libjuice yet)
|
// Pick a STUN server (TURN support is not implemented in libjuice yet)
|
||||||
@ -82,7 +83,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
|||||||
mStunHostname = server.hostname;
|
mStunHostname = server.hostname;
|
||||||
mStunService = server.service;
|
mStunService = server.service;
|
||||||
jconfig.stun_server_host = mStunHostname.c_str();
|
jconfig.stun_server_host = mStunHostname.c_str();
|
||||||
jconfig.stun_server_port = std::stoul(mStunService);
|
jconfig.stun_server_port = uint16_t(std::stoul(mStunService));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,7 +207,7 @@ void IceTransport::processCandidate(const string &candidate) {
|
|||||||
|
|
||||||
void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
|
void IceTransport::processGatheringDone() { changeGatheringState(GatheringState::Complete); }
|
||||||
|
|
||||||
void IceTransport::StateChangeCallback(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
|
void IceTransport::StateChangeCallback(juice_agent_t *, juice_state_t state, void *user_ptr) {
|
||||||
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
||||||
try {
|
try {
|
||||||
iceTransport->processStateChange(static_cast<unsigned int>(state));
|
iceTransport->processStateChange(static_cast<unsigned int>(state));
|
||||||
@ -215,7 +216,7 @@ void IceTransport::StateChangeCallback(juice_agent_t *agent, juice_state_t state
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::CandidateCallback(juice_agent_t *agent, const char *sdp, void *user_ptr) {
|
void IceTransport::CandidateCallback(juice_agent_t *, const char *sdp, void *user_ptr) {
|
||||||
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
||||||
try {
|
try {
|
||||||
iceTransport->processCandidate(sdp);
|
iceTransport->processCandidate(sdp);
|
||||||
@ -224,7 +225,7 @@ void IceTransport::CandidateCallback(juice_agent_t *agent, const char *sdp, void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::GatheringDoneCallback(juice_agent_t *agent, void *user_ptr) {
|
void IceTransport::GatheringDoneCallback(juice_agent_t *, void *user_ptr) {
|
||||||
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
||||||
try {
|
try {
|
||||||
iceTransport->processGatheringDone();
|
iceTransport->processGatheringDone();
|
||||||
@ -233,8 +234,7 @@ void IceTransport::GatheringDoneCallback(juice_agent_t *agent, void *user_ptr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::RecvCallback(juice_agent_t *agent, const char *data, size_t size,
|
void IceTransport::RecvCallback(juice_agent_t *, const char *data, size_t size, void *user_ptr) {
|
||||||
void *user_ptr) {
|
|
||||||
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
||||||
try {
|
try {
|
||||||
PLOG_VERBOSE << "Incoming size=" << size;
|
PLOG_VERBOSE << "Incoming size=" << size;
|
||||||
@ -337,7 +337,7 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
|||||||
|
|
||||||
// Randomize order
|
// Randomize order
|
||||||
std::vector<IceServer> servers = config.iceServers;
|
std::vector<IceServer> servers = config.iceServers;
|
||||||
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
|
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||||
std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
|
std::shuffle(servers.begin(), servers.end(), std::default_random_engine(seed));
|
||||||
|
|
||||||
// Add one STUN server
|
// Add one STUN server
|
||||||
|
@ -60,7 +60,10 @@ void Init::Preload() {
|
|||||||
make_certificate().wait(); // preload certificate
|
make_certificate().wait(); // preload certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
void Init::Cleanup() { Global.reset(); }
|
void Init::Cleanup() {
|
||||||
|
Global.reset();
|
||||||
|
CleanupCertificateCache();
|
||||||
|
}
|
||||||
|
|
||||||
Init::Init() {
|
Init::Init() {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -86,7 +89,6 @@ Init::Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Init::~Init() {
|
Init::~Init() {
|
||||||
CleanupCertificateCache();
|
|
||||||
SctpTransport::Cleanup();
|
SctpTransport::Cleanup();
|
||||||
DtlsTransport::Cleanup();
|
DtlsTransport::Cleanup();
|
||||||
#if RTC_ENABLE_WEBSOCKET
|
#if RTC_ENABLE_WEBSOCKET
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
|
|
||||||
#include "plog/Appenders/ColorConsoleAppender.h"
|
#include "plog/Appenders/ColorConsoleAppender.h"
|
||||||
|
#include "plog/Formatters/TxtFormatter.h"
|
||||||
|
#include "plog/Init.h"
|
||||||
#include "plog/Log.h"
|
#include "plog/Log.h"
|
||||||
#include "plog/Logger.h"
|
#include "plog/Logger.h"
|
||||||
|
|
||||||
|
@ -483,7 +483,7 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto channel = findDataChannel(message->stream);
|
auto channel = findDataChannel(uint16_t(message->stream));
|
||||||
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||||
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
||||||
@ -658,13 +658,14 @@ void PeerConnection::resetCallbacks() {
|
|||||||
mGatheringStateChangeCallback = nullptr;
|
mGatheringStateChangeCallback = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerConnection::getSelectedCandidatePair(CandidateInfo *local, CandidateInfo *remote) {
|
bool PeerConnection::getSelectedCandidatePair([[maybe_unused]] CandidateInfo *local,
|
||||||
#if not USE_JUICE
|
[[maybe_unused]] CandidateInfo *remote) {
|
||||||
|
#if USE_JUICE
|
||||||
|
PLOG_WARNING << "getSelectedCandidatePair() is not implemented for libjuice";
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||||
return iceTransport->getSelectedCandidatePair(local, remote);
|
return iceTransport->getSelectedCandidatePair(local, remote);
|
||||||
#else
|
|
||||||
PLOG_WARNING << "getSelectedCandidatePair is not implemented for libjuice";
|
|
||||||
return false;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
src/rtc.cpp
120
src/rtc.cpp
@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2019 Paul-Louis Ageneau
|
* Copyright (c) 2019-2020 Paul-Louis Ageneau
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -18,14 +18,16 @@
|
|||||||
|
|
||||||
#include "include.hpp"
|
#include "include.hpp"
|
||||||
|
|
||||||
#include "datachannel.hpp"
|
#include "rtc.h"
|
||||||
#include "peerconnection.hpp"
|
|
||||||
|
|
||||||
|
#include "datachannel.hpp"
|
||||||
|
#include "log.hpp"
|
||||||
|
#include "peerconnection.hpp"
|
||||||
#if RTC_ENABLE_WEBSOCKET
|
#if RTC_ENABLE_WEBSOCKET
|
||||||
#include "websocket.hpp"
|
#include "websocket.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <rtc.h>
|
#include "plog/Formatters/FuncMessageFormatter.h"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@ -33,6 +35,11 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace rtc;
|
using namespace rtc;
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
using std::string;
|
using std::string;
|
||||||
@ -57,7 +64,7 @@ void *getUserPointer(int id) {
|
|||||||
void setUserPointer(int i, void *ptr) {
|
void setUserPointer(int i, void *ptr) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
if (ptr)
|
if (ptr)
|
||||||
userPointerMap.insert(std::make_pair(i, ptr));
|
userPointerMap[i] = ptr;
|
||||||
else
|
else
|
||||||
userPointerMap.erase(i);
|
userPointerMap.erase(i);
|
||||||
}
|
}
|
||||||
@ -143,7 +150,7 @@ shared_ptr<Channel> getChannel(int id) {
|
|||||||
|
|
||||||
template <typename F> int wrap(F func) {
|
template <typename F> int wrap(F func) {
|
||||||
try {
|
try {
|
||||||
return func();
|
return int(func());
|
||||||
|
|
||||||
} catch (const std::invalid_argument &e) {
|
} catch (const std::invalid_argument &e) {
|
||||||
PLOG_ERROR << e.what();
|
PLOG_ERROR << e.what();
|
||||||
@ -160,9 +167,48 @@ template <typename F> int wrap(F func) {
|
|||||||
return RTC_ERR_SUCCESS; \
|
return RTC_ERR_SUCCESS; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
class plog_appender : public plog::IAppender {
|
||||||
|
public:
|
||||||
|
plog_appender(rtcLogCallbackFunc cb = nullptr) { set_callback(cb); }
|
||||||
|
|
||||||
|
void set_callback(rtcLogCallbackFunc cb) {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const plog::Record &record) override {
|
||||||
|
plog::Severity severity = record.getSeverity();
|
||||||
|
auto formatted = plog::FuncMessageFormatter::format(record);
|
||||||
|
formatted.pop_back(); // remove newline
|
||||||
|
#ifdef _WIN32
|
||||||
|
using convert_type = std::codecvt_utf8<wchar_t>;
|
||||||
|
std::wstring_convert<convert_type, wchar_t> converter;
|
||||||
|
std::string str = converter.to_bytes(formatted);
|
||||||
|
#else
|
||||||
|
std::string str = formatted;
|
||||||
|
#endif
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
if (callback)
|
||||||
|
callback(static_cast<rtcLogLevel>(record.getSeverity()), str.c_str());
|
||||||
|
else
|
||||||
|
std::cout << plog::severityToString(severity) << " " << str << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rtcLogCallbackFunc callback;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void rtcInitLogger(rtcLogLevel level) { InitLogger(static_cast<LogLevel>(level)); }
|
void rtcInitLogger(rtcLogLevel level, rtcLogCallbackFunc cb) {
|
||||||
|
static std::optional<plog_appender> appender;
|
||||||
|
if (appender)
|
||||||
|
appender->set_callback(cb);
|
||||||
|
else if (cb)
|
||||||
|
appender.emplace(plog_appender(cb));
|
||||||
|
|
||||||
|
InitLogger(static_cast<plog::Severity>(level), appender ? &appender.value() : nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
|
void rtcSetUserPointer(int i, void *ptr) { setUserPointer(i, ptr); }
|
||||||
|
|
||||||
@ -241,7 +287,7 @@ int rtcDeleteWebsocket(int ws) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) {
|
int rtcSetDataChannelCallback(int pc, rtcDataChannelCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto peerConnection = getPeerConnection(pc);
|
auto peerConnection = getPeerConnection(pc);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -256,7 +302,7 @@ int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
|
int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto peerConnection = getPeerConnection(pc);
|
auto peerConnection = getPeerConnection(pc);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -268,7 +314,7 @@ int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
|
int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto peerConnection = getPeerConnection(pc);
|
auto peerConnection = getPeerConnection(pc);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -280,7 +326,7 @@ int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
|
int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto peerConnection = getPeerConnection(pc);
|
auto peerConnection = getPeerConnection(pc);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -292,7 +338,7 @@ int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb) {
|
int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto peerConnection = getPeerConnection(pc);
|
auto peerConnection = getPeerConnection(pc);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -333,9 +379,13 @@ int rtcGetLocalAddress(int pc, char *buffer, int size) {
|
|||||||
if (!buffer)
|
if (!buffer)
|
||||||
throw std::invalid_argument("Unexpected null pointer");
|
throw std::invalid_argument("Unexpected null pointer");
|
||||||
|
|
||||||
|
if (size <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (auto addr = peerConnection->localAddress()) {
|
if (auto addr = peerConnection->localAddress()) {
|
||||||
size = std::min(size_t(size - 1), addr->size());
|
const char *data = addr->data();
|
||||||
std::copy(addr->data(), addr->data() + size, buffer);
|
size = std::min(size - 1, int(addr->size()));
|
||||||
|
std::copy(data, data + size, buffer);
|
||||||
buffer[size] = '\0';
|
buffer[size] = '\0';
|
||||||
return size + 1;
|
return size + 1;
|
||||||
}
|
}
|
||||||
@ -349,11 +399,15 @@ int rtcGetRemoteAddress(int pc, char *buffer, int size) {
|
|||||||
if (!buffer)
|
if (!buffer)
|
||||||
throw std::invalid_argument("Unexpected null pointer");
|
throw std::invalid_argument("Unexpected null pointer");
|
||||||
|
|
||||||
|
if (size <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (auto addr = peerConnection->remoteAddress()) {
|
if (auto addr = peerConnection->remoteAddress()) {
|
||||||
size = std::min(size_t(size - 1), addr->size());
|
const char *data = addr->data();
|
||||||
std::copy(addr->data(), addr->data() + size, buffer);
|
size = std::min(size - 1, int(addr->size()));
|
||||||
|
std::copy(data, data + size, buffer);
|
||||||
buffer[size] = '\0';
|
buffer[size] = '\0';
|
||||||
return size + 1;
|
return int(size + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -365,19 +419,19 @@ int rtcGetDataChannelLabel(int dc, char *buffer, int size) {
|
|||||||
if (!buffer)
|
if (!buffer)
|
||||||
throw std::invalid_argument("Unexpected null pointer");
|
throw std::invalid_argument("Unexpected null pointer");
|
||||||
|
|
||||||
if (size >= 0) {
|
if (size <= 0)
|
||||||
string label = dataChannel->label();
|
|
||||||
size = std::min(size_t(size - 1), label.size());
|
|
||||||
std::copy(label.data(), label.data() + size, buffer);
|
|
||||||
buffer[size] = '\0';
|
|
||||||
return size + 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
string label = dataChannel->label();
|
||||||
|
const char *data = label.data();
|
||||||
|
size = std::min(size - 1, int(label.size()));
|
||||||
|
std::copy(data, data + size, buffer);
|
||||||
|
buffer[size] = '\0';
|
||||||
|
return int(size + 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetOpenCallback(int id, openCallbackFunc cb) {
|
int rtcSetOpenCallback(int id, rtcOpenCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto channel = getChannel(id);
|
auto channel = getChannel(id);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -387,7 +441,7 @@ int rtcSetOpenCallback(int id, openCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetClosedCallback(int id, closedCallbackFunc cb) {
|
int rtcSetClosedCallback(int id, rtcClosedCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto channel = getChannel(id);
|
auto channel = getChannel(id);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -397,7 +451,7 @@ int rtcSetClosedCallback(int id, closedCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetErrorCallback(int id, errorCallbackFunc cb) {
|
int rtcSetErrorCallback(int id, rtcErrorCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto channel = getChannel(id);
|
auto channel = getChannel(id);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -408,13 +462,13 @@ int rtcSetErrorCallback(int id, errorCallbackFunc cb) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetMessageCallback(int id, messageCallbackFunc cb) {
|
int rtcSetMessageCallback(int id, rtcMessageCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto channel = getChannel(id);
|
auto channel = getChannel(id);
|
||||||
if (cb)
|
if (cb)
|
||||||
channel->onMessage(
|
channel->onMessage(
|
||||||
[id, cb](const binary &b) {
|
[id, cb](const binary &b) {
|
||||||
cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(id));
|
cb(reinterpret_cast<const char *>(b.data()), int(b.size()), getUserPointer(id));
|
||||||
},
|
},
|
||||||
[id, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(id)); });
|
[id, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(id)); });
|
||||||
else
|
else
|
||||||
@ -435,7 +489,7 @@ int rtcSendMessage(int id, const char *data, int size) {
|
|||||||
return size;
|
return size;
|
||||||
} else {
|
} else {
|
||||||
string str(data);
|
string str(data);
|
||||||
int len = str.size();
|
int len = int(str.size());
|
||||||
channel->send(std::move(str));
|
channel->send(std::move(str));
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@ -456,7 +510,7 @@ int rtcSetBufferedAmountLowThreshold(int id, int amount) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetBufferedAmountLowCallback(int id, bufferedAmountLowCallbackFunc cb) {
|
int rtcSetBufferedAmountLowCallback(int id, rtcBufferedAmountLowCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto channel = getChannel(id);
|
auto channel = getChannel(id);
|
||||||
if (cb)
|
if (cb)
|
||||||
@ -470,7 +524,7 @@ int rtcGetAvailableAmount(int id) {
|
|||||||
return WRAP({ return int(getChannel(id)->availableAmount()); });
|
return WRAP({ return int(getChannel(id)->availableAmount()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
int rtcSetAvailableCallback(int id, availableCallbackFunc cb) {
|
int rtcSetAvailableCallback(int id, rtcAvailableCallbackFunc cb) {
|
||||||
return WRAP({
|
return WRAP({
|
||||||
auto channel = getChannel(id);
|
auto channel = getChannel(id);
|
||||||
if (cb)
|
if (cb)
|
||||||
|
@ -262,7 +262,7 @@ bool SctpTransport::send(message_ptr message) {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
mSendQueue.push(message);
|
mSendQueue.push(message);
|
||||||
updateBufferedAmount(message->stream, message_size_func(message));
|
updateBufferedAmount(uint16_t(message->stream), long(message_size_func(message)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ bool SctpTransport::trySendQueue() {
|
|||||||
if (!trySendMessage(message))
|
if (!trySendMessage(message))
|
||||||
return false;
|
return false;
|
||||||
mSendQueue.pop();
|
mSendQueue.pop();
|
||||||
updateBufferedAmount(message->stream, -message_size_func(message));
|
updateBufferedAmount(uint16_t(message->stream), -long(message_size_func(message)));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -324,7 +324,7 @@ bool SctpTransport::trySendMessage(message_ptr message) {
|
|||||||
ppid = PPID_CONTROL;
|
ppid = PPID_CONTROL;
|
||||||
break;
|
break;
|
||||||
case Message::Reset:
|
case Message::Reset:
|
||||||
sendReset(message->stream);
|
sendReset(uint16_t(message->stream));
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
// Ignore
|
// Ignore
|
||||||
@ -448,32 +448,46 @@ bool SctpTransport::safeFlush() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data,
|
int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*addr*/,
|
||||||
size_t len, struct sctp_rcvinfo info, int flags) {
|
const byte *data, size_t len, struct sctp_rcvinfo info, int flags) {
|
||||||
try {
|
try {
|
||||||
PLOG_VERBOSE << "Handle recv, len=" << len;
|
PLOG_VERBOSE << "Handle recv, len=" << len;
|
||||||
if (!len)
|
if (!len)
|
||||||
return -1;
|
return 0; // Ignore
|
||||||
|
|
||||||
// This is valid because SCTP_FRAGMENT_INTERLEAVE is set to level 0
|
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
|
||||||
// so partial messages and notifications may not be interleaved.
|
// therefore partial notifications and messages need to be handled separately.
|
||||||
|
if (flags & MSG_NOTIFICATION) {
|
||||||
|
// SCTP event notification
|
||||||
if (flags & MSG_EOR) {
|
if (flags & MSG_EOR) {
|
||||||
if (!mPartialRecv.empty()) {
|
if (!mPartialNotification.empty()) {
|
||||||
mPartialRecv.insert(mPartialRecv.end(), data, data + len);
|
mPartialNotification.insert(mPartialNotification.end(), data, data + len);
|
||||||
data = mPartialRecv.data();
|
data = mPartialNotification.data();
|
||||||
len = mPartialRecv.size();
|
len = mPartialNotification.size();
|
||||||
}
|
}
|
||||||
// Message/Notification is complete, process it
|
// Notification is complete, process it
|
||||||
if (flags & MSG_NOTIFICATION)
|
|
||||||
processNotification(reinterpret_cast<const union sctp_notification *>(data), len);
|
processNotification(reinterpret_cast<const union sctp_notification *>(data), len);
|
||||||
else
|
mPartialNotification.clear();
|
||||||
processData(data, len, info.rcv_sid, PayloadId(htonl(info.rcv_ppid)));
|
|
||||||
|
|
||||||
mPartialRecv.clear();
|
|
||||||
} else {
|
} else {
|
||||||
// Message/Notification is not complete
|
mPartialNotification.insert(mPartialNotification.end(), data, data + len);
|
||||||
mPartialRecv.insert(mPartialRecv.end(), data, data + len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// SCTP message
|
||||||
|
if (flags & MSG_EOR) {
|
||||||
|
if (!mPartialMessage.empty()) {
|
||||||
|
mPartialMessage.insert(mPartialMessage.end(), data, data + len);
|
||||||
|
data = mPartialMessage.data();
|
||||||
|
len = mPartialMessage.size();
|
||||||
|
}
|
||||||
|
// Message is complete, process it
|
||||||
|
processData(data, len, info.rcv_sid, PayloadId(htonl(info.rcv_ppid)));
|
||||||
|
mPartialMessage.clear();
|
||||||
|
} else {
|
||||||
|
mPartialMessage.insert(mPartialMessage.end(), data, data + len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
PLOG_ERROR << "SCTP recv: " << e.what();
|
PLOG_ERROR << "SCTP recv: " << e.what();
|
||||||
return -1;
|
return -1;
|
||||||
@ -486,7 +500,7 @@ int SctpTransport::handleSend(size_t free) {
|
|||||||
return safeFlush() ? 0 : -1;
|
return safeFlush() ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df) {
|
int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t /*set_df*/) {
|
||||||
try {
|
try {
|
||||||
PLOG_VERBOSE << "Handle write, len=" << len;
|
PLOG_VERBOSE << "Handle write, len=" << len;
|
||||||
|
|
||||||
|
@ -99,7 +99,8 @@ private:
|
|||||||
std::atomic<bool> mWritten = false; // written outside lock
|
std::atomic<bool> mWritten = false; // written outside lock
|
||||||
std::atomic<bool> mWrittenOnce = false; // same
|
std::atomic<bool> mWrittenOnce = false; // same
|
||||||
|
|
||||||
binary mPartialRecv, mPartialStringData, mPartialBinaryData;
|
binary mPartialMessage, mPartialNotification;
|
||||||
|
binary mPartialStringData, mPartialBinaryData;
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#if RTC_ENABLE_WEBSOCKET
|
#if RTC_ENABLE_WEBSOCKET
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -150,7 +151,7 @@ void TcpTransport::connect(const string &hostname, const string &service) {
|
|||||||
|
|
||||||
for (auto p = result; p; p = p->ai_next) {
|
for (auto p = result; p; p = p->ai_next) {
|
||||||
try {
|
try {
|
||||||
connect(p->ai_addr, p->ai_addrlen);
|
connect(p->ai_addr, socklen_t(p->ai_addrlen));
|
||||||
|
|
||||||
PLOG_INFO << "Connected to " << hostname << ":" << service;
|
PLOG_INFO << "Connected to " << hostname << ":" << service;
|
||||||
freeaddrinfo(result);
|
freeaddrinfo(result);
|
||||||
@ -201,7 +202,7 @@ void TcpTransport::connect(const sockaddr *addr, socklen_t addrlen) {
|
|||||||
|
|
||||||
// Initiate connection
|
// Initiate connection
|
||||||
int ret = ::connect(mSock, addr, addrlen);
|
int ret = ::connect(mSock, addr, addrlen);
|
||||||
if (ret < 0 && errno != EINPROGRESS) {
|
if (ret < 0 && sockerrno != SEINPROGRESS && sockerrno != SEWOULDBLOCK) {
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
msg << "TCP connection to " << node << ":" << serv << " failed, errno=" << sockerrno;
|
msg << "TCP connection to " << node << ":" << serv << " failed, errno=" << sockerrno;
|
||||||
throw std::runtime_error(msg.str());
|
throw std::runtime_error(msg.str());
|
||||||
@ -271,14 +272,14 @@ bool TcpTransport::trySendMessage(message_ptr &message) {
|
|||||||
auto data = reinterpret_cast<const char *>(message->data());
|
auto data = reinterpret_cast<const char *>(message->data());
|
||||||
auto size = message->size();
|
auto size = message->size();
|
||||||
while (size) {
|
while (size) {
|
||||||
#if defined(__APPLE__) or defined(_WIN32)
|
#if defined(__APPLE__) || defined(_WIN32)
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
#else
|
#else
|
||||||
int flags = MSG_NOSIGNAL;
|
int flags = MSG_NOSIGNAL;
|
||||||
#endif
|
#endif
|
||||||
int len = ::send(mSock, data, size, flags);
|
int len = ::send(mSock, data, int(size), flags);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
|
||||||
message = make_message(message->end() - size, message->end());
|
message = make_message(message->end() - size, message->end());
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -335,7 +336,7 @@ void TcpTransport::runLoop() {
|
|||||||
char buffer[bufferSize];
|
char buffer[bufferSize];
|
||||||
int len = ::recv(mSock, buffer, bufferSize, 0);
|
int len = ::recv(mSock, buffer, bufferSize, 0);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
if (sockerrno == SEAGAIN || sockerrno == SEWOULDBLOCK) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Connection lost");
|
throw std::runtime_error("Connection lost");
|
||||||
|
@ -48,6 +48,11 @@ gnutls_datum_t make_datum(char *data, size_t size);
|
|||||||
|
|
||||||
#else // USE_GNUTLS==0
|
#else // USE_GNUTLS==0
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Include winsock2.h header first since OpenSSL may include winsock.h
|
||||||
|
#include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
#include <openssl/bio.h>
|
#include <openssl/bio.h>
|
||||||
|
@ -269,9 +269,19 @@ TlsTransport::TlsTransport(shared_ptr<TcpTransport> lower, string host, state_ca
|
|||||||
SSL_CTX_set_quiet_shutdown(mCtx, 1);
|
SSL_CTX_set_quiet_shutdown(mCtx, 1);
|
||||||
SSL_CTX_set_info_callback(mCtx, InfoCallback);
|
SSL_CTX_set_info_callback(mCtx, InfoCallback);
|
||||||
|
|
||||||
SSL_CTX_set_default_verify_paths(mCtx);
|
// SSL_CTX_set_default_verify_paths() does nothing on Windows
|
||||||
|
#ifndef _WIN32
|
||||||
|
if (SSL_CTX_set_default_verify_paths(mCtx)) {
|
||||||
|
#else
|
||||||
|
if (false) {
|
||||||
|
#endif
|
||||||
|
PLOG_INFO << "SSL root CA certificates available, server verification enabled";
|
||||||
SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER, NULL);
|
SSL_CTX_set_verify(mCtx, SSL_VERIFY_PEER, NULL);
|
||||||
SSL_CTX_set_verify_depth(mCtx, 4);
|
SSL_CTX_set_verify_depth(mCtx, 4);
|
||||||
|
} else {
|
||||||
|
PLOG_WARNING << "SSL root CA certificates unavailable, server verification disabled";
|
||||||
|
SSL_CTX_set_verify(mCtx, SSL_VERIFY_NONE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (!(mSsl = SSL_new(mCtx)))
|
if (!(mSsl = SSL_new(mCtx)))
|
||||||
throw std::runtime_error("Failed to create SSL instance");
|
throw std::runtime_error("Failed to create SSL instance");
|
||||||
@ -337,7 +347,7 @@ bool TlsTransport::send(message_ptr message) {
|
|||||||
if (message->size() == 0)
|
if (message->size() == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
int ret = SSL_write(mSsl, message->data(), message->size());
|
int ret = SSL_write(mSsl, message->data(), int(message->size()));
|
||||||
if (!openssl::check(mSsl, ret))
|
if (!openssl::check(mSsl, ret))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -393,7 +403,7 @@ void TlsTransport::runRecvLoop() {
|
|||||||
|
|
||||||
message_ptr message = *next;
|
message_ptr message = *next;
|
||||||
if (message->size() > 0)
|
if (message->size() > 0)
|
||||||
BIO_write(mInBio, message->data(), message->size()); // Input
|
BIO_write(mInBio, message->data(), int(message->size())); // Input
|
||||||
else
|
else
|
||||||
recv(message); // Pass zero-sized messages through
|
recv(message); // Pass zero-sized messages through
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ using std::to_integer;
|
|||||||
using std::to_string;
|
using std::to_string;
|
||||||
|
|
||||||
using random_bytes_engine =
|
using random_bytes_engine =
|
||||||
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned char>;
|
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned short>;
|
||||||
|
|
||||||
WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string path,
|
WsTransport::WsTransport(std::shared_ptr<Transport> lower, string host, string path,
|
||||||
message_callback recvCallback, state_callback stateCallback)
|
message_callback recvCallback, state_callback stateCallback)
|
||||||
@ -145,12 +145,12 @@ void WsTransport::close() {
|
|||||||
bool WsTransport::sendHttpRequest() {
|
bool WsTransport::sendHttpRequest() {
|
||||||
changeState(State::Connecting);
|
changeState(State::Connecting);
|
||||||
|
|
||||||
auto seed = system_clock::now().time_since_epoch().count();
|
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||||
random_bytes_engine generator(seed);
|
random_bytes_engine generator(seed);
|
||||||
|
|
||||||
binary key(16);
|
binary key(16);
|
||||||
auto k = reinterpret_cast<uint8_t *>(key.data());
|
auto k = reinterpret_cast<uint8_t *>(key.data());
|
||||||
std::generate(k, k + key.size(), generator);
|
std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); });
|
||||||
|
|
||||||
const string request = "GET " + mPath +
|
const string request = "GET " + mPath +
|
||||||
" HTTP/1.1\r\n"
|
" HTTP/1.1\r\n"
|
||||||
@ -283,7 +283,7 @@ size_t WsTransport::readFrame(byte *buffer, size_t size, Frame &frame) {
|
|||||||
cur += 4;
|
cur += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end - cur < frame.length)
|
if (size_t(end - cur) < frame.length)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
frame.payload = cur;
|
frame.payload = cur;
|
||||||
@ -292,7 +292,7 @@ size_t WsTransport::readFrame(byte *buffer, size_t size, Frame &frame) {
|
|||||||
frame.payload[i] ^= maskingKey[i % 4];
|
frame.payload[i] ^= maskingKey[i % 4];
|
||||||
cur += frame.length;
|
cur += frame.length;
|
||||||
|
|
||||||
return cur - buffer;
|
return size_t(cur - buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WsTransport::recvFrame(const Frame &frame) {
|
void WsTransport::recvFrame(const Frame &frame) {
|
||||||
@ -378,13 +378,13 @@ bool WsTransport::sendFrame(const Frame &frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frame.mask) {
|
if (frame.mask) {
|
||||||
auto seed = system_clock::now().time_since_epoch().count();
|
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||||
random_bytes_engine generator(seed);
|
random_bytes_engine generator(seed);
|
||||||
|
|
||||||
byte *maskingKey = reinterpret_cast<byte *>(cur);
|
byte *maskingKey = reinterpret_cast<byte *>(cur);
|
||||||
|
|
||||||
auto u = reinterpret_cast<uint8_t *>(maskingKey);
|
auto u = reinterpret_cast<uint8_t *>(maskingKey);
|
||||||
std::generate(u, u + 4, generator);
|
std::generate(u, u + 4, [&]() { return uint8_t(generator()); });
|
||||||
cur += 4;
|
cur += 4;
|
||||||
|
|
||||||
for (size_t i = 0; i < frame.length; ++i)
|
for (size_t i = 0; i < frame.length; ++i)
|
||||||
|
@ -95,13 +95,12 @@ size_t benchmark(milliseconds duration) {
|
|||||||
fill(messageData.begin(), messageData.end(), byte(0xFF));
|
fill(messageData.begin(), messageData.end(), byte(0xFF));
|
||||||
|
|
||||||
atomic<size_t> receivedSize = 0;
|
atomic<size_t> receivedSize = 0;
|
||||||
atomic<bool> finished = false;
|
|
||||||
|
|
||||||
steady_clock::time_point startTime, openTime, receivedTime, endTime;
|
steady_clock::time_point startTime, openTime, receivedTime, endTime;
|
||||||
|
|
||||||
shared_ptr<DataChannel> dc2;
|
shared_ptr<DataChannel> dc2;
|
||||||
pc2->onDataChannel(
|
pc2->onDataChannel(
|
||||||
[&dc2, &finished, &receivedSize, &receivedTime, &endTime](shared_ptr<DataChannel> dc) {
|
[&dc2, &receivedSize, &receivedTime](shared_ptr<DataChannel> dc) {
|
||||||
dc->onMessage([&receivedTime, &receivedSize](const variant<binary, string> &message) {
|
dc->onMessage([&receivedTime, &receivedSize](const variant<binary, string> &message) {
|
||||||
if (holds_alternative<binary>(message)) {
|
if (holds_alternative<binary>(message)) {
|
||||||
const auto &bin = get<binary>(message);
|
const auto &bin = get<binary>(message);
|
||||||
@ -111,11 +110,7 @@ size_t benchmark(milliseconds duration) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
dc->onClosed([&finished, &endTime]() {
|
dc->onClosed([]() { cout << "DataChannel closed." << endl; });
|
||||||
cout << "DataChannel closed." << endl;
|
|
||||||
endTime = steady_clock::now();
|
|
||||||
finished = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::atomic_store(&dc2, dc);
|
std::atomic_store(&dc2, dc);
|
||||||
});
|
});
|
||||||
@ -157,11 +152,9 @@ size_t benchmark(milliseconds duration) {
|
|||||||
cout << "Received: " << receivedSize.load() / 1000 << " KB" << endl;
|
cout << "Received: " << receivedSize.load() / 1000 << " KB" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto adc2 = std::atomic_load(&dc2)) {
|
|
||||||
dc1->close();
|
dc1->close();
|
||||||
while (!finished && adc2->isOpen())
|
|
||||||
this_thread::sleep_for(100ms);
|
endTime = steady_clock::now();
|
||||||
}
|
|
||||||
|
|
||||||
auto connectDuration = duration_cast<milliseconds>(openTime - startTime);
|
auto connectDuration = duration_cast<milliseconds>(openTime - startTime);
|
||||||
auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);
|
auto transferDuration = duration_cast<milliseconds>(endTime - receivedTime);
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
#include <rtc/rtc.h>
|
#include <rtc/rtc.h>
|
||||||
|
|
||||||
#include <cstdbool>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -136,7 +135,7 @@ static void deletePeer(Peer *peer) {
|
|||||||
int test_capi_main() {
|
int test_capi_main() {
|
||||||
int attempts;
|
int attempts;
|
||||||
|
|
||||||
rtcInitLogger(RTC_LOG_DEBUG);
|
rtcInitLogger(RTC_LOG_DEBUG, nullptr);
|
||||||
|
|
||||||
// Create peer 1
|
// Create peer 1
|
||||||
rtcConfiguration config1;
|
rtcConfiguration config1;
|
||||||
|
Reference in New Issue
Block a user