mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
284db56615 | |||
46a3d58cb8 | |||
dd05e4b8ce | |||
f57b860649 | |||
24e7872695 | |||
8348b70ee6 | |||
9a343d5301 | |||
c198ffd994 | |||
6eb92301fb | |||
22e71bd663 | |||
9436757f73 | |||
b3bba4286b | |||
f8df667a14 | |||
c18b1738b0 | |||
96501a8a68 | |||
3be2b0427f | |||
5ae311c50a | |||
7a2e0c267d | |||
5e59186757 | |||
c5c9e24a01 | |||
2c953e77a9 | |||
ddb9f99ed6 | |||
3a737e940c | |||
258135d070 | |||
6302d995f7 | |||
6a7296d40d | |||
88c88bbaf5 | |||
c525c4b3f8 | |||
2c58fd7659 | |||
d0f91d5cf4 | |||
90ce154e15 | |||
b956e45f33 | |||
be1013fe7a | |||
d123041180 | |||
b5511f71a5 | |||
8e08ba1a29 | |||
3713b520db | |||
8c03c24e03 | |||
86b9bace53 | |||
049d339554 | |||
e86ecc2c97 | |||
de51b9adc7 | |||
0f0047729b | |||
2729b247fa | |||
32800c1c1c | |||
f153e2c795 | |||
cb79ec0023 | |||
3b98c9d1ec | |||
e075e9a7ec | |||
c89163610b | |||
c0edf3bfde | |||
c0470d813f | |||
a61c173b8c | |||
980c456de8 |
40
.github/workflows/build-gnutls.yml
vendored
Normal file
40
.github/workflows/build-gnutls.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
name: Build and test with GnuTLS
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
build-ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev
|
||||||
|
- name: submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
- name: cmake
|
||||||
|
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
|
||||||
|
- name: make
|
||||||
|
run: (cd build; make -j2)
|
||||||
|
- name: test
|
||||||
|
run: ./build/tests
|
||||||
|
build-macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: brew install gnutls nettle
|
||||||
|
- name: submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
- name: cmake
|
||||||
|
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
|
||||||
|
run: (cd build; make -j2)
|
||||||
|
- name: test
|
||||||
|
run: ./build/tests
|
24
.github/workflows/build-nice.yml
vendored
Normal file
24
.github/workflows/build-nice.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: Build and test with libnice
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
build-ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: sudo apt update && sudo apt install libgnutls28-dev libnice-dev
|
||||||
|
- name: submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
- name: cmake
|
||||||
|
run: cmake -B build -DUSE_JUICE=0 -DUSE_GNUTLS=1
|
||||||
|
- name: make
|
||||||
|
run: (cd build; make -j2)
|
||||||
|
- name: test
|
||||||
|
run: ./build/tests
|
||||||
|
|
42
.github/workflows/build-openssl.yml
vendored
Normal file
42
.github/workflows/build-openssl.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
name: Build and test with OpenSSL
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
build-ubuntu:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: sudo apt update && sudo apt install libssl-dev
|
||||||
|
- name: submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
- name: cmake
|
||||||
|
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=0
|
||||||
|
- name: make
|
||||||
|
run: (cd build; make -j2)
|
||||||
|
- name: test
|
||||||
|
run: ./build/tests
|
||||||
|
build-macos:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: install packages
|
||||||
|
run: brew reinstall openssl@1.1
|
||||||
|
- name: submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
- name: cmake
|
||||||
|
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=0
|
||||||
|
env:
|
||||||
|
OPENSSL_ROOT_DIR: /usr/local/opt/openssl
|
||||||
|
OPENSSL_LIBRARIES: /usr/local/opt/openssl/lib
|
||||||
|
# hack to bypass EPERM issue on sendto()
|
||||||
|
CFLAGS: -DJUICE_ENABLE_ADDRS_LOCALHOST
|
||||||
|
- name: make
|
||||||
|
run: (cd build; make -j2)
|
||||||
|
- name: test
|
||||||
|
run: ./build/tests
|
21
.github/workflows/build.yml
vendored
21
.github/workflows/build.yml
vendored
@ -1,21 +0,0 @@
|
|||||||
name: Build and test
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: install packages
|
|
||||||
run: sudo apt update && sudo apt install libgnutls28-dev nettle-dev
|
|
||||||
- name: submodules
|
|
||||||
run: git submodule update --init --recursive
|
|
||||||
- name: cmake
|
|
||||||
run: cmake -B build -DUSE_JUICE=1 -DUSE_GNUTLS=1
|
|
||||||
- name: make
|
|
||||||
run: (cd build; make)
|
|
||||||
- name: test
|
|
||||||
run: ./build/tests
|
|
||||||
|
|
@ -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.4.9
|
VERSION 0.5.1
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
option(USE_GNUTLS "Use GnuTLS instead of OpenSSL" OFF)
|
||||||
@ -18,8 +18,8 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
|
|||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||||
if (MSYS OR MINGW)
|
if (MSVC)
|
||||||
add_definitions(-DSCTP_STDINT_INCLUDE=<stdint.h>)
|
add_definitions(-DNOMINMAX)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -71,10 +71,15 @@ set(TESTS_ANSWERER_SOURCES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/p2p/answerer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/p2p/answerer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
|
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
|
||||||
|
if (MSYS OR MINGW)
|
||||||
|
target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
||||||
|
target_compile_definitions(usrsctp-static PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
||||||
|
endif()
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
target_compile_options(usrsctp PRIVATE -Wno-error=format-truncation)
|
target_compile_options(usrsctp PRIVATE -Wno-error=format-truncation)
|
||||||
target_compile_options(usrsctp-static PRIVATE -Wno-error=format-truncation)
|
target_compile_options(usrsctp-static PRIVATE -Wno-error=format-truncation)
|
||||||
@ -88,10 +93,11 @@ set_target_properties(datachannel PROPERTIES
|
|||||||
CXX_STANDARD 17)
|
CXX_STANDARD 17)
|
||||||
|
|
||||||
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
|
||||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||||
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
|
target_link_libraries(datachannel PUBLIC Threads::Threads)
|
||||||
target_link_libraries(datachannel Threads::Threads Usrsctp::UsrsctpStatic)
|
target_link_libraries(datachannel PRIVATE Usrsctp::UsrsctpStatic)
|
||||||
|
|
||||||
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
|
add_library(datachannel-static STATIC EXCLUDE_FROM_ALL ${LIBDATACHANNEL_SOURCES})
|
||||||
set_target_properties(datachannel-static PROPERTIES
|
set_target_properties(datachannel-static PROPERTIES
|
||||||
@ -99,14 +105,15 @@ set_target_properties(datachannel-static PROPERTIES
|
|||||||
CXX_STANDARD 17)
|
CXX_STANDARD 17)
|
||||||
|
|
||||||
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
|
||||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/rtc)
|
||||||
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel-static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
target_include_directories(datachannel-static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/plog/include)
|
target_link_libraries(datachannel-static PUBLIC Threads::Threads)
|
||||||
target_link_libraries(datachannel-static Threads::Threads Usrsctp::UsrsctpStatic)
|
target_link_libraries(datachannel-static PRIVATE Usrsctp::UsrsctpStatic)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(datachannel "wsock32" "ws2_32") # winsock2
|
target_link_libraries(datachannel PRIVATE wsock32 ws2_32) # winsock2
|
||||||
target_link_libraries(datachannel-static "wsock32" "ws2_32") # winsock2
|
target_link_libraries(datachannel-static PRIVATE wsock32 ws2_32) # winsock2
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_GNUTLS)
|
if (USE_GNUTLS)
|
||||||
@ -120,29 +127,29 @@ if (USE_GNUTLS)
|
|||||||
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
|
IMPORTED_LOCATION "${GNUTLS_LIBRARIES}")
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=1)
|
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=1)
|
||||||
target_link_libraries(datachannel GnuTLS::GnuTLS)
|
target_link_libraries(datachannel PRIVATE GnuTLS::GnuTLS)
|
||||||
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=1)
|
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=1)
|
||||||
target_link_libraries(datachannel-static GnuTLS::GnuTLS)
|
target_link_libraries(datachannel-static PRIVATE GnuTLS::GnuTLS)
|
||||||
else()
|
else()
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=0)
|
target_compile_definitions(datachannel PRIVATE USE_GNUTLS=0)
|
||||||
target_link_libraries(datachannel OpenSSL::SSL)
|
target_link_libraries(datachannel PRIVATE OpenSSL::SSL)
|
||||||
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=0)
|
target_compile_definitions(datachannel-static PRIVATE USE_GNUTLS=0)
|
||||||
target_link_libraries(datachannel-static OpenSSL::SSL)
|
target_link_libraries(datachannel-static PRIVATE OpenSSL::SSL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (USE_JUICE)
|
if (USE_JUICE)
|
||||||
add_subdirectory(deps/libjuice EXCLUDE_FROM_ALL)
|
add_subdirectory(deps/libjuice EXCLUDE_FROM_ALL)
|
||||||
target_compile_definitions(datachannel PRIVATE USE_JUICE=1)
|
target_compile_definitions(datachannel PRIVATE USE_JUICE=1)
|
||||||
target_link_libraries(datachannel LibJuice::LibJuiceStatic)
|
target_link_libraries(datachannel PRIVATE LibJuice::LibJuiceStatic)
|
||||||
target_compile_definitions(datachannel-static PRIVATE USE_JUICE=1)
|
target_compile_definitions(datachannel-static PRIVATE USE_JUICE=1)
|
||||||
target_link_libraries(datachannel-static LibJuice::LibJuiceStatic)
|
target_link_libraries(datachannel-static PRIVATE LibJuice::LibJuiceStatic)
|
||||||
else()
|
else()
|
||||||
find_package(LibNice REQUIRED)
|
find_package(LibNice REQUIRED)
|
||||||
target_compile_definitions(datachannel PRIVATE USE_JUICE=0)
|
target_compile_definitions(datachannel PRIVATE USE_JUICE=0)
|
||||||
target_link_libraries(datachannel LibNice::LibNice)
|
target_link_libraries(datachannel PRIVATE LibNice::LibNice)
|
||||||
target_compile_definitions(datachannel-static PRIVATE USE_JUICE=0)
|
target_compile_definitions(datachannel-static PRIVATE USE_JUICE=0)
|
||||||
target_link_libraries(datachannel-static LibNice::LibNice)
|
target_link_libraries(datachannel-static PRIVATE LibNice::LibNice)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
|
add_library(LibDataChannel::LibDataChannel ALIAS datachannel)
|
||||||
|
27
Jamfile
27
Jamfile
@ -1,3 +1,5 @@
|
|||||||
|
import feature : feature ;
|
||||||
|
|
||||||
project libdatachannel ;
|
project libdatachannel ;
|
||||||
path-constant CWD : . ;
|
path-constant CWD : . ;
|
||||||
|
|
||||||
@ -5,21 +7,26 @@ lib libdatachannel
|
|||||||
: # sources
|
: # sources
|
||||||
[ glob ./src/*.cpp ]
|
[ glob ./src/*.cpp ]
|
||||||
: # requirements
|
: # requirements
|
||||||
|
<cxxstd>17
|
||||||
<include>./include/rtc
|
<include>./include/rtc
|
||||||
<define>USE_GNUTLS=0
|
|
||||||
<define>USE_JUICE=1
|
<define>USE_JUICE=1
|
||||||
<cxxflags>"`pkg-config --cflags openssl`"
|
|
||||||
<library>/libdatachannel//usrsctp
|
<library>/libdatachannel//usrsctp
|
||||||
<library>/libdatachannel//juice
|
<library>/libdatachannel//juice
|
||||||
|
<library>/libdatachannel//plog
|
||||||
: # default build
|
: # default build
|
||||||
<link>static
|
<link>static
|
||||||
: # usage requirements
|
: # usage requirements
|
||||||
<include>./include
|
<include>./include
|
||||||
<library>/libdatachannel//plog
|
<library>/libdatachannel//plog
|
||||||
<cxxflags>-pthread
|
<cxxflags>-pthread
|
||||||
<linkflags>"`pkg-config --libs openssl`"
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
feature crypto : openssl gnutls : composite propagated ;
|
||||||
|
feature.compose <crypto>openssl
|
||||||
|
: <define>USE_GNUTLS=0 ;
|
||||||
|
feature.compose <crypto>gnutls
|
||||||
|
: <define>USE_GNUTLS=1 ;
|
||||||
|
|
||||||
alias plog
|
alias plog
|
||||||
: # no sources
|
: # no sources
|
||||||
: # no build requirements
|
: # no build requirements
|
||||||
@ -57,9 +64,21 @@ actions make_libusrsctp
|
|||||||
}
|
}
|
||||||
|
|
||||||
make libjuice.a : : @make_libjuice ;
|
make libjuice.a : : @make_libjuice ;
|
||||||
|
|
||||||
|
rule make_libjuice ( targets * : sources * : properties * )
|
||||||
|
{
|
||||||
|
if <crypto>gnutls in $(properties)
|
||||||
|
{
|
||||||
|
MAKEOPTS on $(targets) = "USE_NETTLE=1" ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MAKEOPTS on $(targets) = "USE_NETTLE=0" ;
|
||||||
|
}
|
||||||
|
}
|
||||||
actions make_libjuice
|
actions make_libjuice
|
||||||
{
|
{
|
||||||
(cd $(CWD)/deps/libjuice && make USE_NETTLE=0)
|
(cd $(CWD)/deps/libjuice && make $(MAKEOPTS))
|
||||||
cp $(CWD)/deps/libjuice/libjuice.a $(<)
|
cp $(CWD)/deps/libjuice/libjuice.a $(<)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ if (NOT TARGET LibNice::LibNice)
|
|||||||
HINTS ${PC_LIBNICE_LIBDIR} ${PC_LIBNICE_LIBRARY_DIRS})
|
HINTS ${PC_LIBNICE_LIBDIR} ${PC_LIBNICE_LIBRARY_DIRS})
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
include(FindPackageHandleStandardArgs)
|
||||||
find_package_handle_standard_args(Libnice DEFAULT_MSG
|
find_package_handle_standard_args(LibNice DEFAULT_MSG
|
||||||
LIBNICE_LIBRARY LIBNICE_INCLUDE_DIR)
|
LIBNICE_LIBRARY LIBNICE_INCLUDE_DIR)
|
||||||
mark_as_advanced(LIBNICE_INCLUDE_DIR LIBNICE_LIBRARY)
|
mark_as_advanced(LIBNICE_INCLUDE_DIR LIBNICE_LIBRARY)
|
||||||
|
|
||||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: a6c5c9a393...6f6faa5783
@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
synchronized_callback &operator=(std::function<void(P...)> func) {
|
synchronized_callback &operator=(std::function<void(P...)> func) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
callback = func;
|
callback = std::move(func);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,10 @@ public:
|
|||||||
callback(args...);
|
callback(args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const { return callback ? true : false; }
|
operator bool() const {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
return callback ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(P...)> callback;
|
std::function<void(P...)> callback;
|
||||||
|
@ -28,9 +28,9 @@
|
|||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
struct Message : binary {
|
struct Message : binary {
|
||||||
enum Type { Binary, String, Control };
|
enum Type { Binary, String, Control, Reset };
|
||||||
|
|
||||||
Message(size_t size) : binary(size), type(Binary) {}
|
Message(size_t size, Type type_ = Binary) : binary(size), type(type_) {}
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
Message(Iterator begin_, Iterator end_, Type type_ = Binary)
|
Message(Iterator begin_, Iterator end_, Type type_ = Binary)
|
||||||
@ -46,7 +46,7 @@ using mutable_message_ptr = std::shared_ptr<Message>;
|
|||||||
using message_callback = std::function<void(message_ptr message)>;
|
using message_callback = std::function<void(message_ptr message)>;
|
||||||
|
|
||||||
constexpr auto message_size_func = [](const message_ptr &m) -> size_t {
|
constexpr auto message_size_func = [](const message_ptr &m) -> size_t {
|
||||||
return m->type != Message::Control ? m->size() : 0;
|
return m->type == Message::Binary || m->type == Message::String ? m->size() : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Iterator>
|
template <typename Iterator>
|
||||||
@ -59,6 +59,15 @@ message_ptr make_message(Iterator begin, Iterator end, Message::Type type = Mess
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline message_ptr make_message(size_t size, Message::Type type = Message::Binary,
|
||||||
|
unsigned int stream = 0,
|
||||||
|
std::shared_ptr<Reliability> reliability = nullptr) {
|
||||||
|
auto message = std::make_shared<Message>(size, type);
|
||||||
|
message->stream = stream;
|
||||||
|
message->reliability = reliability;
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
@ -44,6 +45,9 @@ class IceTransport;
|
|||||||
class DtlsTransport;
|
class DtlsTransport;
|
||||||
class SctpTransport;
|
class SctpTransport;
|
||||||
|
|
||||||
|
using certificate_ptr = std::shared_ptr<Certificate>;
|
||||||
|
using future_certificate_ptr = std::shared_future<certificate_ptr>;
|
||||||
|
|
||||||
class PeerConnection : public std::enable_shared_from_this<PeerConnection> {
|
class PeerConnection : public std::enable_shared_from_this<PeerConnection> {
|
||||||
public:
|
public:
|
||||||
enum class State : int {
|
enum class State : int {
|
||||||
@ -126,7 +130,7 @@ private:
|
|||||||
void resetCallbacks();
|
void resetCallbacks();
|
||||||
|
|
||||||
const Configuration mConfig;
|
const Configuration mConfig;
|
||||||
const std::shared_ptr<Certificate> mCertificate;
|
const future_certificate_ptr mCertificate;
|
||||||
|
|
||||||
std::optional<Description> mLocalDescription, mRemoteDescription;
|
std::optional<Description> mLocalDescription, mRemoteDescription;
|
||||||
mutable std::recursive_mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;
|
mutable std::recursive_mutex mLocalDescriptionMutex, mRemoteDescriptionMutex;
|
||||||
|
@ -114,6 +114,9 @@ int rtcGetAvailableAmount(int dc); // total size available to receive
|
|||||||
int rtcSetAvailableCallback(int dc, availableCallbackFunc cb);
|
int rtcSetAvailableCallback(int dc, availableCallbackFunc cb);
|
||||||
int rtcReceiveMessage(int dc, char *buffer, int *size);
|
int rtcReceiveMessage(int dc, char *buffer, int *size);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
void rtcCleanup();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
@ -60,12 +60,19 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
if (mIsResolved)
|
if (mIsResolved)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
PLOG_VERBOSE << "Resolving candidate (mode="
|
||||||
|
<< (mode == ResolveMode::Simple ? "simple" : "lookup")
|
||||||
|
<< "): " << mCandidate;
|
||||||
|
|
||||||
// See RFC 8445 for format
|
// See RFC 8445 for format
|
||||||
std::stringstream ss(mCandidate);
|
std::istringstream iss(mCandidate);
|
||||||
int component{0}, priority{0};
|
int component{0}, priority{0};
|
||||||
string foundation, transport, node, service, typ_, type;
|
string foundation, transport, node, service, typ_, type;
|
||||||
if (ss >> foundation >> component >> transport >> priority &&
|
if (iss >> foundation >> component >> transport >> priority &&
|
||||||
ss >> node >> service >> typ_ >> type && typ_ == "typ") {
|
iss >> node >> service >> typ_ >> type && typ_ == "typ") {
|
||||||
|
|
||||||
|
string left;
|
||||||
|
std::getline(iss, left);
|
||||||
|
|
||||||
// Try to resolve the node
|
// Try to resolve the node
|
||||||
struct addrinfo hints = {};
|
struct addrinfo hints = {};
|
||||||
@ -94,15 +101,13 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
|
if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN,
|
||||||
servbuffer, MAX_NUMERICSERV_LEN,
|
servbuffer, MAX_NUMERICSERV_LEN,
|
||||||
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||||
string left;
|
|
||||||
std::getline(ss, left);
|
|
||||||
const char sp{' '};
|
const char sp{' '};
|
||||||
ss.clear();
|
std::ostringstream oss;
|
||||||
ss << foundation << sp << component << sp << transport << sp << priority;
|
oss << foundation << sp << component << sp << transport << sp << priority;
|
||||||
ss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
|
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
|
||||||
if (!left.empty())
|
oss << left;
|
||||||
ss << left;
|
mCandidate = oss.str();
|
||||||
mCandidate = ss.str();
|
PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
|
||||||
return mIsResolved = true;
|
return mIsResolved = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,14 +141,9 @@ string make_fingerprint(gnutls_x509_crt_t crt) {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<Certificate> make_certificate(const string &commonName) {
|
namespace {
|
||||||
static std::unordered_map<string, shared_ptr<Certificate>> cache;
|
|
||||||
static std::mutex cacheMutex;
|
|
||||||
|
|
||||||
std::lock_guard lock(cacheMutex);
|
|
||||||
if (auto it = cache.find(commonName); it != cache.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
|
certificate_ptr make_certificate_impl(string commonName) {
|
||||||
std::unique_ptr<gnutls_x509_crt_t, decltype(&delete_crt)> crt(create_crt(), delete_crt);
|
std::unique_ptr<gnutls_x509_crt_t, decltype(&delete_crt)> crt(create_crt(), delete_crt);
|
||||||
std::unique_ptr<gnutls_x509_privkey_t, decltype(&delete_privkey)> privkey(create_privkey(),
|
std::unique_ptr<gnutls_x509_privkey_t, decltype(&delete_privkey)> privkey(create_privkey(),
|
||||||
delete_privkey);
|
delete_privkey);
|
||||||
@ -174,11 +169,11 @@ shared_ptr<Certificate> make_certificate(const string &commonName) {
|
|||||||
check_gnutls(gnutls_x509_crt_sign2(*crt, *crt, *privkey, GNUTLS_DIG_SHA256, 0),
|
check_gnutls(gnutls_x509_crt_sign2(*crt, *crt, *privkey, GNUTLS_DIG_SHA256, 0),
|
||||||
"Unable to auto-sign certificate");
|
"Unable to auto-sign certificate");
|
||||||
|
|
||||||
auto certificate = std::make_shared<Certificate>(*crt, *privkey);
|
return std::make_shared<Certificate>(*crt, *privkey);
|
||||||
cache.emplace(std::make_pair(commonName, certificate));
|
|
||||||
return certificate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@ -236,15 +231,9 @@ string make_fingerprint(X509 *x509) {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
shared_ptr<Certificate> make_certificate(const string &commonName) {
|
certificate_ptr make_certificate_impl(string commonName) {
|
||||||
static std::unordered_map<string, shared_ptr<Certificate>> cache;
|
|
||||||
static std::mutex cacheMutex;
|
|
||||||
|
|
||||||
std::lock_guard lock(cacheMutex);
|
|
||||||
if (auto it = cache.find(commonName); it != cache.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
shared_ptr<X509> x509(X509_new(), X509_free);
|
shared_ptr<X509> x509(X509_new(), X509_free);
|
||||||
shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
|
shared_ptr<EVP_PKEY> pkey(EVP_PKEY_new(), EVP_PKEY_free);
|
||||||
|
|
||||||
@ -265,7 +254,8 @@ shared_ptr<Certificate> make_certificate(const string &commonName) {
|
|||||||
throw std::runtime_error("Unable to generate key pair");
|
throw std::runtime_error("Unable to generate key pair");
|
||||||
|
|
||||||
const size_t serialSize = 16;
|
const size_t serialSize = 16;
|
||||||
const auto *commonNameBytes = reinterpret_cast<const unsigned char *>(commonName.c_str());
|
auto *commonNameBytes =
|
||||||
|
reinterpret_cast<unsigned char *>(const_cast<char *>(commonName.c_str()));
|
||||||
|
|
||||||
if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 3600 * -1) ||
|
if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 3600 * -1) ||
|
||||||
!X509_gmtime_adj(X509_get_notAfter(x509.get()), 3600 * 24 * 365) ||
|
!X509_gmtime_adj(X509_get_notAfter(x509.get()), 3600 * 24 * 365) ||
|
||||||
@ -281,12 +271,54 @@ shared_ptr<Certificate> make_certificate(const string &commonName) {
|
|||||||
if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
|
if (!X509_sign(x509.get(), pkey.get(), EVP_sha256()))
|
||||||
throw std::runtime_error("Unable to auto-sign certificate");
|
throw std::runtime_error("Unable to auto-sign certificate");
|
||||||
|
|
||||||
auto certificate = std::make_shared<Certificate>(x509, pkey);
|
return std::make_shared<Certificate>(x509, pkey);
|
||||||
cache.emplace(std::make_pair(commonName, certificate));
|
|
||||||
return certificate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Common for GnuTLS and OpenSSL
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
template <class F, class... Args>
|
||||||
|
std::future<std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>> thread_call(F &&f,
|
||||||
|
Args &&... args) {
|
||||||
|
using R = std::result_of_t<std::decay_t<F>(std::decay_t<Args>...)>;
|
||||||
|
std::packaged_task<R()> task(std::bind(f, std::forward<Args>(args)...));
|
||||||
|
std::future<R> future = task.get_future();
|
||||||
|
std::thread t(std::move(task));
|
||||||
|
t.detach();
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<string, future_certificate_ptr> CertificateCache;
|
||||||
|
static std::mutex CertificateCacheMutex;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
future_certificate_ptr make_certificate(string commonName) {
|
||||||
|
std::lock_guard lock(CertificateCacheMutex);
|
||||||
|
|
||||||
|
if (auto it = CertificateCache.find(commonName); it != CertificateCache.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
auto future = thread_call(make_certificate_impl, commonName);
|
||||||
|
auto shared = future.share();
|
||||||
|
CertificateCache.emplace(std::move(commonName), shared);
|
||||||
|
return shared;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CleanupCertificateCache() {
|
||||||
|
std::lock_guard lock(CertificateCacheMutex);
|
||||||
|
CertificateCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rtc
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "include.hpp"
|
#include "include.hpp"
|
||||||
|
|
||||||
|
#include <future>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#if USE_GNUTLS
|
#if USE_GNUTLS
|
||||||
@ -62,7 +63,12 @@ string make_fingerprint(gnutls_x509_crt_t crt);
|
|||||||
string make_fingerprint(X509 *x509);
|
string make_fingerprint(X509 *x509);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::shared_ptr<Certificate> make_certificate(const string &commonName);
|
using certificate_ptr = std::shared_ptr<Certificate>;
|
||||||
|
using future_certificate_ptr = std::shared_future<certificate_ptr>;
|
||||||
|
|
||||||
|
future_certificate_ptr make_certificate(string commonName); // cached
|
||||||
|
|
||||||
|
void CleanupCertificateCache();
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
|
@ -93,19 +93,20 @@ string DataChannel::protocol() const { return mProtocol; }
|
|||||||
Reliability DataChannel::reliability() const { return *mReliability; }
|
Reliability DataChannel::reliability() const { return *mReliability; }
|
||||||
|
|
||||||
void DataChannel::close() {
|
void DataChannel::close() {
|
||||||
|
mIsClosed = true;
|
||||||
if (mIsOpen.exchange(false))
|
if (mIsOpen.exchange(false))
|
||||||
if (auto transport = mSctpTransport.lock())
|
if (auto transport = mSctpTransport.lock())
|
||||||
transport->reset(mStream);
|
transport->close(mStream);
|
||||||
mIsClosed = true;
|
|
||||||
mSctpTransport.reset();
|
|
||||||
|
|
||||||
|
mSctpTransport.reset();
|
||||||
resetCallbacks();
|
resetCallbacks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataChannel::remoteClose() {
|
void DataChannel::remoteClose() {
|
||||||
mIsOpen = false;
|
|
||||||
if (!mIsClosed.exchange(true))
|
if (!mIsClosed.exchange(true))
|
||||||
triggerClosed();
|
triggerClosed();
|
||||||
|
|
||||||
|
mIsOpen = false;
|
||||||
mSctpTransport.reset();
|
mSctpTransport.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +140,9 @@ std::optional<std::variant<binary, string>> DataChannel::receive() {
|
|||||||
string(reinterpret_cast<const char *>(message->data()), message->size()));
|
string(reinterpret_cast<const char *>(message->data()), message->size()));
|
||||||
case Message::Binary:
|
case Message::Binary:
|
||||||
return std::make_optional(std::move(*message));
|
return std::make_optional(std::move(*message));
|
||||||
|
default:
|
||||||
|
// Ignore
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +63,8 @@ void DtlsTransport::Cleanup() {
|
|||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
|
DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr certificate,
|
||||||
verifier_callback verifierCallback,
|
verifier_callback verifierCallback, state_callback stateChangeCallback)
|
||||||
state_callback stateChangeCallback)
|
|
||||||
: Transport(lower), mCertificate(certificate), mState(State::Disconnected),
|
: Transport(lower), mCertificate(certificate), mState(State::Disconnected),
|
||||||
mVerifierCallback(std::move(verifierCallback)),
|
mVerifierCallback(std::move(verifierCallback)),
|
||||||
mStateChangeCallback(std::move(stateChangeCallback)) {
|
mStateChangeCallback(std::move(stateChangeCallback)) {
|
||||||
@ -100,6 +99,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
|||||||
gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback);
|
gnutls_transport_set_pull_timeout_function(mSession, TimeoutCallback);
|
||||||
|
|
||||||
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
|
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
|
||||||
|
registerIncoming();
|
||||||
}
|
}
|
||||||
|
|
||||||
DtlsTransport::~DtlsTransport() {
|
DtlsTransport::~DtlsTransport() {
|
||||||
@ -116,9 +116,7 @@ bool DtlsTransport::stop() {
|
|||||||
|
|
||||||
PLOG_DEBUG << "Stopping DTLS recv thread";
|
PLOG_DEBUG << "Stopping DTLS recv thread";
|
||||||
mIncomingQueue.stop();
|
mIncomingQueue.stop();
|
||||||
gnutls_bye(mSession, GNUTLS_SHUT_RDWR);
|
|
||||||
mRecvThread.join();
|
mRecvThread.join();
|
||||||
onRecv(nullptr);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +215,8 @@ void DtlsTransport::runRecvLoop() {
|
|||||||
PLOG_ERROR << "DTLS recv: " << e.what();
|
PLOG_ERROR << "DTLS recv: " << e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gnutls_bye(mSession, GNUTLS_SHUT_RDWR);
|
||||||
|
|
||||||
PLOG_INFO << "DTLS disconnected";
|
PLOG_INFO << "DTLS disconnected";
|
||||||
changeState(State::Disconnected);
|
changeState(State::Disconnected);
|
||||||
recv(nullptr);
|
recv(nullptr);
|
||||||
@ -410,6 +410,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
|||||||
SSL_set_tmp_ecdh(mSsl, ecdh.get());
|
SSL_set_tmp_ecdh(mSsl, ecdh.get());
|
||||||
|
|
||||||
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
|
mRecvThread = std::thread(&DtlsTransport::runRecvLoop, this);
|
||||||
|
registerIncoming();
|
||||||
}
|
}
|
||||||
|
|
||||||
DtlsTransport::~DtlsTransport() {
|
DtlsTransport::~DtlsTransport() {
|
||||||
@ -427,7 +428,6 @@ bool DtlsTransport::stop() {
|
|||||||
mIncomingQueue.stop();
|
mIncomingQueue.stop();
|
||||||
mRecvThread.join();
|
mRecvThread.join();
|
||||||
SSL_shutdown(mSsl);
|
SSL_shutdown(mSsl);
|
||||||
onRecv(nullptr);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ public:
|
|||||||
using verifier_callback = std::function<bool(const std::string &fingerprint)>;
|
using verifier_callback = std::function<bool(const std::string &fingerprint)>;
|
||||||
using state_callback = std::function<void(State state)>;
|
using state_callback = std::function<void(State state)>;
|
||||||
|
|
||||||
DtlsTransport(std::shared_ptr<IceTransport> lower, std::shared_ptr<Certificate> certificate,
|
DtlsTransport(std::shared_ptr<IceTransport> lower, certificate_ptr certificate,
|
||||||
verifier_callback verifierCallback, state_callback stateChangeCallback);
|
verifier_callback verifierCallback, state_callback stateChangeCallback);
|
||||||
~DtlsTransport();
|
~DtlsTransport();
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ private:
|
|||||||
void changeState(State state);
|
void changeState(State state);
|
||||||
void runRecvLoop();
|
void runRecvLoop();
|
||||||
|
|
||||||
const std::shared_ptr<Certificate> mCertificate;
|
const certificate_ptr mCertificate;
|
||||||
|
|
||||||
Queue<message_ptr> mIncomingQueue;
|
Queue<message_ptr> mIncomingQueue;
|
||||||
std::atomic<State> mState;
|
std::atomic<State> mState;
|
||||||
|
@ -103,7 +103,6 @@ IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
|||||||
IceTransport::~IceTransport() { stop(); }
|
IceTransport::~IceTransport() { stop(); }
|
||||||
|
|
||||||
bool IceTransport::stop() {
|
bool IceTransport::stop() {
|
||||||
onRecv(nullptr);
|
|
||||||
return Transport::stop();
|
return Transport::stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,15 +168,6 @@ bool IceTransport::send(message_ptr message) {
|
|||||||
return outgoing(message);
|
return outgoing(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::incoming(message_ptr message) {
|
|
||||||
PLOG_VERBOSE << "Incoming size=" << message->size();
|
|
||||||
recv(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IceTransport::incoming(const byte *data, int size) {
|
|
||||||
incoming(make_message(data, data + size));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IceTransport::outgoing(message_ptr message) {
|
bool IceTransport::outgoing(message_ptr message) {
|
||||||
return juice_send(mAgent.get(), reinterpret_cast<const char *>(message->data()),
|
return juice_send(mAgent.get(), reinterpret_cast<const char *>(message->data()),
|
||||||
message->size()) >= 0;
|
message->size()) >= 0;
|
||||||
@ -234,7 +224,9 @@ void IceTransport::RecvCallback(juice_agent_t *agent, const char *data, size_t s
|
|||||||
void *user_ptr) {
|
void *user_ptr) {
|
||||||
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
auto iceTransport = static_cast<rtc::IceTransport *>(user_ptr);
|
||||||
try {
|
try {
|
||||||
iceTransport->incoming(reinterpret_cast<const byte *>(data), size);
|
PLOG_VERBOSE << "Incoming size=" << size;
|
||||||
|
auto b = reinterpret_cast<const byte *>(data);
|
||||||
|
iceTransport->incoming(make_message(b, b + size));
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
PLOG_WARNING << e.what();
|
PLOG_WARNING << e.what();
|
||||||
}
|
}
|
||||||
@ -455,6 +447,9 @@ bool IceTransport::stop() {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
PLOG_DEBUG << "Stopping ICE thread";
|
PLOG_DEBUG << "Stopping ICE thread";
|
||||||
|
nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(mMainLoop.get()),
|
||||||
|
NULL, NULL);
|
||||||
|
nice_agent_remove_stream(mNiceAgent.get(), mStreamId);
|
||||||
g_main_loop_quit(mMainLoop.get());
|
g_main_loop_quit(mMainLoop.get());
|
||||||
mMainLoopThread.join();
|
mMainLoopThread.join();
|
||||||
return true;
|
return true;
|
||||||
@ -541,15 +536,6 @@ bool IceTransport::send(message_ptr message) {
|
|||||||
return outgoing(message);
|
return outgoing(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::incoming(message_ptr message) {
|
|
||||||
PLOG_VERBOSE << "Incoming size=" << message->size();
|
|
||||||
recv(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IceTransport::incoming(const byte *data, int size) {
|
|
||||||
incoming(make_message(data, data + size));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IceTransport::outgoing(message_ptr message) {
|
bool IceTransport::outgoing(message_ptr message) {
|
||||||
return nice_agent_send(mNiceAgent.get(), mStreamId, 1, message->size(),
|
return nice_agent_send(mNiceAgent.get(), mStreamId, 1, message->size(),
|
||||||
reinterpret_cast<const char *>(message->data())) >= 0;
|
reinterpret_cast<const char *>(message->data())) >= 0;
|
||||||
@ -637,7 +623,9 @@ void IceTransport::RecvCallback(NiceAgent *agent, guint streamId, guint componen
|
|||||||
gchar *buf, gpointer userData) {
|
gchar *buf, gpointer userData) {
|
||||||
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
|
auto iceTransport = static_cast<rtc::IceTransport *>(userData);
|
||||||
try {
|
try {
|
||||||
iceTransport->incoming(reinterpret_cast<byte *>(buf), len);
|
PLOG_VERBOSE << "Incoming size=" << len;
|
||||||
|
auto b = reinterpret_cast<byte *>(buf);
|
||||||
|
iceTransport->incoming(make_message(b, b + len));
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
PLOG_WARNING << e.what();
|
PLOG_WARNING << e.what();
|
||||||
}
|
}
|
||||||
@ -700,14 +688,14 @@ bool IceTransport::getSelectedCandidatePair(CandidateInfo *localInfo, CandidateI
|
|||||||
|
|
||||||
const CandidateType IceTransport::NiceTypeToCandidateType(NiceCandidateType type) {
|
const CandidateType IceTransport::NiceTypeToCandidateType(NiceCandidateType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_HOST:
|
|
||||||
return CandidateType::Host;
|
|
||||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
|
case NiceCandidateType::NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
|
||||||
return CandidateType::PeerReflexive;
|
return CandidateType::PeerReflexive;
|
||||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_RELAYED:
|
case NiceCandidateType::NICE_CANDIDATE_TYPE_RELAYED:
|
||||||
return CandidateType::Relayed;
|
return CandidateType::Relayed;
|
||||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
|
case NiceCandidateType::NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
|
||||||
return CandidateType::ServerReflexive;
|
return CandidateType::ServerReflexive;
|
||||||
|
default:
|
||||||
|
return CandidateType::Host;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,7 +708,7 @@ IceTransport::NiceTransportTypeToCandidateTransportType(NiceCandidateTransport t
|
|||||||
return CandidateTransportType::TcpPassive;
|
return CandidateTransportType::TcpPassive;
|
||||||
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_TCP_SO:
|
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_TCP_SO:
|
||||||
return CandidateTransportType::TcpSo;
|
return CandidateTransportType::TcpSo;
|
||||||
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_UDP:
|
default:
|
||||||
return CandidateTransportType::Udp;
|
return CandidateTransportType::Udp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,8 +85,6 @@ public:
|
|||||||
bool send(message_ptr message) override; // false if dropped
|
bool send(message_ptr message) override; // false if dropped
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void incoming(message_ptr message) override;
|
|
||||||
void incoming(const byte *data, int size);
|
|
||||||
bool outgoing(message_ptr message) override;
|
bool outgoing(message_ptr message) override;
|
||||||
|
|
||||||
void changeState(State state);
|
void changeState(State state);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "init.hpp"
|
#include "init.hpp"
|
||||||
|
|
||||||
|
#include "certificate.hpp"
|
||||||
#include "dtlstransport.hpp"
|
#include "dtlstransport.hpp"
|
||||||
#include "sctptransport.hpp"
|
#include "sctptransport.hpp"
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ Init::Init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Init::~Init() {
|
Init::~Init() {
|
||||||
|
CleanupCertificateCache();
|
||||||
DtlsTransport::Cleanup();
|
DtlsTransport::Cleanup();
|
||||||
SctpTransport::Cleanup();
|
SctpTransport::Cleanup();
|
||||||
|
|
||||||
|
@ -53,7 +53,8 @@ auto weak_bind_verifier(F &&f, T *t, Args &&... _args) {
|
|||||||
PeerConnection::PeerConnection() : PeerConnection(Configuration()) {}
|
PeerConnection::PeerConnection() : PeerConnection(Configuration()) {}
|
||||||
|
|
||||||
PeerConnection::PeerConnection(const Configuration &config)
|
PeerConnection::PeerConnection(const Configuration &config)
|
||||||
: mConfig(config), mCertificate(make_certificate("libdatachannel")), mState(State::New) {}
|
: mConfig(config), mCertificate(make_certificate("libdatachannel")), mState(State::New),
|
||||||
|
mGatheringState(GatheringState::New) {}
|
||||||
|
|
||||||
PeerConnection::~PeerConnection() { close(); }
|
PeerConnection::~PeerConnection() { close(); }
|
||||||
|
|
||||||
@ -268,9 +269,10 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
|
|||||||
if (auto transport = std::atomic_load(&mDtlsTransport))
|
if (auto transport = std::atomic_load(&mDtlsTransport))
|
||||||
return transport;
|
return transport;
|
||||||
|
|
||||||
|
auto certificate = mCertificate.get();
|
||||||
auto lower = std::atomic_load(&mIceTransport);
|
auto lower = std::atomic_load(&mIceTransport);
|
||||||
auto transport = std::make_shared<DtlsTransport>(
|
auto transport = std::make_shared<DtlsTransport>(
|
||||||
lower, mCertificate, weak_bind_verifier(&PeerConnection::checkFingerprint, this, _1),
|
lower, certificate, weak_bind_verifier(&PeerConnection::checkFingerprint, this, _1),
|
||||||
[this, weak_this = weak_from_this()](DtlsTransport::State state) {
|
[this, weak_this = weak_from_this()](DtlsTransport::State state) {
|
||||||
auto shared_this = weak_this.lock();
|
auto shared_this = weak_this.lock();
|
||||||
if (!shared_this)
|
if (!shared_this)
|
||||||
@ -421,8 +423,8 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
|||||||
weak_ptr<DataChannel>{channel}));
|
weak_ptr<DataChannel>{channel}));
|
||||||
mDataChannels.insert(std::make_pair(message->stream, channel));
|
mDataChannels.insert(std::make_pair(message->stream, channel));
|
||||||
} else {
|
} else {
|
||||||
// Invalid, close the DataChannel by resetting the stream
|
// Invalid, close the DataChannel
|
||||||
sctpTransport->reset(message->stream);
|
sctpTransport->close(message->stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -512,9 +514,11 @@ void PeerConnection::processLocalDescription(Description description) {
|
|||||||
if (auto remote = remoteDescription())
|
if (auto remote = remoteDescription())
|
||||||
remoteSctpPort = remote->sctpPort();
|
remoteSctpPort = remote->sctpPort();
|
||||||
|
|
||||||
|
auto certificate = mCertificate.get(); // wait for certificate if not ready
|
||||||
|
|
||||||
std::lock_guard lock(mLocalDescriptionMutex);
|
std::lock_guard lock(mLocalDescriptionMutex);
|
||||||
mLocalDescription.emplace(std::move(description));
|
mLocalDescription.emplace(std::move(description));
|
||||||
mLocalDescription->setFingerprint(mCertificate->fingerprint());
|
mLocalDescription->setFingerprint(certificate->fingerprint());
|
||||||
mLocalDescription->setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT));
|
mLocalDescription->setSctpPort(remoteSctpPort.value_or(DEFAULT_SCTP_PORT));
|
||||||
mLocalDescription->setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
|
mLocalDescription->setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
|
||||||
|
|
||||||
|
@ -450,3 +450,5 @@ int rtcReceiveMessage(int dc, char *buffer, int *size) {
|
|||||||
*message);
|
*message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rtcCleanup() { rtc::Cleanup(); }
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef USE_JUICE
|
#ifdef USE_JUICE
|
||||||
@ -62,7 +63,10 @@ void SctpTransport::Init() {
|
|||||||
usrsctp_sysctl_set_sctp_heartbeat_interval_default(10 * 1000); // ms
|
usrsctp_sysctl_set_sctp_heartbeat_interval_default(10 * 1000); // ms
|
||||||
}
|
}
|
||||||
|
|
||||||
void SctpTransport::Cleanup() { usrsctp_finish(); }
|
void SctpTransport::Cleanup() {
|
||||||
|
while (usrsctp_finish() != 0)
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
}
|
||||||
|
|
||||||
SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
||||||
message_callback recvCallback, amount_callback bufferedAmountCallback,
|
message_callback recvCallback, amount_callback bufferedAmountCallback,
|
||||||
@ -163,13 +167,16 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
|||||||
throw std::runtime_error("Could not set SCTP send buffer size, errno=" +
|
throw std::runtime_error("Could not set SCTP send buffer size, errno=" +
|
||||||
std::to_string(errno));
|
std::to_string(errno));
|
||||||
|
|
||||||
|
registerIncoming();
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
SctpTransport::~SctpTransport() {
|
SctpTransport::~SctpTransport() {
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
usrsctp_close(mSock);
|
if (mSock)
|
||||||
|
usrsctp_close(mSock);
|
||||||
|
|
||||||
usrsctp_deregister_address(this);
|
usrsctp_deregister_address(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +194,9 @@ bool SctpTransport::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SctpTransport::connect() {
|
void SctpTransport::connect() {
|
||||||
|
if (!mSock)
|
||||||
|
return;
|
||||||
|
|
||||||
PLOG_DEBUG << "SCTP connect";
|
PLOG_DEBUG << "SCTP connect";
|
||||||
changeState(State::Connecting);
|
changeState(State::Connecting);
|
||||||
|
|
||||||
@ -210,12 +220,19 @@ void SctpTransport::connect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SctpTransport::shutdown() {
|
void SctpTransport::shutdown() {
|
||||||
|
if (!mSock)
|
||||||
|
return;
|
||||||
|
|
||||||
PLOG_DEBUG << "SCTP shutdown";
|
PLOG_DEBUG << "SCTP shutdown";
|
||||||
|
|
||||||
if (usrsctp_shutdown(mSock, SHUT_RDWR)) {
|
if (usrsctp_shutdown(mSock, SHUT_RDWR) != 0 && errno != ENOTCONN) {
|
||||||
PLOG_WARNING << "SCTP shutdown failed, errno=" << errno;
|
PLOG_WARNING << "SCTP shutdown failed, errno=" << errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// close() abort the connection when linger is disabled, call it now
|
||||||
|
usrsctp_close(mSock);
|
||||||
|
mSock = nullptr;
|
||||||
|
|
||||||
PLOG_INFO << "SCTP disconnected";
|
PLOG_INFO << "SCTP disconnected";
|
||||||
changeState(State::Disconnected);
|
changeState(State::Disconnected);
|
||||||
mWrittenCondition.notify_all();
|
mWrittenCondition.notify_all();
|
||||||
@ -237,31 +254,15 @@ bool SctpTransport::send(message_ptr message) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SctpTransport::close(unsigned int stream) {
|
||||||
|
send(make_message(0, Message::Reset, uint16_t(stream)));
|
||||||
|
}
|
||||||
|
|
||||||
void SctpTransport::flush() {
|
void SctpTransport::flush() {
|
||||||
std::lock_guard lock(mSendMutex);
|
std::lock_guard lock(mSendMutex);
|
||||||
trySendQueue();
|
trySendQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SctpTransport::reset(unsigned int stream) {
|
|
||||||
PLOG_DEBUG << "SCTP resetting stream " << stream;
|
|
||||||
|
|
||||||
std::unique_lock lock(mWriteMutex);
|
|
||||||
mWritten = false;
|
|
||||||
using srs_t = struct sctp_reset_streams;
|
|
||||||
const size_t len = sizeof(srs_t) + sizeof(uint16_t);
|
|
||||||
byte buffer[len] = {};
|
|
||||||
srs_t &srs = *reinterpret_cast<srs_t *>(buffer);
|
|
||||||
srs.srs_flags = SCTP_STREAM_RESET_OUTGOING;
|
|
||||||
srs.srs_number_streams = 1;
|
|
||||||
srs.srs_stream_list[0] = uint16_t(stream);
|
|
||||||
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_RESET_STREAMS, &srs, len) == 0) {
|
|
||||||
mWrittenCondition.wait_for(lock, 1000ms,
|
|
||||||
[&]() { return mWritten || mState != State::Connected; });
|
|
||||||
} else {
|
|
||||||
PLOG_WARNING << "SCTP reset stream " << stream << " failed, errno=" << errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SctpTransport::incoming(message_ptr message) {
|
void SctpTransport::incoming(message_ptr message) {
|
||||||
// There could be a race condition here where we receive the remote INIT before the local one is
|
// There could be a race condition here where we receive the remote INIT before the local one is
|
||||||
// sent, which would result in the connection being aborted. Therefore, we need to wait for data
|
// sent, which would result in the connection being aborted. Therefore, we need to wait for data
|
||||||
@ -301,16 +302,9 @@ bool SctpTransport::trySendQueue() {
|
|||||||
|
|
||||||
bool SctpTransport::trySendMessage(message_ptr message) {
|
bool SctpTransport::trySendMessage(message_ptr message) {
|
||||||
// Requires mSendMutex to be locked
|
// Requires mSendMutex to be locked
|
||||||
if (mState != State::Connected)
|
if (!mSock || mState != State::Connected)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PLOG_VERBOSE << "SCTP try send size=" << message->size();
|
|
||||||
|
|
||||||
// TODO: Implement SCTP ndata specification draft when supported everywhere
|
|
||||||
// See https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-08
|
|
||||||
|
|
||||||
const Reliability reliability = message->reliability ? *message->reliability : Reliability();
|
|
||||||
|
|
||||||
uint32_t ppid;
|
uint32_t ppid;
|
||||||
switch (message->type) {
|
switch (message->type) {
|
||||||
case Message::String:
|
case Message::String:
|
||||||
@ -319,11 +313,24 @@ bool SctpTransport::trySendMessage(message_ptr message) {
|
|||||||
case Message::Binary:
|
case Message::Binary:
|
||||||
ppid = !message->empty() ? PPID_BINARY : PPID_BINARY_EMPTY;
|
ppid = !message->empty() ? PPID_BINARY : PPID_BINARY_EMPTY;
|
||||||
break;
|
break;
|
||||||
default:
|
case Message::Control:
|
||||||
ppid = PPID_CONTROL;
|
ppid = PPID_CONTROL;
|
||||||
break;
|
break;
|
||||||
|
case Message::Reset:
|
||||||
|
sendReset(message->stream);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
// Ignore
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLOG_VERBOSE << "SCTP try send size=" << message->size();
|
||||||
|
|
||||||
|
// TODO: Implement SCTP ndata specification draft when supported everywhere
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-08
|
||||||
|
|
||||||
|
const Reliability reliability = message->reliability ? *message->reliability : Reliability();
|
||||||
|
|
||||||
struct sctp_sendv_spa spa = {};
|
struct sctp_sendv_spa spa = {};
|
||||||
|
|
||||||
// set sndinfo
|
// set sndinfo
|
||||||
@ -388,6 +395,33 @@ void SctpTransport::updateBufferedAmount(uint16_t streamId, long delta) {
|
|||||||
mBufferedAmountCallback(streamId, amount);
|
mBufferedAmountCallback(streamId, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SctpTransport::sendReset(uint16_t streamId) {
|
||||||
|
// Requires mSendMutex to be locked
|
||||||
|
if (!mSock || state() != State::Connected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PLOG_DEBUG << "SCTP resetting stream " << streamId;
|
||||||
|
|
||||||
|
using srs_t = struct sctp_reset_streams;
|
||||||
|
const size_t len = sizeof(srs_t) + sizeof(uint16_t);
|
||||||
|
byte buffer[len] = {};
|
||||||
|
srs_t &srs = *reinterpret_cast<srs_t *>(buffer);
|
||||||
|
srs.srs_flags = SCTP_STREAM_RESET_OUTGOING;
|
||||||
|
srs.srs_number_streams = 1;
|
||||||
|
srs.srs_stream_list[0] = streamId;
|
||||||
|
|
||||||
|
mWritten = false;
|
||||||
|
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_RESET_STREAMS, &srs, len) == 0) {
|
||||||
|
std::unique_lock lock(mWriteMutex); // locking before setsockopt might deadlock usrsctp...
|
||||||
|
mWrittenCondition.wait_for(lock, 1000ms,
|
||||||
|
[&]() { return mWritten || mState != State::Connected; });
|
||||||
|
} else if (errno == EINVAL) {
|
||||||
|
PLOG_VERBOSE << "SCTP stream " << streamId << " already reset";
|
||||||
|
} else {
|
||||||
|
PLOG_WARNING << "SCTP reset stream " << streamId << " failed, errno=" << errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SctpTransport::safeFlush() {
|
bool SctpTransport::safeFlush() {
|
||||||
try {
|
try {
|
||||||
flush();
|
flush();
|
||||||
@ -564,7 +598,7 @@ void SctpTransport::processNotification(const union sctp_notification *notify, s
|
|||||||
if (flags & SCTP_STREAM_RESET_OUTGOING_SSN) {
|
if (flags & SCTP_STREAM_RESET_OUTGOING_SSN) {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
uint16_t streamId = reset_event.strreset_stream_list[i];
|
uint16_t streamId = reset_event.strreset_stream_list[i];
|
||||||
reset(streamId);
|
close(streamId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flags & SCTP_STREAM_RESET_INCOMING_SSN) {
|
if (flags & SCTP_STREAM_RESET_INCOMING_SSN) {
|
||||||
@ -593,15 +627,17 @@ size_t SctpTransport::bytesSent() { return mBytesSent; }
|
|||||||
|
|
||||||
size_t SctpTransport::bytesReceived() { return mBytesReceived; }
|
size_t SctpTransport::bytesReceived() { return mBytesReceived; }
|
||||||
|
|
||||||
std::optional<std::chrono::milliseconds> SctpTransport::rtt() {
|
std::optional<milliseconds> SctpTransport::rtt() {
|
||||||
|
if (!mSock || state() != State::Connected)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
struct sctp_status status = {};
|
struct sctp_status status = {};
|
||||||
socklen_t len = sizeof(status);
|
socklen_t len = sizeof(status);
|
||||||
|
if (usrsctp_getsockopt(mSock, IPPROTO_SCTP, SCTP_STATUS, &status, &len)) {
|
||||||
if (usrsctp_getsockopt(this->mSock, IPPROTO_SCTP, SCTP_STATUS, &status, &len)) {
|
|
||||||
PLOG_WARNING << "Could not read SCTP_STATUS";
|
PLOG_WARNING << "Could not read SCTP_STATUS";
|
||||||
return std::nullopt;
|
return nullopt;
|
||||||
}
|
}
|
||||||
return std::chrono::milliseconds(status.sstat_primary.spinfo_srtt);
|
return milliseconds(status.sstat_primary.spinfo_srtt);
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data,
|
int SctpTransport::RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data,
|
||||||
|
@ -51,8 +51,8 @@ public:
|
|||||||
|
|
||||||
bool stop() override;
|
bool stop() override;
|
||||||
bool send(message_ptr message) override; // false if buffered
|
bool send(message_ptr message) override; // false if buffered
|
||||||
|
void close(unsigned int stream);
|
||||||
void flush();
|
void flush();
|
||||||
void reset(unsigned int stream);
|
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
void clearStats();
|
void clearStats();
|
||||||
@ -81,6 +81,7 @@ private:
|
|||||||
bool trySendQueue();
|
bool trySendQueue();
|
||||||
bool trySendMessage(message_ptr message);
|
bool trySendMessage(message_ptr message);
|
||||||
void updateBufferedAmount(uint16_t streamId, long delta);
|
void updateBufferedAmount(uint16_t streamId, long delta);
|
||||||
|
void sendReset(uint16_t streamId);
|
||||||
bool safeFlush();
|
bool safeFlush();
|
||||||
|
|
||||||
int handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data, size_t len,
|
int handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data, size_t len,
|
||||||
@ -99,9 +100,9 @@ private:
|
|||||||
std::map<uint16_t, size_t> mBufferedAmount;
|
std::map<uint16_t, size_t> mBufferedAmount;
|
||||||
amount_callback mBufferedAmountCallback;
|
amount_callback mBufferedAmountCallback;
|
||||||
|
|
||||||
std::recursive_mutex mWriteMutex;
|
std::mutex mWriteMutex;
|
||||||
std::condition_variable_any mWrittenCondition;
|
std::condition_variable mWrittenCondition;
|
||||||
bool mWritten = false;
|
std::atomic<bool> mWritten = false; // written outside lock
|
||||||
bool mWrittenOnce = false;
|
bool mWrittenOnce = false;
|
||||||
|
|
||||||
state_callback mStateChangeCallback;
|
state_callback mStateChangeCallback;
|
||||||
|
@ -32,26 +32,30 @@ using namespace std::placeholders;
|
|||||||
|
|
||||||
class Transport {
|
class Transport {
|
||||||
public:
|
public:
|
||||||
Transport(std::shared_ptr<Transport> lower = nullptr) : mLower(std::move(lower)) {
|
Transport(std::shared_ptr<Transport> lower = nullptr) : mLower(std::move(lower)) {}
|
||||||
|
virtual ~Transport() {
|
||||||
|
stop();
|
||||||
if (mLower)
|
if (mLower)
|
||||||
mLower->onRecv(std::bind(&Transport::incoming, this, _1));
|
mLower->onRecv(nullptr); // doing it on stop could cause a deadlock
|
||||||
}
|
}
|
||||||
virtual ~Transport() { stop(); }
|
|
||||||
|
|
||||||
virtual bool stop() {
|
virtual bool stop() {
|
||||||
if (mLower)
|
|
||||||
mLower->onRecv(nullptr);
|
|
||||||
return !mShutdown.exchange(true);
|
return !mShutdown.exchange(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool send(message_ptr message) = 0;
|
void registerIncoming() {
|
||||||
|
if (mLower)
|
||||||
|
mLower->onRecv(std::bind(&Transport::incoming, this, _1));
|
||||||
|
}
|
||||||
|
|
||||||
void onRecv(message_callback callback) { mRecvCallback = std::move(callback); }
|
void onRecv(message_callback callback) { mRecvCallback = std::move(callback); }
|
||||||
|
|
||||||
|
virtual bool send(message_ptr message) { return outgoing(message); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void recv(message_ptr message) { mRecvCallback(message); }
|
void recv(message_ptr message) { mRecvCallback(message); }
|
||||||
|
|
||||||
virtual void incoming(message_ptr message) = 0;
|
virtual void incoming(message_ptr message) { recv(message); }
|
||||||
virtual bool outgoing(message_ptr message) {
|
virtual bool outgoing(message_ptr message) {
|
||||||
if (mLower)
|
if (mLower)
|
||||||
return mLower->send(message);
|
return mLower->send(message);
|
||||||
|
@ -23,9 +23,12 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
|
||||||
|
#else
|
||||||
#include <unistd.h> // for sleep
|
#include <unistd.h> // for sleep
|
||||||
|
#endif
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
rtcState state;
|
rtcState state;
|
||||||
@ -196,6 +199,10 @@ int test_capi_main() {
|
|||||||
deletePeer(peer2);
|
deletePeer(peer2);
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
|
// You may call rtcCleanup() when finished to free static resources
|
||||||
|
rtcCleanup();
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
printf("Success\n");
|
printf("Success\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -140,5 +140,9 @@ void test_connectivity() {
|
|||||||
pc2->close();
|
pc2->close();
|
||||||
this_thread::sleep_for(1s);
|
this_thread::sleep_for(1s);
|
||||||
|
|
||||||
|
// You may call rtc::Cleanup() when finished to free static resources
|
||||||
|
rtc::Cleanup();
|
||||||
|
this_thread::sleep_for(1s);
|
||||||
|
|
||||||
cout << "Success" << endl;
|
cout << "Success" << endl;
|
||||||
}
|
}
|
||||||
|
12
test/p2p_c_version/CMakeLists.txt
Normal file
12
test/p2p_c_version/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5.1)
|
||||||
|
project(offerer C)
|
||||||
|
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_C_FLAGS "-Wall -g -O2")
|
||||||
|
|
||||||
|
add_executable(offerer offerer.c)
|
||||||
|
target_link_libraries(offerer datachannel)
|
||||||
|
|
||||||
|
add_executable(answerer answerer.c)
|
||||||
|
target_link_libraries(answerer datachannel)
|
326
test/p2p_c_version/answerer.c
Normal file
326
test/p2p_c_version/answerer.c
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||||
|
* Copyright (c) 2020 Stevedan Ogochukwu Omodolor
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rtc/rtc.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h> // for sleep
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
rtcState state;
|
||||||
|
rtcGatheringState gatheringState;
|
||||||
|
int pc;
|
||||||
|
int dc;
|
||||||
|
bool connected;
|
||||||
|
} Peer;
|
||||||
|
|
||||||
|
Peer *peer = NULL;
|
||||||
|
|
||||||
|
static void dataChannelCallback(int dc, 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 stateChangeCallback(rtcState state, void *ptr);
|
||||||
|
|
||||||
|
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
|
||||||
|
|
||||||
|
static void closedCallback(void *ptr);
|
||||||
|
|
||||||
|
static void messageCallback(const char *message, int size, void *ptr);
|
||||||
|
|
||||||
|
static void deletePeer(Peer *peer);
|
||||||
|
|
||||||
|
int all_space(const char *str);
|
||||||
|
char* state_print(rtcState state);
|
||||||
|
char* rtcGatheringState_print(rtcState state);
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
rtcInitLogger(RTC_LOG_DEBUG);
|
||||||
|
|
||||||
|
// Create peer
|
||||||
|
rtcConfiguration config;
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
|
||||||
|
Peer *peer = (Peer *)malloc(sizeof(Peer));
|
||||||
|
if (!peer) {
|
||||||
|
|
||||||
|
printf("Error allocating memory for peer\n");
|
||||||
|
deletePeer(peer);
|
||||||
|
|
||||||
|
}
|
||||||
|
memset(peer, 0, sizeof(Peer));
|
||||||
|
|
||||||
|
printf("Peer created\n");
|
||||||
|
|
||||||
|
// Create peer connection
|
||||||
|
peer->pc = rtcCreatePeerConnection(&config);
|
||||||
|
|
||||||
|
rtcSetUserPointer(peer->pc, peer);
|
||||||
|
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
|
||||||
|
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
|
||||||
|
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
|
||||||
|
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
|
||||||
|
|
||||||
|
rtcSetUserPointer(peer->dc, NULL);
|
||||||
|
rtcSetDataChannelCallback(peer->pc, dataChannelCallback);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool exit = false;
|
||||||
|
|
||||||
|
while (!exit) {
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("***************************************************************************************\n");
|
||||||
|
|
||||||
|
// << endl
|
||||||
|
printf("* 0: Exit /"
|
||||||
|
" 1: Enter remote description /"
|
||||||
|
" 2: Enter remote candidate /"
|
||||||
|
" 3: Send message /"
|
||||||
|
" 4: Print Connection Info *\n"
|
||||||
|
"[Command]: ");
|
||||||
|
|
||||||
|
int command = -1;
|
||||||
|
int c;
|
||||||
|
// int check_scan
|
||||||
|
if (scanf("%d", &command)) {
|
||||||
|
|
||||||
|
}else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while ((c = getchar()) != '\n' && c != EOF) { }
|
||||||
|
|
||||||
|
fflush(stdin);
|
||||||
|
switch (command) {
|
||||||
|
case 0: {
|
||||||
|
exit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
// Parse Description
|
||||||
|
printf("[Description]: ");
|
||||||
|
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t read = 0;
|
||||||
|
char *sdp = (char*) malloc(sizeof(char));
|
||||||
|
while ((read = getline(&line, &len, stdin)) != -1 && !all_space(line)) {
|
||||||
|
sdp = (char*) realloc (sdp,(strlen(sdp)+1) +strlen(line)+1);
|
||||||
|
strcat(sdp, line);
|
||||||
|
|
||||||
|
}
|
||||||
|
printf("%s\n",sdp);
|
||||||
|
rtcSetRemoteDescription(peer->pc, sdp, "offer");
|
||||||
|
free(sdp);
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// Parse Candidate
|
||||||
|
printf("[Candidate]: ");
|
||||||
|
char* candidate = NULL;
|
||||||
|
size_t candidate_size = 0;
|
||||||
|
|
||||||
|
if(getline(&candidate, &candidate_size, stdin)) {
|
||||||
|
rtcAddRemoteCandidate(peer->pc, candidate, "0");
|
||||||
|
free(candidate);
|
||||||
|
|
||||||
|
}else {
|
||||||
|
printf("Error reading line\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
// Send Message
|
||||||
|
if(!peer->connected) {
|
||||||
|
printf("** Channel is not Open **");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("[Message]: ");
|
||||||
|
char* message = NULL;
|
||||||
|
size_t message_size = 0;
|
||||||
|
|
||||||
|
if(getline(&message, &message_size, stdin)) {
|
||||||
|
rtcSendMessage(peer->dc, message, -1);
|
||||||
|
free(message);
|
||||||
|
}else {
|
||||||
|
printf("Error reading line\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
// Connection Info
|
||||||
|
if(!peer->connected) {
|
||||||
|
printf("** Channel is not Open **");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char buffer[256];
|
||||||
|
if (rtcGetLocalAddress(peer->pc, buffer, 256) >= 0)
|
||||||
|
printf("Local address 1: %s\n", buffer);
|
||||||
|
if (rtcGetRemoteAddress(peer->pc, buffer, 256) >= 0)
|
||||||
|
printf("Remote address 1: %s\n", buffer);
|
||||||
|
|
||||||
|
else
|
||||||
|
printf("Could not get Candidate Pair Info\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
printf("** Invalid Command **");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deletePeer(peer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void descriptionCallback(const char *sdp, const char *type, void *ptr) {
|
||||||
|
// Peer *peer = (Peer *)ptr;
|
||||||
|
printf("Description %s:\n%s\n", "answerer", sdp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void candidateCallback(const char *cand, const char *mid, void *ptr) {
|
||||||
|
// Peer *peer = (Peer *)ptr;
|
||||||
|
printf("Candidate %s: %s\n", "answerer", cand);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stateChangeCallback(rtcState state, void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->state = state;
|
||||||
|
printf("State %s: %s\n", "answerer", state_print(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gatheringStateCallback(rtcGatheringState state, void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->gatheringState = state;
|
||||||
|
printf("Gathering state %s: %s\n", "answerer", rtcGatheringState_print(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void closedCallback(void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->connected = false;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
static void messageCallback(const char *message, int size, void *ptr) {
|
||||||
|
// Peer *peer = (Peer *)ptr;
|
||||||
|
if (size < 0) { // negative size indicates a null-terminated string
|
||||||
|
printf("Message %s: %s\n", "answerer", message);
|
||||||
|
} else {
|
||||||
|
printf("Message %s: [binary of size %d]\n", "answerer", size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void deletePeer(Peer *peer) {
|
||||||
|
if (peer) {
|
||||||
|
if (peer->dc)
|
||||||
|
rtcDeleteDataChannel(peer->dc);
|
||||||
|
if (peer->pc)
|
||||||
|
rtcDeletePeerConnection(peer->pc);
|
||||||
|
free(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dataChannelCallback(int dc, void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->dc = dc;
|
||||||
|
peer->connected = true;
|
||||||
|
rtcSetClosedCallback(dc, closedCallback);
|
||||||
|
rtcSetMessageCallback(dc, messageCallback);
|
||||||
|
char buffer[256];
|
||||||
|
if (rtcGetDataChannelLabel(dc, buffer, 256) >= 0)
|
||||||
|
printf("DataChannel %s: Received with label \"%s\"\n", "answerer", buffer);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
int all_space(const char *str) {
|
||||||
|
while (*str) {
|
||||||
|
if (!isspace(*str++)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* state_print(rtcState state) {
|
||||||
|
char *str = NULL;
|
||||||
|
switch (state) {
|
||||||
|
case RTC_NEW:
|
||||||
|
str = "RTC_NEW";
|
||||||
|
break;
|
||||||
|
case RTC_CONNECTING:
|
||||||
|
str = "RTC_CONNECTING";
|
||||||
|
break;
|
||||||
|
case RTC_CONNECTED:
|
||||||
|
str = "RTC_CONNECTED";
|
||||||
|
break;
|
||||||
|
case RTC_DISCONNECTED:
|
||||||
|
str = "RTC_DISCONNECTED";
|
||||||
|
break;
|
||||||
|
case RTC_FAILED:
|
||||||
|
str = "RTC_FAILED";
|
||||||
|
break;
|
||||||
|
case RTC_CLOSED:
|
||||||
|
str = "RTC_CLOSED";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
char* rtcGatheringState_print(rtcState state) {
|
||||||
|
char* str = NULL;
|
||||||
|
switch (state) {
|
||||||
|
case RTC_GATHERING_NEW:
|
||||||
|
str = "RTC_GATHERING_NEW";
|
||||||
|
break;
|
||||||
|
case RTC_GATHERING_INPROGRESS:
|
||||||
|
str = "RTC_GATHERING_INPROGRESS";
|
||||||
|
break;
|
||||||
|
case RTC_GATHERING_COMPLETE:
|
||||||
|
str = "RTC_GATHERING_COMPLETE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
334
test/p2p_c_version/offerer.c
Normal file
334
test/p2p_c_version/offerer.c
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Paul-Louis Ageneau
|
||||||
|
* Copyright (c) 2020 Stevedan Ogochukwu Omodolor
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rtc/rtc.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h> // for sleep
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
char* state_print(rtcState state);
|
||||||
|
char* rtcGatheringState_print(rtcState state);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
rtcState state;
|
||||||
|
rtcGatheringState gatheringState;
|
||||||
|
int pc;
|
||||||
|
int dc;
|
||||||
|
bool connected;
|
||||||
|
} Peer;
|
||||||
|
|
||||||
|
Peer *peer = NULL;
|
||||||
|
|
||||||
|
static void descriptionCallback(const char *sdp, const char *type, void *ptr);
|
||||||
|
|
||||||
|
static void candidateCallback(const char *cand, const char *mid, void *ptr);
|
||||||
|
|
||||||
|
static void stateChangeCallback(rtcState state, void *ptr);
|
||||||
|
|
||||||
|
static void gatheringStateCallback(rtcGatheringState state, void *ptr);
|
||||||
|
|
||||||
|
static void openCallback(void *ptr);
|
||||||
|
|
||||||
|
static void closedCallback(void *ptr);
|
||||||
|
|
||||||
|
static void messageCallback(const char *message, int size, void *ptr);
|
||||||
|
|
||||||
|
static void deletePeer(Peer *peer);
|
||||||
|
|
||||||
|
int all_space(const char *str);
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
rtcInitLogger(RTC_LOG_DEBUG);
|
||||||
|
|
||||||
|
// Create peer
|
||||||
|
rtcConfiguration config;
|
||||||
|
memset(&config, 0, sizeof(config));
|
||||||
|
|
||||||
|
Peer *peer = (Peer *)malloc(sizeof(Peer));
|
||||||
|
if (!peer) {
|
||||||
|
|
||||||
|
printf("Error allocating memory for peer\n");
|
||||||
|
deletePeer(peer);
|
||||||
|
|
||||||
|
}
|
||||||
|
memset(peer, 0, sizeof(Peer));
|
||||||
|
|
||||||
|
printf("Peer created\n");
|
||||||
|
|
||||||
|
// Create peer connection
|
||||||
|
peer->pc = rtcCreatePeerConnection(&config);
|
||||||
|
rtcSetUserPointer(peer->pc, peer);
|
||||||
|
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
|
||||||
|
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
|
||||||
|
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
|
||||||
|
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
|
||||||
|
|
||||||
|
// Since this is the offere, we will create a datachannel
|
||||||
|
peer->dc = rtcCreateDataChannel(peer->pc, "test");
|
||||||
|
|
||||||
|
rtcSetOpenCallback(peer->dc, openCallback);
|
||||||
|
|
||||||
|
|
||||||
|
rtcSetClosedCallback(peer->dc, closedCallback);
|
||||||
|
|
||||||
|
rtcSetMessageCallback(peer->dc, messageCallback);
|
||||||
|
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
bool exit = false;
|
||||||
|
|
||||||
|
while (!exit) {
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
printf("***************************************************************************************\n");
|
||||||
|
|
||||||
|
// << endl
|
||||||
|
printf("* 0: Exit /"
|
||||||
|
" 1: Enter remote description /"
|
||||||
|
" 2: Enter remote candidate /"
|
||||||
|
" 3: Send message /"
|
||||||
|
" 4: Print Connection Info *\n"
|
||||||
|
"[Command]: ");
|
||||||
|
|
||||||
|
int command = -1;
|
||||||
|
int c;
|
||||||
|
if (scanf("%d", &command)) {
|
||||||
|
|
||||||
|
}else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while ((c = getchar()) != '\n' && c != EOF) { }
|
||||||
|
fflush(stdin);
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 0: {
|
||||||
|
exit = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
// Parse Description
|
||||||
|
printf("[Description]: ");
|
||||||
|
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t read = 0;
|
||||||
|
char *sdp = (char*) malloc(sizeof(char));
|
||||||
|
while ((read = getline(&line, &len, stdin)) != -1 && !all_space(line)) {
|
||||||
|
sdp = (char*) realloc (sdp,(strlen(sdp)+1) +strlen(line)+1);
|
||||||
|
strcat(sdp, line);
|
||||||
|
|
||||||
|
}
|
||||||
|
printf("%s\n",sdp);
|
||||||
|
rtcSetRemoteDescription(peer->pc, sdp, "answer");
|
||||||
|
free(sdp);
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// Parse Candidate
|
||||||
|
printf("[Candidate]: ");
|
||||||
|
char* candidate = NULL;
|
||||||
|
size_t candidate_size = 0;
|
||||||
|
if(getline(&candidate, &candidate_size, stdin)) {
|
||||||
|
rtcAddRemoteCandidate(peer->pc, candidate, "0");
|
||||||
|
free(candidate);
|
||||||
|
|
||||||
|
}else {
|
||||||
|
printf("Error reading line\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
// Send Message
|
||||||
|
if(!peer->connected) {
|
||||||
|
printf("** Channel is not Open **");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printf("[Message]: ");
|
||||||
|
char* message = NULL;
|
||||||
|
size_t message_size = 0;
|
||||||
|
if(getline(&message, &message_size, stdin)) {
|
||||||
|
rtcSendMessage(peer->dc, message, -1);
|
||||||
|
free(message);
|
||||||
|
}else {
|
||||||
|
printf("Error reading line\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
// Connection Info
|
||||||
|
if(!peer->connected) {
|
||||||
|
printf("** Channel is not Open **");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char buffer[256];
|
||||||
|
if (rtcGetLocalAddress(peer->pc, buffer, 256) >= 0)
|
||||||
|
printf("Local address 1: %s\n", buffer);
|
||||||
|
if (rtcGetRemoteAddress(peer->pc, buffer, 256) >= 0)
|
||||||
|
printf("Remote address 1: %s\n", buffer);
|
||||||
|
|
||||||
|
else
|
||||||
|
printf("Could not get Candidate Pair Info\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
printf("** Invalid Command **");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deletePeer(peer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void descriptionCallback(const char *sdp, const char *type, void *ptr) {
|
||||||
|
// Peer *peer = (Peer *)ptr;
|
||||||
|
printf("Description %s:\n%s\n", "offerer", sdp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void candidateCallback(const char *cand, const char *mid, void *ptr) {
|
||||||
|
// Peer *peer = (Peer *)ptr;
|
||||||
|
printf("Candidate %s: %s\n", "offerer", cand);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stateChangeCallback(rtcState state, void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->state = state;
|
||||||
|
printf("State %s: %s\n", "offerer", state_print(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gatheringStateCallback(rtcGatheringState state, void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->gatheringState = state;
|
||||||
|
printf("Gathering state %s: %s\n", "offerer", rtcGatheringState_print(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void openCallback(void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->connected = true;
|
||||||
|
char buffer[256];
|
||||||
|
if (rtcGetDataChannelLabel(peer->dc, buffer, 256) >= 0)
|
||||||
|
printf("DataChannel %s: Received with label \"%s\"\n","offerer", buffer);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void closedCallback(void *ptr) {
|
||||||
|
Peer *peer = (Peer *)ptr;
|
||||||
|
peer->connected = false;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void messageCallback(const char *message, int size, void *ptr) {
|
||||||
|
// Peer *peer = (Peer *)ptr;
|
||||||
|
if (size < 0) { // negative size indicates a null-terminated string
|
||||||
|
printf("Message %s: %s\n", "offerer", message);
|
||||||
|
} else {
|
||||||
|
printf("Message %s: [binary of size %d]\n", "offerer", size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void deletePeer(Peer *peer) {
|
||||||
|
if (peer) {
|
||||||
|
if (peer->dc)
|
||||||
|
rtcDeleteDataChannel(peer->dc);
|
||||||
|
if (peer->pc)
|
||||||
|
rtcDeletePeerConnection(peer->pc);
|
||||||
|
free(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int all_space(const char *str) {
|
||||||
|
while (*str) {
|
||||||
|
if (!isspace(*str++)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* state_print(rtcState state) {
|
||||||
|
char *str = NULL;
|
||||||
|
switch (state) {
|
||||||
|
case RTC_NEW:
|
||||||
|
str = "RTC_NEW";
|
||||||
|
break;
|
||||||
|
case RTC_CONNECTING:
|
||||||
|
str = "RTC_CONNECTING";
|
||||||
|
break;
|
||||||
|
case RTC_CONNECTED:
|
||||||
|
str = "RTC_CONNECTED";
|
||||||
|
break;
|
||||||
|
case RTC_DISCONNECTED:
|
||||||
|
str = "RTC_DISCONNECTED";
|
||||||
|
break;
|
||||||
|
case RTC_FAILED:
|
||||||
|
str = "RTC_FAILED";
|
||||||
|
break;
|
||||||
|
case RTC_CLOSED:
|
||||||
|
str = "RTC_CLOSED";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
char* rtcGatheringState_print(rtcState state) {
|
||||||
|
char* str = NULL;
|
||||||
|
switch (state) {
|
||||||
|
case RTC_GATHERING_NEW:
|
||||||
|
str = "RTC_GATHERING_NEW";
|
||||||
|
break;
|
||||||
|
case RTC_GATHERING_INPROGRESS:
|
||||||
|
str = "RTC_GATHERING_INPROGRESS";
|
||||||
|
break;
|
||||||
|
case RTC_GATHERING_COMPLETE:
|
||||||
|
str = "RTC_GATHERING_COMPLETE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user