mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-23 15:48:03 +00:00
Compare commits
94 Commits
Author | SHA1 | Date | |
---|---|---|---|
679ecfc066 | |||
4e3ea69073 | |||
b79c886480 | |||
08da691911 | |||
754568506a | |||
7ac351d1b9 | |||
6d5cecbca1 | |||
abec5fc219 | |||
684b7ba925 | |||
d8515b6362 | |||
62da885028 | |||
ff2e83bbdc | |||
8f9e8e718e | |||
c6bee7b0d4 | |||
4d93303be8 | |||
1620ddfb03 | |||
452b742adc | |||
244c834992 | |||
ffe202a6a2 | |||
ea87e5ae09 | |||
4259b4e968 | |||
6aff5dc5bc | |||
99bae7f830 | |||
7598d992dc | |||
6380038584 | |||
6144bca0f7 | |||
6ec129f8f8 | |||
be394b7185 | |||
f008b5b447 | |||
5482912e18 | |||
fcc4eaf78b | |||
cca0742973 | |||
495b389e05 | |||
e83494df09 | |||
7bf87c6989 | |||
cb591de15f | |||
eb4540e319 | |||
5d34439cb7 | |||
b19e9077af | |||
4ff010b20b | |||
442e50825c | |||
9f2801b7b9 | |||
3b0bf3a152 | |||
fe4a9ec453 | |||
dc1d14adf1 | |||
14918c16e8 | |||
a023acfa33 | |||
f098019c1f | |||
a67ca9da9b | |||
613ebf2564 | |||
dc6427770c | |||
81e0a05a1a | |||
9ea613f05f | |||
eb4a764648 | |||
baf8a3adce | |||
d9aec59352 | |||
3ff5801512 | |||
fcc2577e11 | |||
b4865f26e4 | |||
fc6d5afdd9 | |||
7a49c0b88b | |||
679c0ccd2e | |||
ee3bc9694b | |||
0c0ba77de5 | |||
8729e0d2aa | |||
12098e7c41 | |||
90eb610bfe | |||
08ddfa1276 | |||
87df64a002 | |||
5af414d0df | |||
2443c72350 | |||
f033e4ab8f | |||
1a6dcdce6f | |||
100039eba8 | |||
e2005c789a | |||
819566b4c1 | |||
82caab8906 | |||
802516b2db | |||
0fcafad9c7 | |||
aab876d346 | |||
11ec8f7247 | |||
1597c9ae6f | |||
b093c4c3d5 | |||
447624322c | |||
422713cbdc | |||
d3d4187021 | |||
f2dd46e589 | |||
5b5debf260 | |||
86c3f914fb | |||
6a1fff13c1 | |||
91a854aa5b | |||
1181fdc599 | |||
fe3d92cebf | |||
c06d77bd8e |
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.7)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
project(libdatachannel
|
project(libdatachannel
|
||||||
VERSION 0.10.0
|
VERSION 0.10.5
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
|
set(PROJECT_DESCRIPTION "WebRTC Data Channels Library")
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ endif()
|
|||||||
|
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
|
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)
|
||||||
|
set(BUILD_SHARED_LIBS OFF) # to force usrsctp to be built static
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||||
@ -115,17 +116,15 @@ set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
|
|||||||
add_subdirectory(deps/plog)
|
add_subdirectory(deps/plog)
|
||||||
|
|
||||||
option(sctp_build_programs 0)
|
option(sctp_build_programs 0)
|
||||||
|
option(sctp_build_shared_lib 0)
|
||||||
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
|
add_subdirectory(deps/usrsctp EXCLUDE_FROM_ALL)
|
||||||
if (MSYS OR MINGW)
|
if (MSYS OR MINGW)
|
||||||
target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
target_compile_definitions(usrsctp PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
||||||
target_compile_definitions(usrsctp-static PUBLIC -DSCTP_STDINT_INCLUDE=<stdint.h>)
|
|
||||||
endif()
|
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)
|
|
||||||
endif()
|
endif()
|
||||||
add_library(Usrsctp::Usrsctp ALIAS usrsctp)
|
add_library(Usrsctp::Usrsctp ALIAS usrsctp)
|
||||||
add_library(Usrsctp::UsrsctpStatic ALIAS usrsctp-static)
|
|
||||||
|
|
||||||
if (NO_WEBSOCKET)
|
if (NO_WEBSOCKET)
|
||||||
add_library(datachannel SHARED
|
add_library(datachannel SHARED
|
||||||
@ -156,17 +155,17 @@ target_include_directories(datachannel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/includ
|
|||||||
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_link_libraries(datachannel PUBLIC Threads::Threads plog::plog)
|
target_link_libraries(datachannel PUBLIC Threads::Threads plog::plog)
|
||||||
target_link_libraries(datachannel PRIVATE Usrsctp::UsrsctpStatic)
|
target_link_libraries(datachannel PRIVATE Usrsctp::Usrsctp)
|
||||||
|
|
||||||
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 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_link_libraries(datachannel-static PUBLIC Threads::Threads plog::plog)
|
target_link_libraries(datachannel-static PUBLIC Threads::Threads plog::plog)
|
||||||
target_link_libraries(datachannel-static PRIVATE Usrsctp::UsrsctpStatic)
|
target_link_libraries(datachannel-static PRIVATE Usrsctp::Usrsctp)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(datachannel PRIVATE ws2_32) # winsock2
|
target_link_libraries(datachannel PUBLIC ws2_32) # winsock2
|
||||||
target_link_libraries(datachannel-static PRIVATE ws2_32) # winsock2
|
target_link_libraries(datachannel-static PUBLIC ws2_32) # winsock2
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NO_MEDIA)
|
if(NO_MEDIA)
|
||||||
@ -274,11 +273,7 @@ if(NOT NO_TESTS)
|
|||||||
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
|
set_target_properties(datachannel-tests PROPERTIES OUTPUT_NAME tests)
|
||||||
endif()
|
endif()
|
||||||
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(datachannel-tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(datachannel-tests datachannel-static) # DLL exports only the C API
|
|
||||||
else()
|
|
||||||
target_link_libraries(datachannel-tests datachannel)
|
target_link_libraries(datachannel-tests datachannel)
|
||||||
endif()
|
|
||||||
|
|
||||||
# Benchmark
|
# Benchmark
|
||||||
add_executable(datachannel-benchmark test/benchmark.cpp)
|
add_executable(datachannel-benchmark test/benchmark.cpp)
|
||||||
@ -290,12 +285,8 @@ if(NOT NO_TESTS)
|
|||||||
endif()
|
endif()
|
||||||
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)
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(datachannel-benchmark datachannel-static) # DLL exports only the C API
|
|
||||||
else()
|
|
||||||
target_link_libraries(datachannel-benchmark datachannel)
|
target_link_libraries(datachannel-benchmark datachannel)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
if(NOT NO_EXAMPLES AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
if(NOT NO_EXAMPLES AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||||
|
8
Jamfile
8
Jamfile
@ -15,9 +15,9 @@ lib libdatachannel
|
|||||||
: # requirements
|
: # requirements
|
||||||
<cxxstd>17
|
<cxxstd>17
|
||||||
<include>./include/rtc
|
<include>./include/rtc
|
||||||
<define>USE_NICE=0
|
|
||||||
<define>RTC_ENABLE_MEDIA=0
|
<define>RTC_ENABLE_MEDIA=0
|
||||||
<define>RTC_ENABLE_WEBSOCKET=0
|
<define>RTC_ENABLE_WEBSOCKET=0
|
||||||
|
<define>USE_NICE=0
|
||||||
<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
|
<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
|
||||||
<toolset>msvc:<define>NOMINMAX
|
<toolset>msvc:<define>NOMINMAX
|
||||||
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS
|
||||||
@ -32,6 +32,8 @@ lib libdatachannel
|
|||||||
<link>static
|
<link>static
|
||||||
: # usage requirements
|
: # usage requirements
|
||||||
<include>./include
|
<include>./include
|
||||||
|
<define>RTC_ENABLE_MEDIA=0
|
||||||
|
<define>RTC_ENABLE_WEBSOCKET=0
|
||||||
<library>/libdatachannel//plog
|
<library>/libdatachannel//plog
|
||||||
<toolset>gcc:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
<toolset>gcc:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
||||||
<toolset>clang:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
<toolset>clang:<cxxflags>"-pthread -Wno-pedantic -Wno-unused-parameter -Wno-unused-variable"
|
||||||
@ -94,7 +96,7 @@ rule make_libusrsctp ( targets * : sources * : properties * )
|
|||||||
}
|
}
|
||||||
actions make_libusrsctp
|
actions make_libusrsctp
|
||||||
{
|
{
|
||||||
(cd $(CWD)/deps/usrsctp && mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=$(VARIANT) -DCMAKE_C_FLAGS="-fPIC -Wno-unknown-warning-option -Wno-format-truncation" .. && make -j2 usrsctp-static)
|
(cd $(CWD)/deps/usrsctp && mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake -DCMAKE_BUILD_TYPE=$(VARIANT) -DCMAKE_C_FLAGS="-fPIC -Wno-unknown-warning-option -Wno-format-truncation" -Dsctp_build_shared_lib=0 -Dsctp_build_programs=0 .. && make -j2 usrsctp)
|
||||||
cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/libusrsctp.a $(<)
|
cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/libusrsctp.a $(<)
|
||||||
}
|
}
|
||||||
rule make_libusrsctp_msvc ( targets * : sources * : properties * )
|
rule make_libusrsctp_msvc ( targets * : sources * : properties * )
|
||||||
@ -109,7 +111,7 @@ actions make_libusrsctp_msvc
|
|||||||
cd $(CWD)/deps/usrsctp
|
cd $(CWD)/deps/usrsctp
|
||||||
mkdir $(BUILD_DIR)
|
mkdir $(BUILD_DIR)
|
||||||
cd $(BUILD_DIR)
|
cd $(BUILD_DIR)
|
||||||
cmake -G "Visual Studio 16 2019" ..
|
cmake -G "Visual Studio 16 2019" -Dsctp_build_shared_lib=0 -Dsctp_build_programs=0 ..
|
||||||
msbuild usrsctplib.sln /property:Configuration=$(VARIANT)
|
msbuild usrsctplib.sln /property:Configuration=$(VARIANT)
|
||||||
cd %OLDD%
|
cd %OLDD%
|
||||||
cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/Release/usrsctp.lib $(<)
|
cp $(CWD)/deps/usrsctp/$(BUILD_DIR)/usrsctplib/Release/usrsctp.lib $(<)
|
||||||
|
@ -27,8 +27,9 @@ Features:
|
|||||||
- Full IPv6 support
|
- Full IPv6 support
|
||||||
- Trickle ICE ([draft-ietf-ice-trickle-21](https://tools.ietf.org/html/draft-ietf-ice-trickle-21))
|
- Trickle ICE ([draft-ietf-ice-trickle-21](https://tools.ietf.org/html/draft-ietf-ice-trickle-21))
|
||||||
- JSEP compatible ([draft-ietf-rtcweb-jsep-26](https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26))
|
- JSEP compatible ([draft-ietf-rtcweb-jsep-26](https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26))
|
||||||
- SRTP and SRTCP key derivation from DTLS ([RFC5764](https://tools.ietf.org/html/rfc5764))
|
|
||||||
- Multicast DNS candidates ([draft-ietf-rtcweb-mdns-ice-candidates-04](https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-04))
|
- Multicast DNS candidates ([draft-ietf-rtcweb-mdns-ice-candidates-04](https://tools.ietf.org/html/draft-ietf-rtcweb-mdns-ice-candidates-04))
|
||||||
|
- SRTP and SRTCP key derivation from DTLS ([RFC5764](https://tools.ietf.org/html/rfc5764))
|
||||||
|
- Differentiated Services QoS ([draft-ietf-tsvwg-rtcweb-qos-18](https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18))
|
||||||
- TURN relaying ([RFC5766](https://tools.ietf.org/html/rfc5766)) with [libnice](https://github.com/libnice/libnice) as ICE backend
|
- TURN relaying ([RFC5766](https://tools.ietf.org/html/rfc5766)) with [libnice](https://github.com/libnice/libnice) as ICE backend
|
||||||
|
|
||||||
Note only SDP BUNDLE mode is supported for media multiplexing ([draft-ietf-mmusic-sdp-bundle-negotiation-54](https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54)). The behavior is equivalent to the JSEP bundle-only policy: the library always negociates one unique network component, where SRTP media streams are multiplexed with SRTCP control packets ([RFC5761](https://tools.ietf.org/html/rfc5761)) and SCTP/DTLS data traffic ([RFC5764](https://tools.ietf.org/html/rfc5764)).
|
Note only SDP BUNDLE mode is supported for media multiplexing ([draft-ietf-mmusic-sdp-bundle-negotiation-54](https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54)). The behavior is equivalent to the JSEP bundle-only policy: the library always negociates one unique network component, where SRTP media streams are multiplexed with SRTCP control packets ([RFC5761](https://tools.ietf.org/html/rfc5761)) and SCTP/DTLS data traffic ([RFC5764](https://tools.ietf.org/html/rfc5764)).
|
||||||
|
2
deps/libjuice
vendored
2
deps/libjuice
vendored
Submodule deps/libjuice updated: 7afe3940dd...421c650d9d
2
deps/libsrtp
vendored
2
deps/libsrtp
vendored
Submodule deps/libsrtp updated: d02d21111e...f379f48412
2
deps/usrsctp
vendored
2
deps/usrsctp
vendored
Submodule deps/usrsctp updated: 0db9691000...2e754d5822
@ -9,14 +9,9 @@ target_compile_definitions(datachannel-client PUBLIC STATIC_GETOPT)
|
|||||||
else()
|
else()
|
||||||
add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
|
add_executable(datachannel-client main.cpp parse_cl.cpp parse_cl.h)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -23,17 +23,18 @@
|
|||||||
|
|
||||||
#include "rtc/rtc.hpp"
|
#include "rtc/rtc.hpp"
|
||||||
|
|
||||||
|
#include "parse_cl.h"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <future>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <thread>
|
|
||||||
#include <future>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <thread>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "parse_cl.h"
|
|
||||||
|
|
||||||
using namespace rtc;
|
using namespace rtc;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -47,23 +48,21 @@ unordered_map<string, shared_ptr<PeerConnection>> peerConnectionMap;
|
|||||||
unordered_map<string, shared_ptr<DataChannel>> dataChannelMap;
|
unordered_map<string, shared_ptr<DataChannel>> dataChannelMap;
|
||||||
|
|
||||||
string localId;
|
string localId;
|
||||||
bool echoDataChannelMessages = false;
|
|
||||||
|
|
||||||
shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
||||||
weak_ptr<WebSocket> wws, string id);
|
weak_ptr<WebSocket> wws, string id);
|
||||||
void printReceived(bool echoed, string id, string type, size_t length);
|
|
||||||
string randomId(size_t length);
|
string randomId(size_t length);
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) try {
|
int main(int argc, char **argv) try {
|
||||||
auto params = std::make_unique<Cmdline>(argc, argv);
|
auto params = std::make_unique<Cmdline>(argc, argv);
|
||||||
|
|
||||||
rtc::InitLogger(LogLevel::Debug);
|
rtc::InitLogger(LogLevel::Info);
|
||||||
|
|
||||||
Configuration config;
|
Configuration config;
|
||||||
string stunServer = "";
|
string stunServer = "";
|
||||||
if (params->noStun()) {
|
if (params->noStun()) {
|
||||||
cout << "No STUN server is configured. Only local hosts and public IP addresses supported." << endl;
|
cout << "No STUN server is configured. Only local hosts and public IP addresses supported."
|
||||||
|
<< endl;
|
||||||
} else {
|
} else {
|
||||||
if (params->stunServer().substr(0, 5).compare("stun:") != 0) {
|
if (params->stunServer().substr(0, 5).compare("stun:") != 0) {
|
||||||
stunServer = "stun:";
|
stunServer = "stun:";
|
||||||
@ -76,10 +75,6 @@ int main(int argc, char **argv) try {
|
|||||||
localId = randomId(4);
|
localId = randomId(4);
|
||||||
cout << "The local ID is: " << localId << endl;
|
cout << "The local ID is: " << localId << endl;
|
||||||
|
|
||||||
echoDataChannelMessages = params->echoDataChannelMessages();
|
|
||||||
cout << "Received data channel messages will be "
|
|
||||||
<< (echoDataChannelMessages ? "echoed back to sender" : "printed to stdout") << endl;
|
|
||||||
|
|
||||||
auto ws = make_shared<WebSocket>();
|
auto ws = make_shared<WebSocket>();
|
||||||
|
|
||||||
std::promise<void> wsPromise;
|
std::promise<void> wsPromise;
|
||||||
@ -171,20 +166,12 @@ int main(int argc, char **argv) try {
|
|||||||
|
|
||||||
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
||||||
|
|
||||||
dc->onMessage([id, wdc = make_weak_ptr(dc)](const variant<binary, string> &message) {
|
dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) {
|
||||||
static bool firstMessage = true;
|
if (holds_alternative<string>(data))
|
||||||
if (holds_alternative<string>(message) && (!echoDataChannelMessages || firstMessage)) {
|
cout << "Message from " << id << " received: " << get<string>(data) << endl;
|
||||||
cout << "Message from " << id << " received: " << get<string>(message) << endl;
|
else
|
||||||
firstMessage = false;
|
cout << "Binary message from " << id
|
||||||
} else if (echoDataChannelMessages) {
|
<< " received, size=" << get<binary>(data).size() << endl;
|
||||||
bool echoed = false;
|
|
||||||
if (auto dc = wdc.lock()) {
|
|
||||||
dc->send(message);
|
|
||||||
echoed = true;
|
|
||||||
}
|
|
||||||
printReceived(echoed, id, (holds_alternative<string>(message) ? "text" : "binary"),
|
|
||||||
get<string>(message).length());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dataChannelMap.emplace(id, dc);
|
dataChannelMap.emplace(id, dc);
|
||||||
@ -237,20 +224,12 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
|||||||
|
|
||||||
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
dc->onClosed([id]() { cout << "DataChannel from " << id << " closed" << endl; });
|
||||||
|
|
||||||
dc->onMessage([id, wdc = make_weak_ptr(dc)](const variant<binary, string> &message) {
|
dc->onMessage([id, wdc = make_weak_ptr(dc)](variant<binary, string> data) {
|
||||||
static bool firstMessage = true;
|
if (holds_alternative<string>(data))
|
||||||
if (holds_alternative<string>(message) && (!echoDataChannelMessages || firstMessage)) {
|
cout << "Message from " << id << " received: " << get<string>(data) << endl;
|
||||||
cout << "Message from " << id << " received: " << get<string>(message) << endl;
|
else
|
||||||
firstMessage = false;
|
cout << "Binary message from " << id
|
||||||
} else if (echoDataChannelMessages) {
|
<< " received, size=" << get<binary>(data).size() << endl;
|
||||||
bool echoed = false;
|
|
||||||
if (auto dc = wdc.lock()) {
|
|
||||||
dc->send(message);
|
|
||||||
echoed = true;
|
|
||||||
}
|
|
||||||
printReceived(echoed, id, (holds_alternative<string>(message) ? "text" : "binary"),
|
|
||||||
get<string>(message).length());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dc->send("Hello from " + localId);
|
dc->send("Hello from " + localId);
|
||||||
@ -262,20 +241,6 @@ shared_ptr<PeerConnection> createPeerConnection(const Configuration &config,
|
|||||||
return pc;
|
return pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to print received pings
|
|
||||||
void printReceived(bool echoed, string id, string type, size_t length) {
|
|
||||||
static long count = 0;
|
|
||||||
static long freq = 100;
|
|
||||||
if (!(++count%freq)) {
|
|
||||||
cout << "Received " << count << " pings in total from " << id << ", most recent of type "
|
|
||||||
<< type << " and " << (echoed ? "" : "un") << "successfully echoed most recent ping of size "
|
|
||||||
<< length << " back to " << id << endl;
|
|
||||||
if (count >= (freq * 10) && freq < 1000000) {
|
|
||||||
freq *= 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to generate a random ID
|
// Helper function to generate a random ID
|
||||||
string randomId(size_t length) {
|
string randomId(size_t length) {
|
||||||
static const string characters(
|
static const string characters(
|
||||||
|
@ -43,38 +43,30 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
|
|||||||
|
|
||||||
static struct option long_options[] =
|
static struct option long_options[] =
|
||||||
{
|
{
|
||||||
{"echo", no_argument, NULL, 'e'},
|
|
||||||
{"noStun", no_argument, NULL, 'n'},
|
{"noStun", no_argument, NULL, 'n'},
|
||||||
{"stunServer", required_argument, NULL, 's'},
|
{"stunServer", required_argument, NULL, 's'},
|
||||||
{"stunPort", required_argument, NULL, 't'},
|
{"stunPort", required_argument, NULL, 't'},
|
||||||
{"webSocketServer", required_argument, NULL, 'w'},
|
{"webSocketServer", required_argument, NULL, 'w'},
|
||||||
{"webSocketPort", required_argument, NULL, 'x'},
|
{"webSocketPort", required_argument, NULL, 'x'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
|
||||||
{NULL, 0, NULL, 0}
|
{NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
_program_name += argv[0];
|
_program_name += argv[0];
|
||||||
|
|
||||||
/* default values */
|
/* default values */
|
||||||
_e = false;
|
|
||||||
_n = false;
|
_n = false;
|
||||||
_s = "stun.l.google.com";
|
_s = "stun.l.google.com";
|
||||||
_t = 19302;
|
_t = 19302;
|
||||||
_w = "localhost";
|
_w = "localhost";
|
||||||
_x = 8000;
|
_x = 8000;
|
||||||
_h = false;
|
_h = false;
|
||||||
_v = false;
|
|
||||||
|
|
||||||
optind = 0;
|
optind = 0;
|
||||||
while ((c = getopt_long (argc, argv, "s:t:w:x:enhv", long_options, &optind)) != - 1)
|
while ((c = getopt_long (argc, argv, "s:t:w:x:enhv", long_options, &optind)) != - 1)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'e':
|
|
||||||
_e = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
_n = true;
|
_n = true;
|
||||||
break;
|
break;
|
||||||
@ -124,11 +116,6 @@ Cmdline::Cmdline (int argc, char *argv[]) // ISO C++17 not allowed: throw (std::
|
|||||||
this->usage (EXIT_SUCCESS);
|
this->usage (EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'v':
|
|
||||||
_v = true;
|
|
||||||
this->version (EXIT_SUCCESS);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this->usage (EXIT_FAILURE);
|
this->usage (EXIT_FAILURE);
|
||||||
|
|
||||||
@ -155,8 +142,6 @@ void Cmdline::usage (int status)
|
|||||||
std::cout << "\
|
std::cout << "\
|
||||||
usage: " << _program_name << " [ -enstwxhv ] \n\
|
usage: " << _program_name << " [ -enstwxhv ] \n\
|
||||||
libdatachannel client implementing WebRTC Data Channels with WebSocket signaling\n\
|
libdatachannel client implementing WebRTC Data Channels with WebSocket signaling\n\
|
||||||
[ -e ] [ --echo ] (type=FLAG)\n\
|
|
||||||
Echo data channel messages back to sender rather than putting to stdout.\n\
|
|
||||||
[ -n ] [ --noStun ] (type=FLAG)\n\
|
[ -n ] [ --noStun ] (type=FLAG)\n\
|
||||||
Do NOT use a stun server (overrides -s and -t).\n\
|
Do NOT use a stun server (overrides -s and -t).\n\
|
||||||
[ -s ] [ --stunServer ] (type=STRING, default=stun.l.google.com)\n\
|
[ -s ] [ --stunServer ] (type=STRING, default=stun.l.google.com)\n\
|
||||||
@ -168,15 +153,8 @@ libdatachannel client implementing WebRTC Data Channels with WebSocket signaling
|
|||||||
[ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\
|
[ -x ] [ --webSocketPort ] (type=INTEGER, range=0...65535, default=8000)\n\
|
||||||
Web socket server port.\n\
|
Web socket server port.\n\
|
||||||
[ -h ] [ --help ] (type=FLAG)\n\
|
[ -h ] [ --help ] (type=FLAG)\n\
|
||||||
Display this help and exit.\n\
|
Display this help and exit.\n";
|
||||||
[ -v ] [ --version ] (type=FLAG)\n\
|
|
||||||
Output version information and exit.\n";
|
|
||||||
}
|
}
|
||||||
exit (status);
|
exit (status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cmdline::version (int status)
|
|
||||||
{
|
|
||||||
std::cout << _program_name << " v0.5\n";
|
|
||||||
exit (status);
|
|
||||||
}
|
|
||||||
|
@ -34,14 +34,12 @@ class Cmdline
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/* parameters */
|
/* parameters */
|
||||||
bool _e;
|
|
||||||
bool _n;
|
bool _n;
|
||||||
std::string _s;
|
std::string _s;
|
||||||
int _t;
|
int _t;
|
||||||
std::string _w;
|
std::string _w;
|
||||||
int _x;
|
int _x;
|
||||||
bool _h;
|
bool _h;
|
||||||
bool _v;
|
|
||||||
|
|
||||||
/* other stuff to keep track of */
|
/* other stuff to keep track of */
|
||||||
std::string _program_name;
|
std::string _program_name;
|
||||||
@ -55,20 +53,15 @@ public:
|
|||||||
/* usage function */
|
/* usage function */
|
||||||
void usage (int status);
|
void usage (int status);
|
||||||
|
|
||||||
/* version function */
|
|
||||||
void version (int status);
|
|
||||||
|
|
||||||
/* return next (non-option) parameter */
|
/* return next (non-option) parameter */
|
||||||
int next_param () { return _optind; }
|
int next_param () { return _optind; }
|
||||||
|
|
||||||
bool echoDataChannelMessages () const { return _e; }
|
|
||||||
bool noStun () const { return _n; }
|
bool noStun () const { return _n; }
|
||||||
std::string stunServer () const { return _s; }
|
std::string stunServer () const { return _s; }
|
||||||
int stunPort () const { return _t; }
|
int stunPort () const { return _t; }
|
||||||
std::string webSocketServer () const { return _w; }
|
std::string webSocketServer () const { return _w; }
|
||||||
int webSocketPort () const { return _x; }
|
int webSocketPort () const { return _x; }
|
||||||
bool h () const { return _h; }
|
bool h () const { return _h; }
|
||||||
bool v () const { return _v; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -137,7 +137,7 @@ int main(int argc, char **argv) {
|
|||||||
size_t candidate_size = 0;
|
size_t candidate_size = 0;
|
||||||
|
|
||||||
if (getline(&candidate, &candidate_size, stdin)) {
|
if (getline(&candidate, &candidate_size, stdin)) {
|
||||||
rtcAddRemoteCandidate(peer->pc, candidate, "0");
|
rtcAddRemoteCandidate(peer->pc, candidate, NULL);
|
||||||
free(candidate);
|
free(candidate);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -140,7 +140,7 @@ int main(int argc, char **argv) {
|
|||||||
char *candidate = NULL;
|
char *candidate = NULL;
|
||||||
size_t candidate_size = 0;
|
size_t candidate_size = 0;
|
||||||
if (getline(&candidate, &candidate_size, stdin)) {
|
if (getline(&candidate, &candidate_size, stdin)) {
|
||||||
rtcAddRemoteCandidate(peer->pc, candidate, "0");
|
rtcAddRemoteCandidate(peer->pc, candidate, NULL);
|
||||||
free(candidate);
|
free(candidate);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -4,19 +4,11 @@ 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)
|
||||||
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)
|
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)
|
||||||
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)
|
target_link_libraries(datachannel-copy-paste-answerer datachannel)
|
||||||
endif()
|
|
||||||
|
|
||||||
|
@ -4,11 +4,5 @@ add_executable(datachannel-media main.cpp)
|
|||||||
set_target_properties(datachannel-media PROPERTIES
|
set_target_properties(datachannel-media PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
OUTPUT_NAME media)
|
OUTPUT_NAME media)
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(datachannel-media datachannel-static) # DLL exports only the C API
|
|
||||||
else()
|
|
||||||
target_link_libraries(datachannel-media datachannel)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(datachannel-media datachannel nlohmann_json)
|
target_link_libraries(datachannel-media datachannel nlohmann_json)
|
||||||
|
|
||||||
|
@ -4,12 +4,5 @@ add_executable(datachannel-sfu-media main.cpp)
|
|||||||
set_target_properties(datachannel-sfu-media PROPERTIES
|
set_target_properties(datachannel-sfu-media PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
OUTPUT_NAME sfu-media)
|
OUTPUT_NAME sfu-media)
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(datachannel-sfu-media datachannel-static) # DLL exports only the C API
|
|
||||||
else()
|
|
||||||
target_link_libraries(datachannel-sfu-media datachannel)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(datachannel-sfu-media datachannel nlohmann_json)
|
target_link_libraries(datachannel-sfu-media datachannel nlohmann_json)
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ function openSignaling(url) {
|
|||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
if(typeof(e.data) != 'string') return;
|
if(typeof(e.data) != 'string') return;
|
||||||
const message = JSON.parse(e.data);
|
const message = JSON.parse(e.data);
|
||||||
|
console.log(message);
|
||||||
const { id, type } = message;
|
const { id, type } = message;
|
||||||
|
|
||||||
let pc = peerConnectionMap[id];
|
let pc = peerConnectionMap[id];
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
class Candidate {
|
class RTC_CPP_EXPORT Candidate {
|
||||||
public:
|
public:
|
||||||
enum class Family { Unresolved, Ipv4, Ipv6 };
|
enum class Family { Unresolved, Ipv4, Ipv6 };
|
||||||
enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
|
enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
|
||||||
@ -40,35 +40,44 @@ public:
|
|||||||
enum class ResolveMode { Simple, Lookup };
|
enum class ResolveMode { Simple, Lookup };
|
||||||
bool resolve(ResolveMode mode = ResolveMode::Simple);
|
bool resolve(ResolveMode mode = ResolveMode::Simple);
|
||||||
|
|
||||||
|
Type type() const;
|
||||||
|
TransportType transportType() const;
|
||||||
|
uint32_t priority() const;
|
||||||
string candidate() const;
|
string candidate() const;
|
||||||
string mid() const;
|
string mid() const;
|
||||||
operator string() const;
|
operator string() const;
|
||||||
|
|
||||||
|
bool operator==(const Candidate &other) const;
|
||||||
|
bool operator!=(const Candidate &other) const;
|
||||||
|
|
||||||
bool isResolved() const;
|
bool isResolved() const;
|
||||||
Family family() const;
|
Family family() const;
|
||||||
Type type() const;
|
|
||||||
TransportType transportType() const;
|
|
||||||
std::optional<string> address() const;
|
std::optional<string> address() const;
|
||||||
std::optional<uint16_t> port() const;
|
std::optional<uint16_t> port() const;
|
||||||
std::optional<uint32_t> priority() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
string mCandidate;
|
void parse(string candidate);
|
||||||
|
|
||||||
|
string mFoundation;
|
||||||
|
uint32_t mComponent, mPriority;
|
||||||
|
string mTypeString, mTransportString;
|
||||||
|
Type mType;
|
||||||
|
TransportType mTransportType;
|
||||||
|
string mNode, mService;
|
||||||
|
string mTail;
|
||||||
|
|
||||||
std::optional<string> mMid;
|
std::optional<string> mMid;
|
||||||
|
|
||||||
// Extracted on resolution
|
// Extracted on resolution
|
||||||
Family mFamily;
|
Family mFamily;
|
||||||
Type mType;
|
|
||||||
TransportType mTransportType;
|
|
||||||
string mAddress;
|
string mAddress;
|
||||||
uint16_t mPort;
|
uint16_t mPort;
|
||||||
uint32_t mPriority;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate);
|
||||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
|
||||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,13 +28,14 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
class Channel {
|
class RTC_CPP_EXPORT Channel {
|
||||||
public:
|
public:
|
||||||
Channel() = default;
|
Channel() = default;
|
||||||
virtual ~Channel() = default;
|
virtual ~Channel() = default;
|
||||||
|
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
virtual bool send(message_variant data) = 0; // returns false if buffered
|
virtual bool send(message_variant data) = 0; // returns false if buffered
|
||||||
|
virtual bool send(const byte *data, size_t size) = 0;
|
||||||
|
|
||||||
virtual bool isOpen() const = 0;
|
virtual bool isOpen() const = 0;
|
||||||
virtual bool isClosed() const = 0;
|
virtual bool isClosed() const = 0;
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
struct IceServer {
|
struct RTC_CPP_EXPORT IceServer {
|
||||||
enum class Type { Stun, Turn };
|
enum class Type { Stun, Turn };
|
||||||
enum class RelayType { TurnUdp, TurnTcp, TurnTls };
|
enum class RelayType { TurnUdp, TurnTcp, TurnTls };
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ struct IceServer {
|
|||||||
RelayType relayType;
|
RelayType relayType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProxyServer {
|
struct RTC_CPP_EXPORT ProxyServer {
|
||||||
enum class Type { None = 0, Socks5, Http, Last = Http };
|
enum class Type { None = 0, Socks5, Http, Last = Http };
|
||||||
|
|
||||||
ProxyServer(Type type_, string ip_, uint16_t port_, string username_ = "",
|
ProxyServer(Type type_, string ip_, uint16_t port_, string username_ = "",
|
||||||
@ -64,7 +64,7 @@ struct ProxyServer {
|
|||||||
string password;
|
string password;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Configuration {
|
struct RTC_CPP_EXPORT Configuration {
|
||||||
std::vector<IceServer> iceServers;
|
std::vector<IceServer> iceServers;
|
||||||
std::optional<ProxyServer> proxyServer;
|
std::optional<ProxyServer> proxyServer;
|
||||||
bool enableIceTcp = false;
|
bool enableIceTcp = false;
|
||||||
|
@ -36,7 +36,7 @@ namespace rtc {
|
|||||||
class SctpTransport;
|
class SctpTransport;
|
||||||
class PeerConnection;
|
class PeerConnection;
|
||||||
|
|
||||||
class DataChannel : public std::enable_shared_from_this<DataChannel>, public Channel {
|
class RTC_CPP_EXPORT DataChannel : public std::enable_shared_from_this<DataChannel>, public Channel {
|
||||||
public:
|
public:
|
||||||
DataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
|
DataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label, string protocol,
|
||||||
Reliability reliability);
|
Reliability reliability);
|
||||||
@ -50,7 +50,7 @@ public:
|
|||||||
|
|
||||||
void close(void) override;
|
void close(void) override;
|
||||||
bool send(message_variant data) override;
|
bool send(message_variant data) override;
|
||||||
bool send(const byte *data, size_t size);
|
bool send(const byte *data, size_t size) override;
|
||||||
template <typename Buffer> bool sendBuffer(const Buffer &buf);
|
template <typename Buffer> bool sendBuffer(const Buffer &buf);
|
||||||
template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
|
template <typename Iterator> bool sendBuffer(Iterator first, Iterator last);
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ private:
|
|||||||
friend class PeerConnection;
|
friend class PeerConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NegociatedDataChannel final : public DataChannel {
|
class RTC_CPP_EXPORT NegociatedDataChannel final : public DataChannel {
|
||||||
public:
|
public:
|
||||||
NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label,
|
NegociatedDataChannel(std::weak_ptr<PeerConnection> pc, uint16_t stream, string label,
|
||||||
string protocol, Reliability reliability);
|
string protocol, Reliability reliability);
|
||||||
|
@ -32,7 +32,16 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
class Description {
|
const string DEFAULT_AUDIO_PROFILE =
|
||||||
|
"minptime=10;maxaveragebitrate=96000;stereo=1;sprop-stereo=1;useinbandfec=1";
|
||||||
|
|
||||||
|
// Use Constrained Baseline profile Level 4.2 (necessary for Firefox)
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs
|
||||||
|
// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this.
|
||||||
|
const string DEFAULT_VIDEO_PROFILE =
|
||||||
|
"profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1";
|
||||||
|
|
||||||
|
class RTC_CPP_EXPORT Description {
|
||||||
public:
|
public:
|
||||||
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
|
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
|
||||||
enum class Role { ActPass, Passive, Active };
|
enum class Role { ActPass, Passive, Active };
|
||||||
@ -53,6 +62,7 @@ public:
|
|||||||
void hintType(Type type);
|
void hintType(Type type);
|
||||||
void setFingerprint(string fingerprint);
|
void setFingerprint(string fingerprint);
|
||||||
|
|
||||||
|
bool hasCandidate(const Candidate &candidate) const;
|
||||||
void addCandidate(Candidate candidate);
|
void addCandidate(Candidate candidate);
|
||||||
void addCandidates(std::vector<Candidate> candidates);
|
void addCandidates(std::vector<Candidate> candidates);
|
||||||
void endCandidates();
|
void endCandidates();
|
||||||
@ -62,7 +72,7 @@ public:
|
|||||||
string generateSdp(string_view eol) const;
|
string generateSdp(string_view eol) const;
|
||||||
string generateApplicationSdp(string_view eol) const;
|
string generateApplicationSdp(string_view eol) const;
|
||||||
|
|
||||||
class Entry {
|
class RTC_CPP_EXPORT Entry {
|
||||||
public:
|
public:
|
||||||
virtual ~Entry() = default;
|
virtual ~Entry() = default;
|
||||||
|
|
||||||
@ -94,7 +104,7 @@ public:
|
|||||||
Direction mDirection;
|
Direction mDirection;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Application : public Entry {
|
struct RTC_CPP_EXPORT Application : public Entry {
|
||||||
public:
|
public:
|
||||||
Application(string mid = "data");
|
Application(string mid = "data");
|
||||||
virtual ~Application() = default;
|
virtual ~Application() = default;
|
||||||
@ -119,7 +129,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Media (non-data)
|
// Media (non-data)
|
||||||
class Media : public Entry {
|
class RTC_CPP_EXPORT Media : public Entry {
|
||||||
public:
|
public:
|
||||||
Media(const string &sdp);
|
Media(const string &sdp);
|
||||||
Media(const string &mline, string mid, Direction dir = Direction::SendOnly);
|
Media(const string &mline, string mid, Direction dir = Direction::SendOnly);
|
||||||
@ -130,9 +140,10 @@ public:
|
|||||||
|
|
||||||
void removeFormat(const string &fmt);
|
void removeFormat(const string &fmt);
|
||||||
|
|
||||||
void addSSRC(uint32_t ssrc, std::string name);
|
void addSSRC(uint32_t ssrc, std::optional<string> name,
|
||||||
void addSSRC(uint32_t ssrc);
|
std::optional<string> msid = nullopt);
|
||||||
void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, string name);
|
void replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
|
||||||
|
std::optional<string> msid = nullopt);
|
||||||
bool hasSSRC(uint32_t ssrc);
|
bool hasSSRC(uint32_t ssrc);
|
||||||
std::vector<uint32_t> getSSRCs();
|
std::vector<uint32_t> getSSRCs();
|
||||||
|
|
||||||
@ -141,6 +152,9 @@ public:
|
|||||||
|
|
||||||
bool hasPayloadType(int payloadType) const;
|
bool hasPayloadType(int payloadType) const;
|
||||||
|
|
||||||
|
void addRTXCodec(unsigned int payloadType, unsigned int originalPayloadType,
|
||||||
|
unsigned int clockRate);
|
||||||
|
|
||||||
virtual void parseSdpLine(string_view line) override;
|
virtual void parseSdpLine(string_view line) override;
|
||||||
|
|
||||||
struct RTPMap {
|
struct RTPMap {
|
||||||
@ -149,7 +163,7 @@ public:
|
|||||||
|
|
||||||
void removeFB(const string &string);
|
void removeFB(const string &string);
|
||||||
void addFB(const string &string);
|
void addFB(const string &string);
|
||||||
void addAttribute(std::string attr) { fmtps.emplace_back(attr); }
|
void addAttribute(string attr) { fmtps.emplace_back(std::move(attr)); }
|
||||||
|
|
||||||
int pt;
|
int pt;
|
||||||
string format;
|
string format;
|
||||||
@ -180,21 +194,27 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void addRTPMap(const RTPMap &map);
|
void addRTPMap(const RTPMap &map);
|
||||||
|
|
||||||
|
void removeSSRC(uint32_t oldSSRC);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Audio : public Media {
|
class RTC_CPP_EXPORT Audio : public Media {
|
||||||
public:
|
public:
|
||||||
Audio(string mid = "audio", Direction dir = Direction::SendOnly);
|
Audio(string mid = "audio", Direction dir = Direction::SendOnly);
|
||||||
|
|
||||||
void addAudioCodec(int payloadType, const string &codec);
|
void addAudioCodec(int payloadType, string codec,
|
||||||
|
std::optional<string> profile = DEFAULT_AUDIO_PROFILE);
|
||||||
|
|
||||||
void addOpusCodec(int payloadType);
|
void addOpusCodec(int payloadType);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Video : public Media {
|
class RTC_CPP_EXPORT Video : public Media {
|
||||||
public:
|
public:
|
||||||
Video(string mid = "video", Direction dir = Direction::SendOnly);
|
Video(string mid = "video", Direction dir = Direction::SendOnly);
|
||||||
|
|
||||||
void addVideoCodec(int payloadType, const string &codec);
|
void addVideoCodec(int payloadType, string codec,
|
||||||
|
std::optional<string> profile = DEFAULT_VIDEO_PROFILE);
|
||||||
|
|
||||||
void addH264Codec(int payloadType);
|
void addH264Codec(int payloadType);
|
||||||
void addVP8Codec(int payloadType);
|
void addVP8Codec(int payloadType);
|
||||||
void addVP9Codec(int payloadType);
|
void addVP9Codec(int payloadType);
|
||||||
@ -244,8 +264,8 @@ private:
|
|||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
|
||||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
|
||||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,9 +28,15 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#define RTC_CPP_EXPORT __declspec(dllexport)
|
||||||
#ifndef _WIN32_WINNT
|
#ifndef _WIN32_WINNT
|
||||||
#define _WIN32_WINNT 0x0602 // Windows 8
|
#define _WIN32_WINNT 0x0602 // Windows 8
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable:4251) // disable "X needs to have dll-interface..."
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define RTC_CPP_EXPORT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
@ -102,12 +108,12 @@ private:
|
|||||||
std::function<void()> function;
|
std::function<void()> function;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename... P> class synchronized_callback {
|
template <typename... Args> class synchronized_callback {
|
||||||
public:
|
public:
|
||||||
synchronized_callback() = default;
|
synchronized_callback() = default;
|
||||||
synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); }
|
synchronized_callback(synchronized_callback &&cb) { *this = std::move(cb); }
|
||||||
synchronized_callback(const synchronized_callback &cb) { *this = cb; }
|
synchronized_callback(const synchronized_callback &cb) { *this = cb; }
|
||||||
synchronized_callback(std::function<void(P...)> func) { *this = std::move(func); }
|
synchronized_callback(std::function<void(Args...)> func) { *this = std::move(func); }
|
||||||
~synchronized_callback() { *this = nullptr; }
|
~synchronized_callback() { *this = nullptr; }
|
||||||
|
|
||||||
synchronized_callback &operator=(synchronized_callback &&cb) {
|
synchronized_callback &operator=(synchronized_callback &&cb) {
|
||||||
@ -123,16 +129,16 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized_callback &operator=(std::function<void(P...)> func) {
|
synchronized_callback &operator=(std::function<void(Args...)> func) {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
callback = std::move(func);
|
callback = std::move(func);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(P... args) const {
|
void operator()(Args... args) const {
|
||||||
std::lock_guard lock(mutex);
|
std::lock_guard lock(mutex);
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(args...);
|
callback(std::move(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const {
|
||||||
@ -140,8 +146,12 @@ public:
|
|||||||
return callback ? true : false;
|
return callback ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::function<void(Args...)> wrap() const {
|
||||||
|
return [this](Args... args) { (*this)(std::move(args)...); };
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(P...)> callback;
|
std::function<void(Args...)> callback;
|
||||||
mutable std::recursive_mutex mutex;
|
mutable std::recursive_mutex mutex;
|
||||||
};
|
};
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
@ -27,7 +27,7 @@ namespace rtc {
|
|||||||
|
|
||||||
using init_token = std::shared_ptr<void>;
|
using init_token = std::shared_ptr<void>;
|
||||||
|
|
||||||
class Init {
|
class RTC_CPP_EXPORT Init {
|
||||||
public:
|
public:
|
||||||
static init_token Token();
|
static init_token Token();
|
||||||
static void Preload();
|
static void Preload();
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "include.hpp"
|
||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
enum class LogLevel { // Don't change, it must match plog severity
|
enum class LogLevel { // Don't change, it must match plog severity
|
||||||
@ -47,8 +49,8 @@ enum class LogLevel { // Don't change, it must match plog severity
|
|||||||
Verbose = 6
|
Verbose = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitLogger(LogLevel level);
|
RTC_CPP_EXPORT void InitLogger(LogLevel level);
|
||||||
void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
|
RTC_CPP_EXPORT void InitLogger(plog::Severity severity, plog::IAppender *appender = nullptr);
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
struct Message : binary {
|
struct RTC_CPP_EXPORT Message : binary {
|
||||||
enum Type { Binary, String, Control, Reset };
|
enum Type { Binary, String, Control, Reset };
|
||||||
|
|
||||||
Message(const Message &message) = default;
|
Message(const Message &message) = default;
|
||||||
@ -42,7 +42,8 @@ struct Message : binary {
|
|||||||
Message(binary &&data, Type type_ = Binary) : binary(std::move(data)), type(type_) {}
|
Message(binary &&data, Type type_ = Binary) : binary(std::move(data)), type(type_) {}
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
unsigned int stream = 0;
|
unsigned int stream = 0; // Stream id (SCTP stream or SSRC)
|
||||||
|
unsigned int dscp = 0; // Differentiated Services Code Point
|
||||||
std::shared_ptr<Reliability> reliability;
|
std::shared_ptr<Reliability> reliability;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,14 +50,14 @@ class SctpTransport;
|
|||||||
using certificate_ptr = std::shared_ptr<Certificate>;
|
using certificate_ptr = std::shared_ptr<Certificate>;
|
||||||
using future_certificate_ptr = std::shared_future<certificate_ptr>;
|
using future_certificate_ptr = std::shared_future<certificate_ptr>;
|
||||||
|
|
||||||
struct DataChannelInit {
|
struct RTC_CPP_EXPORT DataChannelInit {
|
||||||
Reliability reliability = {};
|
Reliability reliability = {};
|
||||||
bool negotiated = false;
|
bool negotiated = false;
|
||||||
std::optional<uint16_t> id = nullopt;
|
std::optional<uint16_t> id = nullopt;
|
||||||
string protocol = "";
|
string protocol = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
class PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
|
class RTC_CPP_EXPORT PeerConnection final : public std::enable_shared_from_this<PeerConnection> {
|
||||||
public:
|
public:
|
||||||
enum class State : int {
|
enum class State : int {
|
||||||
New = RTC_NEW,
|
New = RTC_NEW,
|
||||||
@ -129,7 +129,7 @@ public:
|
|||||||
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
|
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<IceTransport> initIceTransport(Description::Role role);
|
std::shared_ptr<IceTransport> initIceTransport();
|
||||||
std::shared_ptr<DtlsTransport> initDtlsTransport();
|
std::shared_ptr<DtlsTransport> initDtlsTransport();
|
||||||
std::shared_ptr<SctpTransport> initSctpTransport();
|
std::shared_ptr<SctpTransport> initSctpTransport();
|
||||||
void closeTransports();
|
void closeTransports();
|
||||||
@ -157,6 +157,8 @@ private:
|
|||||||
void processLocalCandidate(Candidate candidate);
|
void processLocalCandidate(Candidate candidate);
|
||||||
void processRemoteDescription(Description description);
|
void processRemoteDescription(Description description);
|
||||||
void processRemoteCandidate(Candidate candidate);
|
void processRemoteCandidate(Candidate candidate);
|
||||||
|
string localBundleMid() const;
|
||||||
|
|
||||||
void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel);
|
void triggerDataChannel(std::weak_ptr<DataChannel> weakDataChannel);
|
||||||
void triggerTrack(std::shared_ptr<Track> track);
|
void triggerTrack(std::shared_ptr<Track> track);
|
||||||
bool changeState(State state);
|
bool changeState(State state);
|
||||||
@ -203,8 +205,8 @@ private:
|
|||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
|
||||||
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::GatheringState state);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::GatheringState state);
|
||||||
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::SignalingState state);
|
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::SignalingState state);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
class RtcpHandler {
|
class RTC_CPP_EXPORT RtcpHandler {
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Use this callback when trying to send custom data (such as RTCP) to the client.
|
* Use this callback when trying to send custom data (such as RTCP) to the client.
|
||||||
@ -64,7 +64,7 @@ public:
|
|||||||
class Track;
|
class Track;
|
||||||
|
|
||||||
// An RtcpSession can be plugged into a Track to handle the whole RTCP session
|
// An RtcpSession can be plugged into a Track to handle the whole RTCP session
|
||||||
class RtcpReceivingSession : public RtcpHandler {
|
class RTC_CPP_EXPORT RtcpReceivingSession : public RtcpHandler {
|
||||||
public:
|
public:
|
||||||
rtc::message_ptr incoming(rtc::message_ptr ptr) override;
|
rtc::message_ptr incoming(rtc::message_ptr ptr) override;
|
||||||
rtc::message_ptr outgoing(rtc::message_ptr ptr) override;
|
rtc::message_ptr outgoing(rtc::message_ptr ptr) override;
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#ifndef RTC_RTP_HPP
|
#ifndef RTC_RTP_HPP
|
||||||
#define RTC_RTP_HPP
|
#define RTC_RTP_HPP
|
||||||
|
|
||||||
#include <rtc/log.hpp>
|
#include "log.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ public:
|
|||||||
|
|
||||||
inline uint8_t version() const { return _first >> 6; }
|
inline uint8_t version() const { return _first >> 6; }
|
||||||
inline bool padding() const { return (_first >> 5) & 0x01; }
|
inline bool padding() const { return (_first >> 5) & 0x01; }
|
||||||
|
inline bool extension() const { return (_first >> 4) & 0x01; }
|
||||||
inline uint8_t csrcCount() const { return _first & 0x0F; }
|
inline uint8_t csrcCount() const { return _first & 0x0F; }
|
||||||
inline uint8_t marker() const { return _payloadType & 0b10000000; }
|
inline uint8_t marker() const { return _payloadType & 0b10000000; }
|
||||||
inline uint8_t payloadType() const { return _payloadType & 0b01111111; }
|
inline uint8_t payloadType() const { return _payloadType & 0b01111111; }
|
||||||
@ -65,18 +66,32 @@ public:
|
|||||||
inline uint32_t ssrc() const { return ntohl(_ssrc); }
|
inline uint32_t ssrc() const { return ntohl(_ssrc); }
|
||||||
|
|
||||||
inline size_t getSize() const {
|
inline size_t getSize() const {
|
||||||
return ((char *)&csrc) - ((char *)this) + sizeof(SSRC) * csrcCount();
|
return reinterpret_cast<const char *>(&csrc) - reinterpret_cast<const char *>(this) +
|
||||||
|
sizeof(SSRC) * csrcCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
char *getBody() const { return ((char *)&csrc) + sizeof(SSRC) * csrcCount(); }
|
[[nodiscard]] char *getBody() {
|
||||||
|
return reinterpret_cast<char *>(&csrc) + sizeof(SSRC) * csrcCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const char *getBody() const {
|
||||||
|
return reinterpret_cast<const char *>(&csrc) + sizeof(SSRC) * csrcCount();
|
||||||
|
}
|
||||||
|
|
||||||
inline void setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); }
|
inline void setSeqNumber(uint16_t newSeqNo) { _seqNumber = htons(newSeqNo); }
|
||||||
inline void setPayloadType(uint8_t newPayloadType) {
|
inline void setPayloadType(uint8_t newPayloadType) {
|
||||||
_payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
|
_payloadType = (_payloadType & 0b10000000u) | (0b01111111u & newPayloadType);
|
||||||
}
|
}
|
||||||
inline void setSsrc(uint32_t ssrc) { _ssrc = htonl(ssrc); }
|
inline void setSsrc(uint32_t in_ssrc) { _ssrc = htonl(in_ssrc); }
|
||||||
|
|
||||||
void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
|
void setTimestamp(uint32_t i) { _timestamp = htonl(i); }
|
||||||
|
|
||||||
|
void log() {
|
||||||
|
PLOG_VERBOSE << "RTP V: " << (int)version() << " P: " << (padding() ? "P" : " ")
|
||||||
|
<< " X: " << (extension() ? "X" : " ") << " CC: " << (int)csrcCount()
|
||||||
|
<< " M: " << (marker() ? "M" : " ") << " PT: " << (int)payloadType()
|
||||||
|
<< " SEQNO: " << seqNumber() << " TS: " << timestamp();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RTCP_ReportBlock {
|
struct RTCP_ReportBlock {
|
||||||
@ -91,13 +106,13 @@ private:
|
|||||||
uint32_t _delaySinceLastReport;
|
uint32_t _delaySinceLastReport;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline void preparePacket(SSRC ssrc, [[maybe_unused]] unsigned int packetsLost,
|
inline void preparePacket(SSRC in_ssrc, [[maybe_unused]] unsigned int packetsLost,
|
||||||
[[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
|
[[maybe_unused]] unsigned int totalPackets, uint16_t highestSeqNo,
|
||||||
uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
|
uint16_t seqNoCycles, uint32_t jitter, uint64_t lastSR_NTP,
|
||||||
uint64_t lastSR_DELAY) {
|
uint64_t lastSR_DELAY) {
|
||||||
setSeqNo(highestSeqNo, seqNoCycles);
|
setSeqNo(highestSeqNo, seqNoCycles);
|
||||||
setJitter(jitter);
|
setJitter(jitter);
|
||||||
setSSRC(ssrc);
|
setSSRC(in_ssrc);
|
||||||
|
|
||||||
// Middle 32 bits of NTP Timestamp
|
// Middle 32 bits of NTP Timestamp
|
||||||
// this->lastReport = lastSR_NTP >> 16u;
|
// this->lastReport = lastSR_NTP >> 16u;
|
||||||
@ -108,19 +123,20 @@ public:
|
|||||||
// this->delaySinceLastReport = lastSR_DELAY;
|
// this->delaySinceLastReport = lastSR_DELAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setSSRC(SSRC ssrc) { this->ssrc = htonl(ssrc); }
|
inline void setSSRC(SSRC in_ssrc) { this->ssrc = htonl(in_ssrc); }
|
||||||
inline SSRC getSSRC() const { return ntohl(ssrc); }
|
[[nodiscard]] inline SSRC getSSRC() const { return ntohl(ssrc); }
|
||||||
|
|
||||||
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
|
inline void setPacketsLost([[maybe_unused]] unsigned int packetsLost,
|
||||||
[[maybe_unused]] unsigned int totalPackets) {
|
[[maybe_unused]] unsigned int totalPackets) {
|
||||||
// TODO Implement loss percentages.
|
// TODO Implement loss percentages.
|
||||||
_fractionLostAndPacketsLost = 0;
|
_fractionLostAndPacketsLost = 0;
|
||||||
}
|
}
|
||||||
inline unsigned int getLossPercentage() const {
|
|
||||||
|
[[nodiscard]] inline unsigned int getLossPercentage() const {
|
||||||
// TODO Implement loss percentages.
|
// TODO Implement loss percentages.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
inline unsigned int getPacketLostCount() const {
|
[[nodiscard]] inline unsigned int getPacketLostCount() const {
|
||||||
// TODO Implement total packets lost.
|
// TODO Implement total packets lost.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -137,13 +153,13 @@ public:
|
|||||||
inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
|
inline void setJitter(uint32_t jitter) { _jitter = htonl(jitter); }
|
||||||
|
|
||||||
inline void setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); }
|
inline void setNTPOfSR(uint64_t ntp) { _lastReport = htonll(ntp >> 16u); }
|
||||||
inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
|
[[nodiscard]] inline uint32_t getNTPOfSR() const { return ntohl(_lastReport) << 16u; }
|
||||||
|
|
||||||
inline void setDelaySinceSR(uint32_t sr) {
|
inline void setDelaySinceSR(uint32_t sr) {
|
||||||
// The delay, expressed in units of 1/65536 seconds
|
// The delay, expressed in units of 1/65536 seconds
|
||||||
_delaySinceLastReport = htonl(sr);
|
_delaySinceLastReport = htonl(sr);
|
||||||
}
|
}
|
||||||
inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); }
|
[[nodiscard]] inline uint32_t getDelaySinceSR() const { return ntohl(_delaySinceLastReport); }
|
||||||
|
|
||||||
inline void log() const {
|
inline void log() const {
|
||||||
PLOG_VERBOSE << "RTCP report block: "
|
PLOG_VERBOSE << "RTCP report block: "
|
||||||
@ -234,8 +250,10 @@ public:
|
|||||||
this->_senderSSRC = htonl(senderSSRC);
|
this->_senderSSRC = htonl(senderSSRC);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
|
[[nodiscard]] inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
|
||||||
inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
|
[[nodiscard]] inline const RTCP_ReportBlock *getReportBlock(int num) const {
|
||||||
|
return &_reportBlocks + num;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline size_t getSize() const {
|
[[nodiscard]] inline size_t getSize() const {
|
||||||
// "length" in packet is one less than the number of 32 bit words in the packet.
|
// "length" in packet is one less than the number of 32 bit words in the packet.
|
||||||
@ -272,8 +290,10 @@ private:
|
|||||||
RTCP_ReportBlock _reportBlocks;
|
RTCP_ReportBlock _reportBlocks;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
|
[[nodiscard]] inline RTCP_ReportBlock *getReportBlock(int num) { return &_reportBlocks + num; }
|
||||||
inline const RTCP_ReportBlock *getReportBlock(int num) const { return &_reportBlocks + num; }
|
[[nodiscard]] inline const RTCP_ReportBlock *getReportBlock(int num) const {
|
||||||
|
return &_reportBlocks + num;
|
||||||
|
}
|
||||||
|
|
||||||
inline SSRC senderSSRC() const { return ntohl(_senderSSRC); }
|
inline SSRC senderSSRC() const { return ntohl(_senderSSRC); }
|
||||||
inline void setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); }
|
inline void setSenderSSRC(SSRC ssrc) { this->_senderSSRC = htonl(ssrc); }
|
||||||
@ -325,7 +345,7 @@ struct RTCP_REMB {
|
|||||||
return sizeof(uint32_t) * (1 + header.header.length());
|
return sizeof(uint32_t) * (1 + header.header.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int bitrate) {
|
void preparePacket(SSRC senderSSRC, unsigned int numSSRC, unsigned int in_bitrate) {
|
||||||
|
|
||||||
// Report Count becomes the format here.
|
// Report Count becomes the format here.
|
||||||
header.header.prepareHeader(206, 15, 0);
|
header.header.prepareHeader(206, 15, 0);
|
||||||
@ -340,21 +360,21 @@ struct RTCP_REMB {
|
|||||||
id[2] = 'M';
|
id[2] = 'M';
|
||||||
id[3] = 'B';
|
id[3] = 'B';
|
||||||
|
|
||||||
setBitrate(numSSRC, bitrate);
|
setBitrate(numSSRC, in_bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBitrate(unsigned int numSSRC, unsigned int bitrate) {
|
void setBitrate(unsigned int numSSRC, unsigned int in_bitrate) {
|
||||||
unsigned int exp = 0;
|
unsigned int exp = 0;
|
||||||
while (bitrate > pow(2, 18) - 1) {
|
while (in_bitrate > pow(2, 18) - 1) {
|
||||||
exp++;
|
exp++;
|
||||||
bitrate /= 2;
|
in_bitrate /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "length" in packet is one less than the number of 32 bit words in the packet.
|
// "length" in packet is one less than the number of 32 bit words in the packet.
|
||||||
header.header.setLength(
|
header.header.setLength(
|
||||||
uint16_t((offsetof(RTCP_REMB, ssrc) / sizeof(uint32_t)) - 1 + numSSRC));
|
uint16_t((offsetof(RTCP_REMB, ssrc) / sizeof(uint32_t)) - 1 + numSSRC));
|
||||||
|
|
||||||
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | bitrate);
|
this->bitrate = htonl((numSSRC << (32u - 8u)) | (exp << (32u - 8u - 6u)) | in_bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSsrc(int iterator, SSRC newSssrc) { ssrc[iterator] = htonl(newSssrc); }
|
void setSsrc(int iterator, SSRC newSssrc) { ssrc[iterator] = htonl(newSssrc); }
|
||||||
@ -416,7 +436,7 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount) {
|
void preparePacket(SSRC ssrc, unsigned int discreteSeqNoCount) {
|
||||||
header.header.prepareHeader(205, 1, 2 + discreteSeqNoCount);
|
header.header.prepareHeader(205, 1, 2 + uint16_t(discreteSeqNoCount));
|
||||||
header.setMediaSourceSSRC(ssrc);
|
header.setMediaSourceSSRC(ssrc);
|
||||||
header.setPacketSenderSSRC(ssrc);
|
header.setPacketSenderSSRC(ssrc);
|
||||||
}
|
}
|
||||||
@ -469,11 +489,15 @@ public:
|
|||||||
return ntohs(*(uint16_t *)(header.getBody()));
|
return ntohs(*(uint16_t *)(header.getBody()));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *getBody() { return header.getBody() + sizeof(uint16_t); }
|
[[nodiscard]] char *getBody() { return header.getBody() + sizeof(uint16_t); }
|
||||||
|
|
||||||
size_t getBodySize(size_t totalSize) { return totalSize - ((char *)getBody() - (char *)this); }
|
[[nodiscard]] const char *getBody() const { return header.getBody() + sizeof(uint16_t); }
|
||||||
|
|
||||||
RTP &getHeader() { return header; }
|
[[nodiscard]] size_t getBodySize(size_t totalSize) {
|
||||||
|
return totalSize - (getBody() - reinterpret_cast<char *>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] RTP &getHeader() { return header; }
|
||||||
|
|
||||||
size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) {
|
size_t normalizePacket(size_t totalSize, SSRC originalSSRC, uint8_t originalPayloadType) {
|
||||||
header.setSeqNumber(getOriginalSeqNo());
|
header.setSeqNumber(getOriginalSeqNo());
|
||||||
|
@ -35,7 +35,7 @@ namespace rtc {
|
|||||||
class DtlsSrtpTransport;
|
class DtlsSrtpTransport;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Track final : public std::enable_shared_from_this<Track>, public Channel {
|
class RTC_CPP_EXPORT Track final : public std::enable_shared_from_this<Track>, public Channel {
|
||||||
public:
|
public:
|
||||||
Track(Description::Media description);
|
Track(Description::Media description);
|
||||||
~Track() = default;
|
~Track() = default;
|
||||||
@ -47,7 +47,7 @@ public:
|
|||||||
|
|
||||||
void close(void) override;
|
void close(void) override;
|
||||||
bool send(message_variant data) override;
|
bool send(message_variant data) override;
|
||||||
bool send(const byte *data, size_t size);
|
bool send(const byte *data, size_t size) override;
|
||||||
|
|
||||||
bool isOpen(void) const override;
|
bool isOpen(void) const override;
|
||||||
bool isClosed(void) const override;
|
bool isClosed(void) const override;
|
||||||
@ -70,8 +70,8 @@ private:
|
|||||||
std::weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;
|
std::weak_ptr<DtlsSrtpTransport> mDtlsSrtpTransport;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool outgoing(message_ptr message);
|
|
||||||
void incoming(message_ptr message);
|
void incoming(message_ptr message);
|
||||||
|
bool outgoing(message_ptr message);
|
||||||
|
|
||||||
Description::Media mMediaDescription;
|
Description::Media mMediaDescription;
|
||||||
std::atomic<bool> mIsClosed = false;
|
std::atomic<bool> mIsClosed = false;
|
||||||
|
@ -38,7 +38,7 @@ class TcpTransport;
|
|||||||
class TlsTransport;
|
class TlsTransport;
|
||||||
class WsTransport;
|
class WsTransport;
|
||||||
|
|
||||||
class WebSocket final : public Channel, public std::enable_shared_from_this<WebSocket> {
|
class RTC_CPP_EXPORT WebSocket final : public Channel, public std::enable_shared_from_this<WebSocket> {
|
||||||
public:
|
public:
|
||||||
enum class State : int {
|
enum class State : int {
|
||||||
Connecting = 0,
|
Connecting = 0,
|
||||||
@ -49,6 +49,7 @@ public:
|
|||||||
|
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
bool disableTlsVerification = false; // if true, don't verify the TLS certificate
|
bool disableTlsVerification = false; // if true, don't verify the TLS certificate
|
||||||
|
std::vector<string> protocols;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocket(std::optional<Configuration> config = nullopt);
|
WebSocket(std::optional<Configuration> config = nullopt);
|
||||||
@ -59,6 +60,7 @@ public:
|
|||||||
void open(const string &url);
|
void open(const string &url);
|
||||||
void close() override;
|
void close() override;
|
||||||
bool send(const message_variant data) override;
|
bool send(const message_variant data) override;
|
||||||
|
bool send(const byte *data, size_t size) override;
|
||||||
|
|
||||||
bool isOpen() const override;
|
bool isOpen() const override;
|
||||||
bool isClosed() const override;
|
bool isClosed() const override;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cctype>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@ -39,39 +40,44 @@ using std::string;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
inline bool hasprefix(const string &str, const string &prefix) {
|
inline bool match_prefix(const string &str, const string &prefix) {
|
||||||
return str.size() >= prefix.size() &&
|
return str.size() >= prefix.size() &&
|
||||||
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
|
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void trim_begin(string &str) {
|
||||||
|
str.erase(str.begin(),
|
||||||
|
std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); }));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void trim_end(string &str) {
|
||||||
|
str.erase(
|
||||||
|
std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(),
|
||||||
|
str.end());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
Candidate::Candidate()
|
Candidate::Candidate()
|
||||||
: mFamily(Family::Unresolved), mType(Type::Unknown), mTransportType(TransportType::Unknown),
|
: mFoundation("none"), mComponent(0), mPriority(0), mTypeString("unknown"),
|
||||||
mPort(0), mPriority(0) {}
|
mTransportString("unknown"), mType(Type::Unknown), mTransportType(TransportType::Unknown),
|
||||||
|
mNode("0.0.0.0"), mService("9"), mFamily(Family::Unresolved), mPort(0) {}
|
||||||
|
|
||||||
Candidate::Candidate(string candidate) : Candidate() {
|
Candidate::Candidate(string candidate) : Candidate() {
|
||||||
const std::array prefixes{"a=", "candidate:"};
|
if (!candidate.empty())
|
||||||
for (const string &prefix : prefixes)
|
parse(std::move(candidate));
|
||||||
if (hasprefix(candidate, prefix))
|
|
||||||
candidate.erase(0, prefix.size());
|
|
||||||
|
|
||||||
mCandidate = std::move(candidate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Candidate::Candidate(string candidate, string mid) : Candidate(std::move(candidate)) {
|
Candidate::Candidate(string candidate, string mid) : Candidate() {
|
||||||
|
if (!candidate.empty())
|
||||||
|
parse(std::move(candidate));
|
||||||
if (!mid.empty())
|
if (!mid.empty())
|
||||||
mMid.emplace(std::move(mid));
|
mMid.emplace(std::move(mid));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Candidate::hintMid(string mid) {
|
void Candidate::parse(string candidate) {
|
||||||
if (!mMid)
|
|
||||||
mMid.emplace(std::move(mid));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Candidate::resolve(ResolveMode mode) {
|
|
||||||
using TypeMap_t = std::unordered_map<string, Type>;
|
using TypeMap_t = std::unordered_map<string, Type>;
|
||||||
using TcpTypeMap_t = std::unordered_map<string, TransportType>;
|
using TcpTypeMap_t = std::unordered_map<string, TransportType>;
|
||||||
|
|
||||||
@ -84,24 +90,23 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
{"passive", TransportType::TcpPassive},
|
{"passive", TransportType::TcpPassive},
|
||||||
{"so", TransportType::TcpSo}};
|
{"so", TransportType::TcpSo}};
|
||||||
|
|
||||||
if (mFamily != Family::Unresolved)
|
const std::array prefixes{"a=", "candidate:"};
|
||||||
return true;
|
for (const string &prefix : prefixes)
|
||||||
|
if (match_prefix(candidate, prefix))
|
||||||
|
candidate.erase(0, prefix.size());
|
||||||
|
|
||||||
if (mCandidate.empty())
|
PLOG_VERBOSE << "Parsing candidate: " << candidate;
|
||||||
throw std::logic_error("Candidate is empty");
|
|
||||||
|
|
||||||
PLOG_VERBOSE << "Resolving candidate (mode="
|
|
||||||
<< (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mCandidate;
|
|
||||||
|
|
||||||
// See RFC 8445 for format
|
// See RFC 8445 for format
|
||||||
std::istringstream iss(mCandidate);
|
std::istringstream iss(candidate);
|
||||||
int component{0}, priority{0};
|
string transport, typ_, type;
|
||||||
string foundation, transport, node, service, typ_, type;
|
if (!(iss >> mFoundation >> mComponent >> mTransportString >> mPriority &&
|
||||||
if (iss >> foundation >> component >> transport >> priority &&
|
iss >> mNode >> mService >> typ_ >> mTypeString && typ_ == "typ"))
|
||||||
iss >> node >> service >> typ_ >> type && typ_ == "typ") {
|
throw std::invalid_argument("Invalid candidate format");
|
||||||
|
|
||||||
string left;
|
std::getline(iss, mTail);
|
||||||
std::getline(iss, left);
|
trim_begin(mTail);
|
||||||
|
trim_end(mTail);
|
||||||
|
|
||||||
if (auto it = TypeMap.find(type); it != TypeMap.end())
|
if (auto it = TypeMap.find(type); it != TypeMap.end())
|
||||||
mType = it->second;
|
mType = it->second;
|
||||||
@ -111,9 +116,10 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
if (transport == "UDP" || transport == "udp") {
|
if (transport == "UDP" || transport == "udp") {
|
||||||
mTransportType = TransportType::Udp;
|
mTransportType = TransportType::Udp;
|
||||||
} else if (transport == "TCP" || transport == "tcp") {
|
} else if (transport == "TCP" || transport == "tcp") {
|
||||||
std::istringstream iss(left);
|
// Peek tail to find TCP type
|
||||||
|
std::istringstream tiss(mTail);
|
||||||
string tcptype_, tcptype;
|
string tcptype_, tcptype;
|
||||||
if (iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
|
if (tiss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
|
||||||
if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
|
if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
|
||||||
mTransportType = it->second;
|
mTransportType = it->second;
|
||||||
else
|
else
|
||||||
@ -125,8 +131,19 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
} else {
|
} else {
|
||||||
mTransportType = TransportType::Unknown;
|
mTransportType = TransportType::Unknown;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to resolve the node
|
void Candidate::hintMid(string mid) {
|
||||||
|
if (!mMid)
|
||||||
|
mMid.emplace(std::move(mid));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Candidate::resolve(ResolveMode mode) {
|
||||||
|
PLOG_VERBOSE << "Resolving candidate (mode="
|
||||||
|
<< (mode == ResolveMode::Simple ? "simple" : "lookup") << "): " << mNode << ' '
|
||||||
|
<< mService;
|
||||||
|
|
||||||
|
// Try to resolve the node and service
|
||||||
struct addrinfo hints = {};
|
struct addrinfo hints = {};
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_flags = AI_ADDRCONFIG;
|
hints.ai_flags = AI_ADDRCONFIG;
|
||||||
@ -142,10 +159,9 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
hints.ai_flags |= AI_NUMERICHOST;
|
hints.ai_flags |= AI_NUMERICHOST;
|
||||||
|
|
||||||
struct addrinfo *result = nullptr;
|
struct addrinfo *result = nullptr;
|
||||||
if (getaddrinfo(node.c_str(), service.c_str(), &hints, &result) == 0) {
|
if (getaddrinfo(mNode.c_str(), mService.c_str(), &hints, &result) == 0) {
|
||||||
for (auto p = result; p; p = p->ai_next) {
|
for (auto p = result; p; p = p->ai_next) {
|
||||||
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
|
if (p->ai_family == AF_INET || p->ai_family == AF_INET6) {
|
||||||
// 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, socklen_t(p->ai_addrlen), nodebuffer,
|
if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
|
||||||
@ -155,15 +171,7 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
mAddress = nodebuffer;
|
mAddress = nodebuffer;
|
||||||
mPort = uint16_t(std::stoul(servbuffer));
|
mPort = uint16_t(std::stoul(servbuffer));
|
||||||
mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
|
mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
|
||||||
|
PLOG_VERBOSE << "Resolved candidate: " << mAddress << ' ' << mPort;
|
||||||
const char sp{' '};
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << foundation << sp << component << sp << transport << sp << priority;
|
|
||||||
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
|
|
||||||
oss << left;
|
|
||||||
mCandidate = oss.str();
|
|
||||||
|
|
||||||
PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,12 +179,33 @@ bool Candidate::resolve(ResolveMode mode) {
|
|||||||
|
|
||||||
freeaddrinfo(result);
|
freeaddrinfo(result);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return mFamily != Family::Unresolved;
|
return mFamily != Family::Unresolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
string Candidate::candidate() const { return "candidate:" + mCandidate; }
|
Candidate::Type Candidate::type() const { return mType; }
|
||||||
|
|
||||||
|
Candidate::TransportType Candidate::transportType() const { return mTransportType; }
|
||||||
|
|
||||||
|
uint32_t Candidate::priority() const { return mPriority; }
|
||||||
|
|
||||||
|
string Candidate::candidate() const {
|
||||||
|
const char sp{' '};
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "candidate:";
|
||||||
|
oss << mFoundation << sp << mComponent << sp << mTransportString << sp << mPriority << sp;
|
||||||
|
if (isResolved())
|
||||||
|
oss << mAddress << sp << mPort;
|
||||||
|
else
|
||||||
|
oss << mNode << sp << mService;
|
||||||
|
|
||||||
|
oss << sp << "typ" << sp << mTypeString;
|
||||||
|
|
||||||
|
if (!mTail.empty())
|
||||||
|
oss << sp << mTail;
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
string Candidate::mid() const { return mMid.value_or("0"); }
|
string Candidate::mid() const { return mMid.value_or("0"); }
|
||||||
|
|
||||||
@ -186,14 +215,18 @@ Candidate::operator string() const {
|
|||||||
return line.str();
|
return line.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Candidate::operator==(const Candidate &other) const {
|
||||||
|
return mFoundation == other.mFoundation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Candidate::operator!=(const Candidate &other) const {
|
||||||
|
return mFoundation != other.mFoundation;
|
||||||
|
}
|
||||||
|
|
||||||
bool Candidate::isResolved() const { return mFamily != Family::Unresolved; }
|
bool Candidate::isResolved() const { return mFamily != Family::Unresolved; }
|
||||||
|
|
||||||
Candidate::Family Candidate::family() const { return mFamily; }
|
Candidate::Family Candidate::family() const { return mFamily; }
|
||||||
|
|
||||||
Candidate::Type Candidate::type() const { return mType; }
|
|
||||||
|
|
||||||
Candidate::TransportType Candidate::transportType() const { return mTransportType; }
|
|
||||||
|
|
||||||
std::optional<string> Candidate::address() const {
|
std::optional<string> Candidate::address() const {
|
||||||
return isResolved() ? std::make_optional(mAddress) : nullopt;
|
return isResolved() ? std::make_optional(mAddress) : nullopt;
|
||||||
}
|
}
|
||||||
@ -202,10 +235,6 @@ std::optional<uint16_t> Candidate::port() const {
|
|||||||
return isResolved() ? std::make_optional(mPort) : nullopt;
|
return isResolved() ? std::make_optional(mPort) : nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<uint32_t> Candidate::priority() const {
|
|
||||||
return isResolved() ? std::make_optional(mPriority) : nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate) {
|
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate) {
|
||||||
@ -217,11 +246,11 @@ std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type) {
|
|||||||
case rtc::Candidate::Type::Host:
|
case rtc::Candidate::Type::Host:
|
||||||
return out << "host";
|
return out << "host";
|
||||||
case rtc::Candidate::Type::PeerReflexive:
|
case rtc::Candidate::Type::PeerReflexive:
|
||||||
return out << "peer_reflexive";
|
return out << "prflx";
|
||||||
case rtc::Candidate::Type::ServerReflexive:
|
case rtc::Candidate::Type::ServerReflexive:
|
||||||
return out << "server_reflexive";
|
return out << "srflx";
|
||||||
case rtc::Candidate::Type::Relayed:
|
case rtc::Candidate::Type::Relayed:
|
||||||
return out << "relayed";
|
return out << "relay";
|
||||||
default:
|
default:
|
||||||
return out << "unknown";
|
return out << "unknown";
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ void NegociatedDataChannel::processOpenMessage(message_ptr message) {
|
|||||||
mLabel.assign(end, open.labelLength);
|
mLabel.assign(end, open.labelLength);
|
||||||
mProtocol.assign(end + open.labelLength, open.protocolLength);
|
mProtocol.assign(end + open.labelLength, open.protocolLength);
|
||||||
|
|
||||||
mReliability->unordered = (open.reliabilityParameter & 0x80) != 0;
|
mReliability->unordered = (open.channelType & 0x80) != 0;
|
||||||
switch (open.channelType & 0x7F) {
|
switch (open.channelType & 0x7F) {
|
||||||
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
|
case CHANNEL_PARTIAL_RELIABLE_REXMIT:
|
||||||
mReliability->type = Reliability::Type::Rexmit;
|
mReliability->type = Reliability::Type::Rexmit;
|
||||||
|
@ -29,13 +29,13 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
using std::size_t;
|
|
||||||
using std::string;
|
|
||||||
using std::string_view;
|
|
||||||
using std::chrono::system_clock;
|
using std::chrono::system_clock;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::string_view;
|
||||||
|
|
||||||
inline bool match_prefix(string_view str, string_view prefix) {
|
inline bool match_prefix(string_view str, string_view prefix) {
|
||||||
return str.size() >= prefix.size() &&
|
return str.size() >= prefix.size() &&
|
||||||
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
|
std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end();
|
||||||
@ -106,7 +106,7 @@ Description::Description(const string &sdp, Type type, Role role)
|
|||||||
mFingerprint->begin(),
|
mFingerprint->begin(),
|
||||||
[](char c) { return char(std::toupper(c)); });
|
[](char c) { return char(std::toupper(c)); });
|
||||||
} else {
|
} else {
|
||||||
PLOG_WARNING << "Unknown SDP fingerprint type: " << value;
|
PLOG_WARNING << "Unknown SDP fingerprint format: " << value;
|
||||||
}
|
}
|
||||||
} else if (key == "ice-ufrag") {
|
} else if (key == "ice-ufrag") {
|
||||||
mIceUfrag = value;
|
mIceUfrag = value;
|
||||||
@ -171,16 +171,27 @@ void Description::setFingerprint(string fingerprint) {
|
|||||||
mFingerprint.emplace(std::move(fingerprint));
|
mFingerprint.emplace(std::move(fingerprint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Description::hasCandidate(const Candidate &candidate) const {
|
||||||
|
for (const Candidate &other : mCandidates)
|
||||||
|
if (candidate == other)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Description::addCandidate(Candidate candidate) {
|
void Description::addCandidate(Candidate candidate) {
|
||||||
candidate.hintMid(bundleMid());
|
candidate.hintMid(bundleMid());
|
||||||
|
|
||||||
|
for (const Candidate &other : mCandidates)
|
||||||
|
if (candidate == other)
|
||||||
|
return;
|
||||||
|
|
||||||
mCandidates.emplace_back(std::move(candidate));
|
mCandidates.emplace_back(std::move(candidate));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::addCandidates(std::vector<Candidate> candidates) {
|
void Description::addCandidates(std::vector<Candidate> candidates) {
|
||||||
for (Candidate candidate : candidates) {
|
for (Candidate candidate : candidates)
|
||||||
candidate.hintMid(bundleMid());
|
addCandidate(std::move(candidate));
|
||||||
mCandidates.emplace_back(std::move(candidate));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::endCandidates() { mEnded = true; }
|
void Description::endCandidates() { mEnded = true; }
|
||||||
@ -472,8 +483,7 @@ string Description::Entry::generateSdpLines(string_view eol) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &attr : mAttributes) {
|
for (const auto &attr : mAttributes) {
|
||||||
if (attr.find("extmap") == std::string::npos &&
|
if (attr.find("extmap") == string::npos && attr.find("rtcp-rsize") == string::npos)
|
||||||
attr.find("rtcp-rsize") == std::string::npos)
|
|
||||||
sdp << "a=" << attr << eol;
|
sdp << "a=" << attr << eol;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,12 +518,21 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
|
|||||||
return mAttributes.erase(it);
|
return mAttributes.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Media::addSSRC(uint32_t ssrc, std::string name) {
|
void Description::Media::addSSRC(uint32_t ssrc, std::optional<string> name,
|
||||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name);
|
std::optional<string> msid) {
|
||||||
|
if (name)
|
||||||
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
|
||||||
|
else
|
||||||
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
|
||||||
|
|
||||||
|
if (msid)
|
||||||
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " + *msid);
|
||||||
|
|
||||||
mSsrcs.emplace_back(ssrc);
|
mSsrcs.emplace_back(ssrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::string name) {
|
void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::optional<string> name,
|
||||||
|
std::optional<string> msid) {
|
||||||
auto it = mAttributes.begin();
|
auto it = mAttributes.begin();
|
||||||
while (it != mAttributes.end()) {
|
while (it != mAttributes.end()) {
|
||||||
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
||||||
@ -521,11 +540,17 @@ void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, std::strin
|
|||||||
} else
|
} else
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + name);
|
addSSRC(ssrc, std::move(name), std::move(msid));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Media::addSSRC(uint32_t ssrc) {
|
void Description::Media::removeSSRC(uint32_t oldSSRC) {
|
||||||
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc));
|
auto it = mAttributes.begin();
|
||||||
|
while (it != mAttributes.end()) {
|
||||||
|
if (it->find("ssrc:" + std::to_string(oldSSRC)) == 0) {
|
||||||
|
it = mAttributes.erase(it);
|
||||||
|
} else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Description::Media::hasSSRC(uint32_t ssrc) {
|
bool Description::Media::hasSSRC(uint32_t ssrc) {
|
||||||
@ -678,32 +703,17 @@ void Description::Media::removeFormat(const string &fmt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Video::addVideoCodec(int payloadType, const string &codec) {
|
void Description::Video::addVideoCodec(int payloadType, string codec,
|
||||||
|
std::optional<string> profile) {
|
||||||
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
|
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
|
||||||
map.addFB("nack");
|
map.addFB("nack");
|
||||||
map.addFB("nack pli");
|
map.addFB("nack pli");
|
||||||
// map.addFB("nack fir");
|
// map.addFB("ccm fir");
|
||||||
map.addFB("goog-remb");
|
map.addFB("goog-remb");
|
||||||
if (codec == "H264") {
|
if (profile)
|
||||||
// Use Constrained Baseline profile Level 4.2 (necessary for Firefox)
|
map.fmtps.emplace_back(*profile);
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs#Supported_video_codecs
|
|
||||||
// TODO: Should be 42E0 but 42C0 appears to be more compatible. Investigate this.
|
|
||||||
map.fmtps.emplace_back(
|
|
||||||
"profile-level-id=4de01f;packetization-mode=1;level-asymmetry-allowed=1");
|
|
||||||
|
|
||||||
// Because certain Android devices don't like me, let us just negotiate some random
|
|
||||||
{
|
|
||||||
RTPMap map(std::to_string(payloadType + 1) + ' ' + codec + "/90000");
|
|
||||||
map.addFB("nack");
|
|
||||||
map.addFB("nack pli");
|
|
||||||
// map.addFB("nack fir");
|
|
||||||
map.addFB("goog-remb");
|
|
||||||
addRTPMap(map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addRTPMap(map);
|
addRTPMap(map);
|
||||||
|
|
||||||
// // RTX Packets
|
|
||||||
/* TODO
|
/* TODO
|
||||||
* TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't
|
* TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't
|
||||||
* negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we
|
* negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we
|
||||||
@ -711,24 +721,34 @@ void Description::Video::addVideoCodec(int payloadType, const string &codec) {
|
|||||||
* rebuild the original packet before we send it distribute it to each track. Three) we complain
|
* rebuild the original packet before we send it distribute it to each track. Three) we complain
|
||||||
* to mozilla. This one probably won't do much.
|
* to mozilla. This one probably won't do much.
|
||||||
*/
|
*/
|
||||||
|
// RTX Packets
|
||||||
// RTPMap rtx(std::to_string(payloadType+1) + " rtx/90000");
|
// RTPMap rtx(std::to_string(payloadType+1) + " rtx/90000");
|
||||||
// // TODO rtx-time is how long can a request be stashed for before needing to resend it.
|
// // TODO rtx-time is how long can a request be stashed for before needing to resend it.
|
||||||
// Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) +
|
// Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) +
|
||||||
// ";rtx-time=3000"); addRTPMap(rtx);
|
// ";rtx-time=3000"); addRTPMap(rtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Audio::addAudioCodec(int payloadType, const string &codec) {
|
void Description::Audio::addAudioCodec(int payloadType, string codec,
|
||||||
|
std::optional<string> profile) {
|
||||||
// TODO This 48000/2 should be parameterized
|
// TODO This 48000/2 should be parameterized
|
||||||
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
|
RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
|
||||||
map.fmtps.emplace_back("maxaveragebitrate=96000; stereo=1; sprop-stereo=1; useinbandfec=1");
|
if (profile)
|
||||||
|
map.fmtps.emplace_back(*profile);
|
||||||
|
addRTPMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Description::Media::addRTXCodec(unsigned int payloadType, unsigned int originalPayloadType,
|
||||||
|
unsigned int clockRate) {
|
||||||
|
RTPMap map(std::to_string(payloadType) + " RTX/" + std::to_string(clockRate));
|
||||||
|
map.fmtps.emplace_back("apt=" + std::to_string(originalPayloadType));
|
||||||
addRTPMap(map);
|
addRTPMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Description::Video::addH264Codec(int pt) { addVideoCodec(pt, "H264"); }
|
void Description::Video::addH264Codec(int pt) { addVideoCodec(pt, "H264"); }
|
||||||
|
|
||||||
void Description::Video::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8"); }
|
void Description::Video::addVP8Codec(int payloadType) { addVideoCodec(payloadType, "VP8", nullopt); }
|
||||||
|
|
||||||
void Description::Video::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9"); }
|
void Description::Video::addVP9Codec(int payloadType) { addVideoCodec(payloadType, "VP9", nullopt); }
|
||||||
|
|
||||||
void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
|
void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
|
||||||
|
|
||||||
@ -797,7 +817,7 @@ void Description::Media::parseSdpLine(string_view line) {
|
|||||||
} else if (key == "rtcp-mux") {
|
} else if (key == "rtcp-mux") {
|
||||||
// always added
|
// always added
|
||||||
} else if (key == "ssrc") {
|
} else if (key == "ssrc") {
|
||||||
mSsrcs.emplace_back(std::stoul((std::string)value));
|
mSsrcs.emplace_back(std::stoul(string(value)));
|
||||||
} else {
|
} else {
|
||||||
Entry::parseSdpLine(line);
|
Entry::parseSdpLine(line);
|
||||||
}
|
}
|
||||||
@ -817,7 +837,7 @@ std::vector<uint32_t> Description::Media::getSSRCs() {
|
|||||||
for (auto &val : mAttributes) {
|
for (auto &val : mAttributes) {
|
||||||
PLOG_DEBUG << val;
|
PLOG_DEBUG << val;
|
||||||
if (val.find("ssrc:") == 0) {
|
if (val.find("ssrc:") == 0) {
|
||||||
vec.emplace_back(std::stoul((std::string)val.substr(5, val.find(" "))));
|
vec.emplace_back(std::stoul(string(val.substr(5, val.find(" ")))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vec;
|
return vec;
|
||||||
@ -841,7 +861,7 @@ Description::Media::RTPMap::RTPMap(string_view mline) { setMLine(mline); }
|
|||||||
void Description::Media::RTPMap::removeFB(const string &str) {
|
void Description::Media::RTPMap::removeFB(const string &str) {
|
||||||
auto it = rtcpFbs.begin();
|
auto it = rtcpFbs.begin();
|
||||||
while (it != rtcpFbs.end()) {
|
while (it != rtcpFbs.end()) {
|
||||||
if (it->find(str) != std::string::npos) {
|
if (it->find(str) != string::npos) {
|
||||||
it = rtcpFbs.erase(it);
|
it = rtcpFbs.erase(it);
|
||||||
} else
|
} else
|
||||||
it++;
|
it++;
|
||||||
|
@ -110,7 +110,7 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
|
|||||||
if (err == srtp_err_status_replay_fail)
|
if (err == srtp_err_status_replay_fail)
|
||||||
throw std::runtime_error("SRTCP packet is a replay");
|
throw std::runtime_error("SRTCP packet is a replay");
|
||||||
else if (err == srtp_err_status_no_ctx) {
|
else if (err == srtp_err_status_no_ctx) {
|
||||||
auto ssrc = ((RTCP_SR *)message->data())->senderSSRC();
|
auto ssrc = reinterpret_cast<RTCP_SR *>(message->data())->senderSSRC();
|
||||||
PLOG_INFO << "Adding SSRC to SRTCP: " << ssrc;
|
PLOG_INFO << "Adding SSRC to SRTCP: " << ssrc;
|
||||||
addSSRC(ssrc);
|
addSSRC(ssrc);
|
||||||
if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)))
|
if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)))
|
||||||
@ -127,11 +127,11 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
|
|||||||
if (err == srtp_err_status_replay_fail)
|
if (err == srtp_err_status_replay_fail)
|
||||||
throw std::runtime_error("Outgoing SRTP packet is a replay");
|
throw std::runtime_error("Outgoing SRTP packet is a replay");
|
||||||
else if (err == srtp_err_status_no_ctx) {
|
else if (err == srtp_err_status_no_ctx) {
|
||||||
auto ssrc = ((RTP *)message->data())->ssrc();
|
auto ssrc = reinterpret_cast<RTP *>(message->data())->ssrc();
|
||||||
PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
|
PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
|
||||||
addSSRC(ssrc);
|
addSSRC(ssrc);
|
||||||
if ((err = srtp_protect_rtcp(mSrtpOut, message->data(), &size)))
|
if ((err = srtp_protect(mSrtpOut, message->data(), &size)))
|
||||||
throw std::runtime_error("SRTCP protect error, status=" +
|
throw std::runtime_error("SRTP protect error, status=" +
|
||||||
to_string(static_cast<int>(err)));
|
to_string(static_cast<int>(err)));
|
||||||
} else
|
} else
|
||||||
throw std::runtime_error("SRTP protect error, status=" +
|
throw std::runtime_error("SRTP protect error, status=" +
|
||||||
@ -141,7 +141,14 @@ bool DtlsSrtpTransport::sendMedia(message_ptr message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message->resize(size);
|
message->resize(size);
|
||||||
return outgoing(message);
|
|
||||||
|
if (message->dscp == 0) { // Track might override the value
|
||||||
|
// Set recommended medium-priority DSCP value
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
|
||||||
|
message->dscp = 36; // AF42: Assured Forwarding class 4, medium drop probability
|
||||||
|
}
|
||||||
|
|
||||||
|
return Transport::outgoing(message); // bypass DTLS DSCP marking
|
||||||
}
|
}
|
||||||
|
|
||||||
void DtlsSrtpTransport::incoming(message_ptr message) {
|
void DtlsSrtpTransport::incoming(message_ptr message) {
|
||||||
@ -189,7 +196,7 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
|
|||||||
else if (err == srtp_err_status_auth_fail)
|
else if (err == srtp_err_status_auth_fail)
|
||||||
PLOG_WARNING << "Incoming SRTCP packet failed authentication check";
|
PLOG_WARNING << "Incoming SRTCP packet failed authentication check";
|
||||||
else if (err == srtp_err_status_no_ctx) {
|
else if (err == srtp_err_status_no_ctx) {
|
||||||
auto ssrc = ((RTCP_SR *)message->data())->senderSSRC();
|
auto ssrc = reinterpret_cast<RTCP_SR *>(message->data())->senderSSRC();
|
||||||
PLOG_INFO << "Adding SSRC to RTCP: " << ssrc;
|
PLOG_INFO << "Adding SSRC to RTCP: " << ssrc;
|
||||||
addSSRC(ssrc);
|
addSSRC(ssrc);
|
||||||
if ((err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size)))
|
if ((err = srtp_unprotect_rtcp(mSrtpIn, message->data(), &size)))
|
||||||
@ -203,8 +210,7 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
|
|||||||
}
|
}
|
||||||
PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
|
PLOG_VERBOSE << "Unprotected SRTCP packet, size=" << size;
|
||||||
message->type = Message::Type::Control;
|
message->type = Message::Type::Control;
|
||||||
auto rtp = (RTCP_SR *)message->data();
|
message->stream = reinterpret_cast<RTCP_SR *>(message->data())->senderSSRC();
|
||||||
message->stream = rtp->senderSSRC();
|
|
||||||
} else {
|
} else {
|
||||||
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
|
PLOG_VERBOSE << "Incoming SRTP packet, size=" << size;
|
||||||
if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
|
if (srtp_err_status_t err = srtp_unprotect(mSrtpIn, message->data(), &size)) {
|
||||||
@ -213,21 +219,20 @@ void DtlsSrtpTransport::incoming(message_ptr message) {
|
|||||||
else if (err == srtp_err_status_auth_fail)
|
else if (err == srtp_err_status_auth_fail)
|
||||||
PLOG_WARNING << "Incoming SRTP packet failed authentication check";
|
PLOG_WARNING << "Incoming SRTP packet failed authentication check";
|
||||||
else if (err == srtp_err_status_no_ctx) {
|
else if (err == srtp_err_status_no_ctx) {
|
||||||
auto ssrc = ((RTP *)message->data())->ssrc();
|
auto ssrc = reinterpret_cast<RTP *>(message->data())->ssrc();
|
||||||
PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
|
PLOG_INFO << "Adding SSRC to RTP: " << ssrc;
|
||||||
addSSRC(ssrc);
|
addSSRC(ssrc);
|
||||||
if ((err = srtp_unprotect(mSrtpIn, message->data(), &size)))
|
if ((err = srtp_unprotect(mSrtpIn, message->data(), &size)))
|
||||||
throw std::runtime_error("SRTCP unprotect error, status=" +
|
throw std::runtime_error("SRTP unprotect error, status=" +
|
||||||
to_string(static_cast<int>(err)));
|
to_string(static_cast<int>(err)));
|
||||||
} else
|
} else
|
||||||
PLOG_WARNING << "SRTP unprotect error, status=" << err
|
PLOG_WARNING << "SRTP unprotect error, status=" << err
|
||||||
<< " SSRC=" << ((RTP *)message->data())->ssrc();
|
<< " SSRC=" << reinterpret_cast<RTP *>(message->data())->ssrc();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
|
PLOG_VERBOSE << "Unprotected SRTP packet, size=" << size;
|
||||||
message->type = Message::Type::Binary;
|
message->type = Message::Type::Binary;
|
||||||
auto rtp = (RTP *)message->data();
|
message->stream = reinterpret_cast<RTP *>(message->data())->ssrc();
|
||||||
message->stream = rtp->ssrc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message->resize(size);
|
message->resize(size);
|
||||||
|
@ -53,7 +53,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, certificate_ptr cer
|
|||||||
verifier_callback verifierCallback, state_callback stateChangeCallback)
|
verifier_callback verifierCallback, state_callback stateChangeCallback)
|
||||||
: Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
|
: Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
|
||||||
mVerifierCallback(std::move(verifierCallback)),
|
mVerifierCallback(std::move(verifierCallback)),
|
||||||
mIsClient(lower->role() == Description::Role::Active) {
|
mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
|
||||||
|
|
||||||
PLOG_DEBUG << "Initializing DTLS transport (GnuTLS)";
|
PLOG_DEBUG << "Initializing DTLS transport (GnuTLS)";
|
||||||
|
|
||||||
@ -122,6 +122,7 @@ bool DtlsTransport::send(message_ptr message) {
|
|||||||
|
|
||||||
PLOG_VERBOSE << "Send size=" << message->size();
|
PLOG_VERBOSE << "Send size=" << message->size();
|
||||||
|
|
||||||
|
mCurrentDscp = message->dscp;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
do {
|
do {
|
||||||
ret = gnutls_record_send(mSession, message->data(), message->size());
|
ret = gnutls_record_send(mSession, message->data(), message->size());
|
||||||
@ -143,6 +144,13 @@ void DtlsTransport::incoming(message_ptr message) {
|
|||||||
mIncomingQueue.push(message);
|
mIncomingQueue.push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DtlsTransport::outgoing(message_ptr message) {
|
||||||
|
if (message->dscp == 0)
|
||||||
|
message->dscp = mCurrentDscp;
|
||||||
|
|
||||||
|
return Transport::outgoing(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
void DtlsTransport::postHandshake() {
|
void DtlsTransport::postHandshake() {
|
||||||
// Dummy
|
// Dummy
|
||||||
}
|
}
|
||||||
@ -309,7 +317,7 @@ DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certific
|
|||||||
verifier_callback verifierCallback, state_callback stateChangeCallback)
|
verifier_callback verifierCallback, state_callback stateChangeCallback)
|
||||||
: Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
|
: Transport(lower, std::move(stateChangeCallback)), mCertificate(certificate),
|
||||||
mVerifierCallback(std::move(verifierCallback)),
|
mVerifierCallback(std::move(verifierCallback)),
|
||||||
mIsClient(lower->role() == Description::Role::Active) {
|
mIsClient(lower->role() == Description::Role::Active), mCurrentDscp(0) {
|
||||||
PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
|
PLOG_DEBUG << "Initializing DTLS transport (OpenSSL)";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -405,6 +413,7 @@ bool DtlsTransport::send(message_ptr message) {
|
|||||||
|
|
||||||
PLOG_VERBOSE << "Send size=" << message->size();
|
PLOG_VERBOSE << "Send size=" << message->size();
|
||||||
|
|
||||||
|
mCurrentDscp = message->dscp;
|
||||||
int ret = SSL_write(mSsl, message->data(), int(message->size()));
|
int ret = SSL_write(mSsl, message->data(), int(message->size()));
|
||||||
return openssl::check(mSsl, ret);
|
return openssl::check(mSsl, ret);
|
||||||
}
|
}
|
||||||
@ -419,6 +428,13 @@ void DtlsTransport::incoming(message_ptr message) {
|
|||||||
mIncomingQueue.push(message);
|
mIncomingQueue.push(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DtlsTransport::outgoing(message_ptr message) {
|
||||||
|
if (message->dscp == 0)
|
||||||
|
message->dscp = mCurrentDscp;
|
||||||
|
|
||||||
|
return Transport::outgoing(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
void DtlsTransport::postHandshake() {
|
void DtlsTransport::postHandshake() {
|
||||||
// Dummy
|
// Dummy
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void incoming(message_ptr message) override;
|
virtual void incoming(message_ptr message) override;
|
||||||
|
virtual bool outgoing(message_ptr message) override;
|
||||||
virtual void postHandshake();
|
virtual void postHandshake();
|
||||||
void runRecvLoop();
|
void runRecvLoop();
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ protected:
|
|||||||
|
|
||||||
Queue<message_ptr> mIncomingQueue;
|
Queue<message_ptr> mIncomingQueue;
|
||||||
std::thread mRecvThread;
|
std::thread mRecvThread;
|
||||||
|
std::atomic<unsigned int> mCurrentDscp;
|
||||||
|
|
||||||
#if USE_GNUTLS
|
#if USE_GNUTLS
|
||||||
gnutls_session_t mSession;
|
gnutls_session_t mSession;
|
||||||
|
@ -46,11 +46,12 @@ using std::chrono::system_clock;
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
|
||||||
candidate_callback candidateCallback, state_callback stateChangeCallback,
|
state_callback stateChangeCallback,
|
||||||
gathering_state_callback gatheringStateChangeCallback)
|
gathering_state_callback gatheringStateChangeCallback)
|
||||||
: Transport(nullptr, std::move(stateChangeCallback)), mRole(role), mMid("0"),
|
: Transport(nullptr, std::move(stateChangeCallback)), mRole(Description::Role::ActPass),
|
||||||
mGatheringState(GatheringState::New), mCandidateCallback(std::move(candidateCallback)),
|
mMid("0"), mGatheringState(GatheringState::New),
|
||||||
|
mCandidateCallback(std::move(candidateCallback)),
|
||||||
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
||||||
mAgent(nullptr, nullptr) {
|
mAgent(nullptr, nullptr) {
|
||||||
|
|
||||||
@ -139,13 +140,19 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
|
|||||||
if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
|
if (juice_get_local_description(mAgent.get(), sdp, JUICE_MAX_SDP_STRING_LEN) < 0)
|
||||||
throw std::runtime_error("Failed to generate local SDP");
|
throw std::runtime_error("Failed to generate local SDP");
|
||||||
|
|
||||||
|
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||||
|
// setup:actpass.
|
||||||
|
// See https://tools.ietf.org/html/rfc5763#section-5
|
||||||
return Description(string(sdp), type,
|
return Description(string(sdp), type,
|
||||||
type == Description::Type::Offer ? Description::Role::ActPass : mRole);
|
type == Description::Type::Offer ? Description::Role::ActPass : mRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::setRemoteDescription(const Description &description) {
|
void IceTransport::setRemoteDescription(const Description &description) {
|
||||||
|
if (mRole == Description::Role::ActPass)
|
||||||
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
||||||
: Description::Role::Active;
|
: Description::Role::Active;
|
||||||
|
if (mRole == description.role())
|
||||||
|
throw std::logic_error("Incompatible roles with remote description");
|
||||||
|
|
||||||
mMid = description.bundleMid();
|
mMid = description.bundleMid();
|
||||||
if (juice_set_remote_description(mAgent.get(),
|
if (juice_set_remote_description(mAgent.get(),
|
||||||
@ -161,7 +168,9 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
|
|||||||
return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0;
|
return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::gatherLocalCandidates() {
|
void IceTransport::gatherLocalCandidates(string mid) {
|
||||||
|
mMid = std::move(mid);
|
||||||
|
|
||||||
// Change state now as candidates calls can be synchronous
|
// Change state now as candidates calls can be synchronous
|
||||||
changeGatheringState(GatheringState::InProgress);
|
changeGatheringState(GatheringState::InProgress);
|
||||||
|
|
||||||
@ -215,8 +224,10 @@ bool IceTransport::send(message_ptr message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IceTransport::outgoing(message_ptr message) {
|
bool IceTransport::outgoing(message_ptr message) {
|
||||||
return juice_send(mAgent.get(), reinterpret_cast<const char *>(message->data()),
|
// Explicit Congestion Notification takes the least-significant 2 bits of the DS field
|
||||||
message->size()) >= 0;
|
int ds = int(message->dscp << 2);
|
||||||
|
return juice_send_diffserv(mAgent.get(), reinterpret_cast<const char *>(message->data()),
|
||||||
|
message->size(), ds) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::changeGatheringState(GatheringState state) {
|
void IceTransport::changeGatheringState(GatheringState state) {
|
||||||
@ -316,13 +327,14 @@ void IceTransport::LogCallback(juice_log_level_t level, const char *message) {
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
IceTransport::IceTransport(const Configuration &config, Description::Role role,
|
IceTransport::IceTransport(const Configuration &config, candidate_callback candidateCallback,
|
||||||
candidate_callback candidateCallback, state_callback stateChangeCallback,
|
state_callback stateChangeCallback,
|
||||||
gathering_state_callback gatheringStateChangeCallback)
|
gathering_state_callback gatheringStateChangeCallback)
|
||||||
: Transport(nullptr, std::move(stateChangeCallback)), mRole(role), mMid("0"),
|
: Transport(nullptr, std::move(stateChangeCallback)), mRole(Description::Role::ActPass),
|
||||||
mGatheringState(GatheringState::New), mCandidateCallback(std::move(candidateCallback)),
|
mMid("0"), mGatheringState(GatheringState::New),
|
||||||
|
mCandidateCallback(std::move(candidateCallback)),
|
||||||
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
mGatheringStateChangeCallback(std::move(gatheringStateChangeCallback)),
|
||||||
mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr) {
|
mNiceAgent(nullptr, nullptr), mMainLoop(nullptr, nullptr), mOutgoingDscp(0) {
|
||||||
|
|
||||||
PLOG_DEBUG << "Initializing ICE transport (libnice)";
|
PLOG_DEBUG << "Initializing ICE transport (libnice)";
|
||||||
|
|
||||||
@ -526,12 +538,21 @@ Description IceTransport::getLocalDescription(Description::Type type) const {
|
|||||||
|
|
||||||
std::unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
|
std::unique_ptr<gchar[], void (*)(void *)> sdp(nice_agent_generate_local_sdp(mNiceAgent.get()),
|
||||||
g_free);
|
g_free);
|
||||||
return Description(string(sdp.get()), type, mRole);
|
|
||||||
|
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||||
|
// setup:actpass.
|
||||||
|
// See https://tools.ietf.org/html/rfc5763#section-5
|
||||||
|
return Description(string(sdp.get()), type,
|
||||||
|
type == Description::Type::Offer ? Description::Role::ActPass : mRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::setRemoteDescription(const Description &description) {
|
void IceTransport::setRemoteDescription(const Description &description) {
|
||||||
|
if (mRole == Description::Role::ActPass)
|
||||||
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
mRole = description.role() == Description::Role::Active ? Description::Role::Passive
|
||||||
: Description::Role::Active;
|
: Description::Role::Active;
|
||||||
|
if (mRole == description.role())
|
||||||
|
throw std::logic_error("Incompatible roles with remote description");
|
||||||
|
|
||||||
mMid = description.bundleMid();
|
mMid = description.bundleMid();
|
||||||
mTrickleTimeout = !description.ended() ? 30s : 0s;
|
mTrickleTimeout = !description.ended() ? 30s : 0s;
|
||||||
|
|
||||||
@ -547,12 +568,14 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Warning: the candidate string must start with "a=candidate:" and it must not end with a
|
// Warning: the candidate string must start with "a=candidate:" and it must not end with a
|
||||||
// newline, else libnice will reject it.
|
// newline or whitespace, else libnice will reject it.
|
||||||
string sdp(candidate);
|
string sdp(candidate);
|
||||||
NiceCandidate *cand =
|
NiceCandidate *cand =
|
||||||
nice_agent_parse_remote_candidate_sdp(mNiceAgent.get(), mStreamId, sdp.c_str());
|
nice_agent_parse_remote_candidate_sdp(mNiceAgent.get(), mStreamId, sdp.c_str());
|
||||||
if (!cand)
|
if (!cand) {
|
||||||
|
PLOG_WARNING << "Rejected ICE candidate: " << sdp;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
GSList *list = g_slist_append(nullptr, cand);
|
GSList *list = g_slist_append(nullptr, cand);
|
||||||
int ret = nice_agent_set_remote_candidates(mNiceAgent.get(), mStreamId, 1, list);
|
int ret = nice_agent_set_remote_candidates(mNiceAgent.get(), mStreamId, 1, list);
|
||||||
@ -561,7 +584,9 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) {
|
|||||||
return ret > 0;
|
return ret > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IceTransport::gatherLocalCandidates() {
|
void IceTransport::gatherLocalCandidates(string mid) {
|
||||||
|
mMid = std::move(mid);
|
||||||
|
|
||||||
// Change state now as candidates calls can be synchronous
|
// Change state now as candidates calls can be synchronous
|
||||||
changeGatheringState(GatheringState::InProgress);
|
changeGatheringState(GatheringState::InProgress);
|
||||||
|
|
||||||
@ -598,6 +623,13 @@ bool IceTransport::send(message_ptr message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IceTransport::outgoing(message_ptr message) {
|
bool IceTransport::outgoing(message_ptr message) {
|
||||||
|
std::lock_guard lock(mOutgoingMutex);
|
||||||
|
if (mOutgoingDscp != message->dscp) {
|
||||||
|
mOutgoingDscp = message->dscp;
|
||||||
|
// Explicit Congestion Notification takes the least-significant 2 bits of the DS field
|
||||||
|
int ds = int(message->dscp << 2);
|
||||||
|
nice_agent_set_stream_tos(mNiceAgent.get(), mStreamId, ds); // ToS is the legacy name for DS
|
||||||
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
@ -45,8 +46,8 @@ public:
|
|||||||
using candidate_callback = std::function<void(const Candidate &candidate)>;
|
using candidate_callback = std::function<void(const Candidate &candidate)>;
|
||||||
using gathering_state_callback = std::function<void(GatheringState state)>;
|
using gathering_state_callback = std::function<void(GatheringState state)>;
|
||||||
|
|
||||||
IceTransport(const Configuration &config, Description::Role role,
|
IceTransport(const Configuration &config, candidate_callback candidateCallback,
|
||||||
candidate_callback candidateCallback, state_callback stateChangeCallback,
|
state_callback stateChangeCallback,
|
||||||
gathering_state_callback gatheringStateChangeCallback);
|
gathering_state_callback gatheringStateChangeCallback);
|
||||||
~IceTransport();
|
~IceTransport();
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ public:
|
|||||||
Description getLocalDescription(Description::Type type) const;
|
Description getLocalDescription(Description::Type type) const;
|
||||||
void setRemoteDescription(const Description &description);
|
void setRemoteDescription(const Description &description);
|
||||||
bool addRemoteCandidate(const Candidate &candidate);
|
bool addRemoteCandidate(const Candidate &candidate);
|
||||||
void gatherLocalCandidates();
|
void gatherLocalCandidates(string mid);
|
||||||
|
|
||||||
std::optional<string> getLocalAddress() const;
|
std::optional<string> getLocalAddress() const;
|
||||||
std::optional<string> getRemoteAddress() const;
|
std::optional<string> getRemoteAddress() const;
|
||||||
@ -99,6 +100,8 @@ private:
|
|||||||
std::unique_ptr<GMainLoop, void (*)(GMainLoop *)> mMainLoop;
|
std::unique_ptr<GMainLoop, void (*)(GMainLoop *)> mMainLoop;
|
||||||
std::thread mMainLoopThread;
|
std::thread mMainLoopThread;
|
||||||
guint mTimeoutId = 0;
|
guint mTimeoutId = 0;
|
||||||
|
std::mutex mOutgoingMutex;
|
||||||
|
unsigned int mOutgoingDscp;
|
||||||
|
|
||||||
static string AddressToString(const NiceAddress &addr);
|
static string AddressToString(const NiceAddress &addr);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ void PeerConnection::close() {
|
|||||||
mNegotiationNeeded = false;
|
mNegotiationNeeded = false;
|
||||||
|
|
||||||
// Close data channels asynchronously
|
// Close data channels asynchronously
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::closeDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::closeDataChannels, this);
|
||||||
|
|
||||||
closeTransports();
|
closeTransports();
|
||||||
}
|
}
|
||||||
@ -187,21 +187,16 @@ void PeerConnection::setLocalDescription(Description::Type type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = initIceTransport();
|
||||||
if (!iceTransport) {
|
|
||||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
|
||||||
// setup:actpass.
|
|
||||||
// See https://tools.ietf.org/html/rfc5763#section-5
|
|
||||||
iceTransport = initIceTransport(Description::Role::ActPass);
|
|
||||||
}
|
|
||||||
|
|
||||||
Description localDescription = iceTransport->getLocalDescription(type);
|
Description local = iceTransport->getLocalDescription(type);
|
||||||
processLocalDescription(std::move(localDescription));
|
processLocalDescription(std::move(local));
|
||||||
|
|
||||||
changeSignalingState(newSignalingState);
|
changeSignalingState(newSignalingState);
|
||||||
|
|
||||||
if (mGatheringState == GatheringState::New)
|
if (mGatheringState == GatheringState::New) {
|
||||||
iceTransport->gatherLocalCandidates();
|
iceTransport->gatherLocalCandidates(localBundleMid());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::setRemoteDescription(Description description) {
|
void PeerConnection::setRemoteDescription(Description description) {
|
||||||
@ -272,12 +267,6 @@ void PeerConnection::setRemoteDescription(Description description) {
|
|||||||
// Candidates will be added at the end, extract them for now
|
// Candidates will be added at the end, extract them for now
|
||||||
auto remoteCandidates = description.extractCandidates();
|
auto remoteCandidates = description.extractCandidates();
|
||||||
auto type = description.type();
|
auto type = description.type();
|
||||||
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
|
||||||
if (!iceTransport)
|
|
||||||
iceTransport = initIceTransport(Description::Role::ActPass);
|
|
||||||
|
|
||||||
iceTransport->setRemoteDescription(description);
|
|
||||||
processRemoteDescription(std::move(description));
|
processRemoteDescription(std::move(description));
|
||||||
|
|
||||||
changeSignalingState(newSignalingState);
|
changeSignalingState(newSignalingState);
|
||||||
@ -287,8 +276,9 @@ void PeerConnection::setRemoteDescription(Description description) {
|
|||||||
setLocalDescription(Description::Type::Answer);
|
setLocalDescription(Description::Type::Answer);
|
||||||
} else {
|
} else {
|
||||||
// This is an answer
|
// This is an answer
|
||||||
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||||
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
||||||
if (!sctpTransport && iceTransport->role() == Description::Role::Active) {
|
if (!sctpTransport && iceTransport && iceTransport->role() == Description::Role::Active) {
|
||||||
// Since we assumed passive role during DataChannel creation, we need to shift the
|
// Since we assumed passive role during DataChannel creation, we need to shift the
|
||||||
// stream numbers by one to shift them from odd to even.
|
// stream numbers by one to shift them from odd to even.
|
||||||
std::unique_lock lock(mDataChannelsMutex); // we are going to swap the container
|
std::unique_lock lock(mDataChannelsMutex); // we are going to swap the container
|
||||||
@ -380,7 +370,7 @@ void PeerConnection::onSignalingStateChange(std::function<void(SignalingState st
|
|||||||
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
|
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
|
||||||
#if !RTC_ENABLE_MEDIA
|
#if !RTC_ENABLE_MEDIA
|
||||||
if (mTracks.empty()) {
|
if (mTracks.empty()) {
|
||||||
PLOG_WARNING << "Tracks will be inative (not compiled with SRTP support)";
|
PLOG_WARNING << "Tracks will be inative (not compiled with media support)";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -405,14 +395,14 @@ void PeerConnection::onTrack(std::function<void(std::shared_ptr<Track>)> callbac
|
|||||||
mTrackCallback = callback;
|
mTrackCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<IceTransport> PeerConnection::initIceTransport(Description::Role role) {
|
shared_ptr<IceTransport> PeerConnection::initIceTransport() {
|
||||||
PLOG_VERBOSE << "Starting ICE transport";
|
PLOG_VERBOSE << "Starting ICE transport";
|
||||||
try {
|
try {
|
||||||
if (auto transport = std::atomic_load(&mIceTransport))
|
if (auto transport = std::atomic_load(&mIceTransport))
|
||||||
return transport;
|
return transport;
|
||||||
|
|
||||||
auto transport = std::make_shared<IceTransport>(
|
auto transport = std::make_shared<IceTransport>(
|
||||||
mConfig, role, weak_bind(&PeerConnection::processLocalCandidate, this, _1),
|
mConfig, weak_bind(&PeerConnection::processLocalCandidate, this, _1),
|
||||||
[this, weak_this = weak_from_this()](IceTransport::State state) {
|
[this, weak_this = weak_from_this()](IceTransport::State state) {
|
||||||
auto shared_this = weak_this.lock();
|
auto shared_this = weak_this.lock();
|
||||||
if (!shared_this)
|
if (!shared_this)
|
||||||
@ -490,7 +480,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
|
|||||||
else
|
else
|
||||||
changeState(State::Connected);
|
changeState(State::Connected);
|
||||||
|
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::openTracks, this));
|
mProcessor->enqueue(&PeerConnection::openTracks, this);
|
||||||
break;
|
break;
|
||||||
case DtlsTransport::State::Failed:
|
case DtlsTransport::State::Failed:
|
||||||
changeState(State::Failed);
|
changeState(State::Failed);
|
||||||
@ -514,7 +504,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
|
|||||||
lower, certificate, verifierCallback,
|
lower, certificate, verifierCallback,
|
||||||
std::bind(&PeerConnection::forwardMedia, this, _1), stateChangeCallback);
|
std::bind(&PeerConnection::forwardMedia, this, _1), stateChangeCallback);
|
||||||
#else
|
#else
|
||||||
PLOG_WARNING << "Ignoring media support (not compiled with SRTP support)";
|
PLOG_WARNING << "Ignoring media support (not compiled with media support)";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,16 +551,16 @@ shared_ptr<SctpTransport> PeerConnection::initSctpTransport() {
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case SctpTransport::State::Connected:
|
case SctpTransport::State::Connected:
|
||||||
changeState(State::Connected);
|
changeState(State::Connected);
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::openDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::openDataChannels, this);
|
||||||
break;
|
break;
|
||||||
case SctpTransport::State::Failed:
|
case SctpTransport::State::Failed:
|
||||||
LOG_WARNING << "SCTP transport failed";
|
LOG_WARNING << "SCTP transport failed";
|
||||||
changeState(State::Failed);
|
changeState(State::Failed);
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::remoteCloseDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::remoteCloseDataChannels, this);
|
||||||
break;
|
break;
|
||||||
case SctpTransport::State::Disconnected:
|
case SctpTransport::State::Disconnected:
|
||||||
changeState(State::Disconnected);
|
changeState(State::Disconnected);
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::remoteCloseDataChannels, this));
|
mProcessor->enqueue(&PeerConnection::remoteCloseDataChannels, this);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Ignore
|
// Ignore
|
||||||
@ -659,10 +649,10 @@ void PeerConnection::forwardMessage(message_ptr message) {
|
|||||||
stream % 2 == remoteParity) {
|
stream % 2 == remoteParity) {
|
||||||
|
|
||||||
channel = std::make_shared<NegociatedDataChannel>(shared_from_this(), sctpTransport,
|
channel = std::make_shared<NegociatedDataChannel>(shared_from_this(), sctpTransport,
|
||||||
message->stream);
|
stream);
|
||||||
channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
|
channel->onOpen(weak_bind(&PeerConnection::triggerDataChannel, this,
|
||||||
weak_ptr<DataChannel>{channel}));
|
weak_ptr<DataChannel>{channel}));
|
||||||
mDataChannels.emplace(message->stream, channel);
|
mDataChannels.emplace(stream, channel);
|
||||||
} else {
|
} else {
|
||||||
// Invalid, close the DataChannel
|
// Invalid, close the DataChannel
|
||||||
sctpTransport->closeStream(message->stream);
|
sctpTransport->closeStream(message->stream);
|
||||||
@ -883,7 +873,7 @@ void PeerConnection::incomingTrack(Description::Media description) {
|
|||||||
std::unique_lock lock(mTracksMutex); // we are going to emplace
|
std::unique_lock lock(mTracksMutex); // we are going to emplace
|
||||||
#if !RTC_ENABLE_MEDIA
|
#if !RTC_ENABLE_MEDIA
|
||||||
if (mTracks.empty()) {
|
if (mTracks.empty()) {
|
||||||
PLOG_WARNING << "Tracks will be inative (not compiled with SRTP support)";
|
PLOG_WARNING << "Tracks will be inative (not compiled with media support)";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (mTracks.find(description.mid()) == mTracks.end()) {
|
if (mTracks.find(description.mid()) == mTracks.end()) {
|
||||||
@ -1024,7 +1014,10 @@ void PeerConnection::processLocalDescription(Description description) {
|
|||||||
if (!description.hasApplication()) {
|
if (!description.hasApplication()) {
|
||||||
std::shared_lock lock(mDataChannelsMutex);
|
std::shared_lock lock(mDataChannelsMutex);
|
||||||
if (!mDataChannels.empty()) {
|
if (!mDataChannels.empty()) {
|
||||||
Description::Application app("data");
|
unsigned int m = 0;
|
||||||
|
while (description.hasMid(std::to_string(m)))
|
||||||
|
++m;
|
||||||
|
Description::Application app(std::to_string(m));
|
||||||
app.setSctpPort(DEFAULT_SCTP_PORT);
|
app.setSctpPort(DEFAULT_SCTP_PORT);
|
||||||
app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
|
app.setMaxMessageSize(LOCAL_MAX_MESSAGE_SIZE);
|
||||||
|
|
||||||
@ -1069,19 +1062,17 @@ void PeerConnection::processLocalDescription(Description description) {
|
|||||||
mCurrentLocalDescription.emplace(std::move(*mLocalDescription));
|
mCurrentLocalDescription.emplace(std::move(*mLocalDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocalDescription.emplace(std::move(description));
|
mLocalDescription.emplace(description);
|
||||||
mLocalDescription->addCandidates(std::move(existingCandidates));
|
mLocalDescription->addCandidates(std::move(existingCandidates));
|
||||||
}
|
}
|
||||||
|
|
||||||
mProcessor->enqueue([this, description = *mLocalDescription]() {
|
|
||||||
PLOG_VERBOSE << "Issuing local description: " << description;
|
PLOG_VERBOSE << "Issuing local description: " << description;
|
||||||
mLocalDescriptionCallback(std::move(description));
|
mProcessor->enqueue(mLocalDescriptionCallback.wrap(), std::move(description));
|
||||||
});
|
|
||||||
|
|
||||||
// Reciprocated tracks might need to be open
|
// Reciprocated tracks might need to be open
|
||||||
if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
||||||
dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
|
dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
|
||||||
mProcessor->enqueue(std::bind(&PeerConnection::openTracks, this));
|
mProcessor->enqueue(&PeerConnection::openTracks, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::processLocalCandidate(Candidate candidate) {
|
void PeerConnection::processLocalCandidate(Candidate candidate) {
|
||||||
@ -1089,13 +1080,11 @@ void PeerConnection::processLocalCandidate(Candidate candidate) {
|
|||||||
if (!mLocalDescription)
|
if (!mLocalDescription)
|
||||||
throw std::logic_error("Got a local candidate without local description");
|
throw std::logic_error("Got a local candidate without local description");
|
||||||
|
|
||||||
candidate.resolve(Candidate::ResolveMode::Simple); // for proper SDP generation later
|
candidate.resolve(Candidate::ResolveMode::Simple);
|
||||||
mLocalDescription->addCandidate(candidate);
|
mLocalDescription->addCandidate(candidate);
|
||||||
|
|
||||||
mProcessor->enqueue([this, candidate = std::move(candidate)]() {
|
|
||||||
PLOG_VERBOSE << "Issuing local candidate: " << candidate;
|
PLOG_VERBOSE << "Issuing local candidate: " << candidate;
|
||||||
mLocalCandidateCallback(std::move(candidate));
|
mProcessor->enqueue(mLocalCandidateCallback.wrap(), std::move(candidate));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::processRemoteDescription(Description description) {
|
void PeerConnection::processRemoteDescription(Description description) {
|
||||||
@ -1107,10 +1096,13 @@ void PeerConnection::processRemoteDescription(Description description) {
|
|||||||
if (mRemoteDescription)
|
if (mRemoteDescription)
|
||||||
existingCandidates = mRemoteDescription->extractCandidates();
|
existingCandidates = mRemoteDescription->extractCandidates();
|
||||||
|
|
||||||
mRemoteDescription.emplace(std::move(description));
|
mRemoteDescription.emplace(description);
|
||||||
mRemoteDescription->addCandidates(std::move(existingCandidates));
|
mRemoteDescription->addCandidates(std::move(existingCandidates));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto iceTransport = initIceTransport();
|
||||||
|
iceTransport->setRemoteDescription(std::move(description));
|
||||||
|
|
||||||
if (description.hasApplication()) {
|
if (description.hasApplication()) {
|
||||||
auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
||||||
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
||||||
@ -1121,28 +1113,45 @@ void PeerConnection::processRemoteDescription(Description description) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::processRemoteCandidate(Candidate candidate) {
|
void PeerConnection::processRemoteCandidate(Candidate candidate) {
|
||||||
std::lock_guard lock(mRemoteDescriptionMutex);
|
|
||||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||||
if (!mRemoteDescription || !iceTransport)
|
{
|
||||||
|
// Set as remote candidate
|
||||||
|
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||||
|
if (!mRemoteDescription)
|
||||||
throw std::logic_error("Got a remote candidate without remote description");
|
throw std::logic_error("Got a remote candidate without remote description");
|
||||||
|
|
||||||
|
if (!iceTransport)
|
||||||
|
throw std::logic_error("Got a remote candidate without ICE transport");
|
||||||
|
|
||||||
candidate.hintMid(mRemoteDescription->bundleMid());
|
candidate.hintMid(mRemoteDescription->bundleMid());
|
||||||
|
|
||||||
if (candidate.resolve(Candidate::ResolveMode::Simple)) {
|
if (mRemoteDescription->hasCandidate(candidate))
|
||||||
iceTransport->addRemoteCandidate(candidate);
|
return; // already in description, ignore
|
||||||
|
|
||||||
|
candidate.resolve(Candidate::ResolveMode::Simple);
|
||||||
|
mRemoteDescription->addCandidate(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate.isResolved()) {
|
||||||
|
iceTransport->addRemoteCandidate(std::move(candidate));
|
||||||
} else {
|
} else {
|
||||||
// OK, we might need a lookup, do it asynchronously
|
// We might need a lookup, do it asynchronously
|
||||||
// We don't use the thread pool because we have no control on the timeout
|
// We don't use the thread pool because we have no control on the timeout
|
||||||
|
if ((iceTransport = std::atomic_load(&mIceTransport))) {
|
||||||
weak_ptr<IceTransport> weakIceTransport{iceTransport};
|
weak_ptr<IceTransport> weakIceTransport{iceTransport};
|
||||||
std::thread t([weakIceTransport, candidate]() mutable {
|
std::thread t([weakIceTransport, candidate = std::move(candidate)]() mutable {
|
||||||
if (candidate.resolve(Candidate::ResolveMode::Lookup))
|
if (candidate.resolve(Candidate::ResolveMode::Lookup))
|
||||||
if (auto iceTransport = weakIceTransport.lock())
|
if (auto iceTransport = weakIceTransport.lock())
|
||||||
iceTransport->addRemoteCandidate(candidate);
|
iceTransport->addRemoteCandidate(std::move(candidate));
|
||||||
});
|
});
|
||||||
t.detach();
|
t.detach();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mRemoteDescription->addCandidate(std::move(candidate));
|
string PeerConnection::localBundleMid() const {
|
||||||
|
std::lock_guard lock(mLocalDescriptionMutex);
|
||||||
|
return mLocalDescription ? mLocalDescription->bundleMid() : "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
|
void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
|
||||||
@ -1150,12 +1159,11 @@ void PeerConnection::triggerDataChannel(weak_ptr<DataChannel> weakDataChannel) {
|
|||||||
if (!dataChannel)
|
if (!dataChannel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mProcessor->enqueue(
|
mProcessor->enqueue(mDataChannelCallback.wrap(), std::move(dataChannel));
|
||||||
[this, dataChannel = std::move(dataChannel)]() { mDataChannelCallback(dataChannel); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerConnection::triggerTrack(std::shared_ptr<Track> track) {
|
void PeerConnection::triggerTrack(std::shared_ptr<Track> track) {
|
||||||
mProcessor->enqueue([this, track = std::move(track)]() { mTrackCallback(track); });
|
mProcessor->enqueue(mTrackCallback.wrap(), std::move(track));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerConnection::changeState(State state) {
|
bool PeerConnection::changeState(State state) {
|
||||||
@ -1177,7 +1185,7 @@ bool PeerConnection::changeState(State state) {
|
|||||||
// This is the last state change, so we may steal the callback
|
// This is the last state change, so we may steal the callback
|
||||||
mProcessor->enqueue([cb = std::move(mStateChangeCallback)]() { cb(State::Closed); });
|
mProcessor->enqueue([cb = std::move(mStateChangeCallback)]() { cb(State::Closed); });
|
||||||
else
|
else
|
||||||
mProcessor->enqueue([this, state]() { mStateChangeCallback(state); });
|
mProcessor->enqueue(mStateChangeCallback.wrap(), state);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1189,7 +1197,7 @@ bool PeerConnection::changeGatheringState(GatheringState state) {
|
|||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << state;
|
s << state;
|
||||||
PLOG_INFO << "Changed gathering state to " << s.str();
|
PLOG_INFO << "Changed gathering state to " << s.str();
|
||||||
mProcessor->enqueue([this, state] { mGatheringStateChangeCallback(state); });
|
mProcessor->enqueue(mGatheringStateChangeCallback.wrap(), state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1200,7 +1208,7 @@ bool PeerConnection::changeSignalingState(SignalingState state) {
|
|||||||
std::ostringstream s;
|
std::ostringstream s;
|
||||||
s << state;
|
s << state;
|
||||||
PLOG_INFO << "Changed signaling state to " << s.str();
|
PLOG_INFO << "Changed signaling state to " << s.str();
|
||||||
mProcessor->enqueue([this, state] { mSignalingStateChangeCallback(state); });
|
mProcessor->enqueue(mSignalingStateChangeCallback.wrap(), state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
|
Processor::Processor(size_t limit) : mTasks(limit) {}
|
||||||
|
|
||||||
Processor::~Processor() { join(); }
|
Processor::~Processor() { join(); }
|
||||||
|
|
||||||
void Processor::join() {
|
void Processor::join() {
|
||||||
@ -29,15 +31,13 @@ void Processor::join() {
|
|||||||
|
|
||||||
void Processor::schedule() {
|
void Processor::schedule() {
|
||||||
std::unique_lock lock(mMutex);
|
std::unique_lock lock(mMutex);
|
||||||
if (mTasks.empty()) {
|
if (auto next = mTasks.tryPop()) {
|
||||||
|
ThreadPool::Instance().enqueue(std::move(*next));
|
||||||
|
} else {
|
||||||
// No more tasks
|
// No more tasks
|
||||||
mPending = false;
|
mPending = false;
|
||||||
mCondition.notify_all();
|
mCondition.notify_all();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::Instance().enqueue(std::move(mTasks.front()));
|
|
||||||
mTasks.pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "include.hpp"
|
#include "include.hpp"
|
||||||
#include "init.hpp"
|
#include "init.hpp"
|
||||||
#include "threadpool.hpp"
|
#include "threadpool.hpp"
|
||||||
|
#include "queue.hpp"
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <future>
|
#include <future>
|
||||||
@ -34,7 +35,7 @@ namespace rtc {
|
|||||||
// Processed tasks in order by delegating them to the thread pool
|
// Processed tasks in order by delegating them to the thread pool
|
||||||
class Processor final {
|
class Processor final {
|
||||||
public:
|
public:
|
||||||
Processor() = default;
|
Processor(size_t limit = 0);
|
||||||
~Processor();
|
~Processor();
|
||||||
|
|
||||||
Processor(const Processor &) = delete;
|
Processor(const Processor &) = delete;
|
||||||
@ -52,7 +53,7 @@ protected:
|
|||||||
// Keep an init token
|
// Keep an init token
|
||||||
const init_token mInitToken = Init::Token();
|
const init_token mInitToken = Init::Token();
|
||||||
|
|
||||||
std::queue<std::function<void()>> mTasks;
|
Queue<std::function<void()>> mTasks;
|
||||||
bool mPending = false; // true iff a task is pending in the thread pool
|
bool mPending = false; // true iff a task is pending in the thread pool
|
||||||
|
|
||||||
mutable std::mutex mMutex;
|
mutable std::mutex mMutex;
|
||||||
@ -71,7 +72,7 @@ template <class F, class... Args> void Processor::enqueue(F &&f, Args &&... args
|
|||||||
ThreadPool::Instance().enqueue(std::move(task));
|
ThreadPool::Instance().enqueue(std::move(task));
|
||||||
mPending = true;
|
mPending = true;
|
||||||
} else {
|
} else {
|
||||||
mTasks.emplace(std::move(task));
|
mTasks.push(std::move(task));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ bool RtcpReceivingSession::requestKeyframe() {
|
|||||||
|
|
||||||
void RtcpReceivingSession::pushPLI() {
|
void RtcpReceivingSession::pushPLI() {
|
||||||
auto msg = rtc::make_message(rtc::RTCP_PLI::size(), rtc::Message::Type::Control);
|
auto msg = rtc::make_message(rtc::RTCP_PLI::size(), rtc::Message::Type::Control);
|
||||||
auto *pli = (rtc::RTCP_PLI *)msg->data();
|
auto *pli = reinterpret_cast<rtc::RTCP_PLI *>(msg->data());
|
||||||
pli->preparePacket(mSsrc);
|
pli->preparePacket(mSsrc);
|
||||||
send(msg);
|
send(msg);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ void SctpTransport::Cleanup() {
|
|||||||
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,
|
||||||
state_callback stateChangeCallback)
|
state_callback stateChangeCallback)
|
||||||
: Transport(lower, std::move(stateChangeCallback)), mPort(port),
|
: Transport(lower, std::move(stateChangeCallback)), mPort(port), mPendingRecvCount(0),
|
||||||
mSendQueue(0, message_size_func), mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
|
mSendQueue(0, message_size_func), mBufferedAmountCallback(std::move(bufferedAmountCallback)) {
|
||||||
onRecv(recvCallback);
|
onRecv(recvCallback);
|
||||||
|
|
||||||
@ -100,11 +100,12 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
|||||||
Instances.insert(this);
|
Instances.insert(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSock = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, &SctpTransport::RecvCallback,
|
mSock = usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, nullptr, nullptr, 0, nullptr);
|
||||||
&SctpTransport::SendCallback, 0, this);
|
|
||||||
if (!mSock)
|
if (!mSock)
|
||||||
throw std::runtime_error("Could not create SCTP socket, errno=" + std::to_string(errno));
|
throw std::runtime_error("Could not create SCTP socket, errno=" + std::to_string(errno));
|
||||||
|
|
||||||
|
usrsctp_set_upcall(mSock, &SctpTransport::UpcallCallback, this);
|
||||||
|
|
||||||
if (usrsctp_set_non_blocking(mSock, 1))
|
if (usrsctp_set_non_blocking(mSock, 1))
|
||||||
throw std::runtime_error("Unable to set non-blocking mode, errno=" + std::to_string(errno));
|
throw std::runtime_error("Unable to set non-blocking mode, errno=" + std::to_string(errno));
|
||||||
|
|
||||||
@ -122,6 +123,10 @@ SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port,
|
|||||||
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(av)))
|
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &av, sizeof(av)))
|
||||||
throw std::runtime_error("Could not set socket option SCTP_ENABLE_STREAM_RESET, errno=" +
|
throw std::runtime_error("Could not set socket option SCTP_ENABLE_STREAM_RESET, errno=" +
|
||||||
std::to_string(errno));
|
std::to_string(errno));
|
||||||
|
int on = 1;
|
||||||
|
if (usrsctp_setsockopt(mSock, IPPROTO_SCTP, SCTP_RECVRCVINFO, &on, sizeof(on)))
|
||||||
|
throw std::runtime_error("Could set socket option SCTP_RECVRCVINFO, errno=" +
|
||||||
|
std::to_string(errno));
|
||||||
|
|
||||||
struct sctp_event se = {};
|
struct sctp_event se = {};
|
||||||
se.se_assoc_id = SCTP_ALL_ASSOC;
|
se.se_assoc_id = SCTP_ALL_ASSOC;
|
||||||
@ -225,6 +230,7 @@ bool SctpTransport::stop() {
|
|||||||
|
|
||||||
void SctpTransport::close() {
|
void SctpTransport::close() {
|
||||||
if (mSock) {
|
if (mSock) {
|
||||||
|
mProcessor.join();
|
||||||
usrsctp_close(mSock);
|
usrsctp_close(mSock);
|
||||||
mSock = nullptr;
|
mSock = nullptr;
|
||||||
}
|
}
|
||||||
@ -319,6 +325,67 @@ void SctpTransport::incoming(message_ptr message) {
|
|||||||
usrsctp_conninput(this, message->data(), message->size(), 0);
|
usrsctp_conninput(this, message->data(), message->size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SctpTransport::outgoing(message_ptr message) {
|
||||||
|
// Set recommended medium-priority DSCP value
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
|
||||||
|
message->dscp = 10; // AF11: Assured Forwarding class 1, low drop probability
|
||||||
|
return Transport::outgoing(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SctpTransport::doRecv() {
|
||||||
|
std::lock_guard lock(mRecvMutex);
|
||||||
|
--mPendingRecvCount;
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const size_t bufferSize = 65536;
|
||||||
|
byte buffer[bufferSize];
|
||||||
|
socklen_t fromlen = 0;
|
||||||
|
struct sctp_rcvinfo info = {};
|
||||||
|
socklen_t infolen = sizeof(info);
|
||||||
|
unsigned int infotype = 0;
|
||||||
|
int flags = 0;
|
||||||
|
ssize_t len = usrsctp_recvv(mSock, buffer, bufferSize, nullptr, &fromlen, &info,
|
||||||
|
&infolen, &infotype, &flags);
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ECONNRESET)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("SCTP recv failed, errno=" + std::to_string(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
PLOG_VERBOSE << "SCTP recv, len=" << len;
|
||||||
|
|
||||||
|
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
|
||||||
|
// therefore partial notifications and messages need to be handled separately.
|
||||||
|
if (flags & MSG_NOTIFICATION) {
|
||||||
|
// SCTP event notification
|
||||||
|
mPartialNotification.insert(mPartialNotification.end(), buffer, buffer + len);
|
||||||
|
if (flags & MSG_EOR) {
|
||||||
|
// Notification is complete, process it
|
||||||
|
auto notification =
|
||||||
|
reinterpret_cast<union sctp_notification *>(mPartialNotification.data());
|
||||||
|
processNotification(notification, mPartialNotification.size());
|
||||||
|
mPartialNotification.clear();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// SCTP message
|
||||||
|
mPartialMessage.insert(mPartialMessage.end(), buffer, buffer + len);
|
||||||
|
if (flags & MSG_EOR) {
|
||||||
|
// Message is complete, process it
|
||||||
|
if (infotype != SCTP_RECVV_RCVINFO)
|
||||||
|
throw std::runtime_error("Missing SCTP recv info");
|
||||||
|
|
||||||
|
processData(std::move(mPartialMessage), info.rcv_sid,
|
||||||
|
PayloadId(ntohl(info.rcv_ppid)));
|
||||||
|
mPartialMessage.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
PLOG_WARNING << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SctpTransport::trySendQueue() {
|
bool SctpTransport::trySendQueue() {
|
||||||
// Requires mSendMutex to be locked
|
// Requires mSendMutex to be locked
|
||||||
while (auto next = mSendQueue.peek()) {
|
while (auto next = mSendQueue.peek()) {
|
||||||
@ -472,44 +539,21 @@ bool SctpTransport::safeFlush() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::handleRecv(struct socket * /*sock*/, union sctp_sockstore /*addr*/,
|
void SctpTransport::handleUpcall() {
|
||||||
const byte *data, size_t len, struct sctp_rcvinfo info, int flags) {
|
if (!mSock)
|
||||||
try {
|
return;
|
||||||
PLOG_VERBOSE << "Handle recv, len=" << len;
|
|
||||||
|
|
||||||
// SCTP_FRAGMENT_INTERLEAVE does not seem to work as expected for messages > 64KB,
|
PLOG_VERBOSE << "Handle upcall";
|
||||||
// therefore partial notifications and messages need to be handled separately.
|
|
||||||
if (flags & MSG_NOTIFICATION) {
|
int events = usrsctp_get_events(mSock);
|
||||||
// SCTP event notification
|
|
||||||
mPartialNotification.insert(mPartialNotification.end(), data, data + len);
|
if (events & SCTP_EVENT_READ && mPendingRecvCount == 0) {
|
||||||
if (flags & MSG_EOR) {
|
++mPendingRecvCount;
|
||||||
// Notification is complete, process it
|
mProcessor.enqueue(&SctpTransport::doRecv, this);
|
||||||
processNotification(
|
|
||||||
reinterpret_cast<const union sctp_notification *>(mPartialNotification.data()),
|
|
||||||
mPartialNotification.size());
|
|
||||||
mPartialNotification.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// SCTP message
|
|
||||||
mPartialMessage.insert(mPartialMessage.end(), data, data + len);
|
|
||||||
if (flags & MSG_EOR) {
|
|
||||||
// Message is complete, process it
|
|
||||||
processData(std::move(mPartialMessage), info.rcv_sid,
|
|
||||||
PayloadId(ntohl(info.rcv_ppid)));
|
|
||||||
mPartialMessage.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const std::exception &e) {
|
if (events & SCTP_EVENT_WRITE)
|
||||||
PLOG_ERROR << "SCTP recv: " << e.what();
|
mProcessor.enqueue(&SctpTransport::safeFlush, this);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0; // success
|
|
||||||
}
|
|
||||||
|
|
||||||
int SctpTransport::handleSend(size_t free) {
|
|
||||||
PLOG_VERBOSE << "Handle send, free=" << free;
|
|
||||||
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*/) {
|
||||||
@ -517,6 +561,7 @@ int SctpTransport::handleWrite(byte *data, size_t len, uint8_t /*tos*/, uint8_t
|
|||||||
std::unique_lock lock(mWriteMutex);
|
std::unique_lock lock(mWriteMutex);
|
||||||
PLOG_VERBOSE << "Handle write, len=" << len;
|
PLOG_VERBOSE << "Handle write, len=" << len;
|
||||||
|
|
||||||
|
auto message = make_message(data, data + len);
|
||||||
if (!outgoing(make_message(data, data + len)))
|
if (!outgoing(make_message(data, data + len)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -699,31 +744,14 @@ std::optional<milliseconds> SctpTransport::rtt() {
|
|||||||
return 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,
|
void SctpTransport::UpcallCallback(struct socket *, void *arg, int /* flags */) {
|
||||||
size_t len, struct sctp_rcvinfo recv_info, int flags,
|
auto *transport = static_cast<SctpTransport *>(arg);
|
||||||
void *ulp_info) {
|
|
||||||
auto *transport = static_cast<SctpTransport *>(ulp_info);
|
|
||||||
|
|
||||||
std::shared_lock lock(InstancesMutex);
|
|
||||||
if (Instances.find(transport) == Instances.end()) {
|
|
||||||
free(data);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret =
|
|
||||||
transport->handleRecv(sock, addr, static_cast<const byte *>(data), len, recv_info, flags);
|
|
||||||
free(data);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SctpTransport::SendCallback(struct socket *, uint32_t sb_free, void *ulp_info) {
|
|
||||||
auto *transport = static_cast<SctpTransport *>(ulp_info);
|
|
||||||
|
|
||||||
std::shared_lock lock(InstancesMutex);
|
std::shared_lock lock(InstancesMutex);
|
||||||
if (Instances.find(transport) == Instances.end())
|
if (Instances.find(transport) == Instances.end())
|
||||||
return -1;
|
return;
|
||||||
|
|
||||||
return transport->handleSend(size_t(sb_free));
|
transport->handleUpcall();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
|
int SctpTransport::WriteCallback(void *ptr, void *data, size_t len, uint8_t tos, uint8_t set_df) {
|
||||||
|
@ -21,8 +21,10 @@
|
|||||||
|
|
||||||
#include "include.hpp"
|
#include "include.hpp"
|
||||||
#include "peerconnection.hpp"
|
#include "peerconnection.hpp"
|
||||||
|
#include "processor.hpp"
|
||||||
#include "queue.hpp"
|
#include "queue.hpp"
|
||||||
#include "transport.hpp"
|
#include "transport.hpp"
|
||||||
|
#include "processor.hpp"
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -35,7 +37,7 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
class SctpTransport : public Transport {
|
class SctpTransport final : public Transport {
|
||||||
public:
|
public:
|
||||||
static void Init();
|
static void Init();
|
||||||
static void Cleanup();
|
static void Cleanup();
|
||||||
@ -75,16 +77,16 @@ private:
|
|||||||
void shutdown();
|
void shutdown();
|
||||||
void close();
|
void close();
|
||||||
void incoming(message_ptr message) override;
|
void incoming(message_ptr message) override;
|
||||||
|
bool outgoing(message_ptr message) override;
|
||||||
|
|
||||||
|
void doRecv();
|
||||||
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);
|
void sendReset(uint16_t streamId);
|
||||||
bool safeFlush();
|
bool safeFlush();
|
||||||
|
|
||||||
int handleRecv(struct socket *sock, union sctp_sockstore addr, const byte *data, size_t len,
|
void handleUpcall();
|
||||||
struct sctp_rcvinfo recv_info, int flags);
|
|
||||||
int handleSend(size_t free);
|
|
||||||
int handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df);
|
int handleWrite(byte *data, size_t len, uint8_t tos, uint8_t set_df);
|
||||||
|
|
||||||
void processData(binary &&data, uint16_t streamId, PayloadId ppid);
|
void processData(binary &&data, uint16_t streamId, PayloadId ppid);
|
||||||
@ -93,7 +95,9 @@ private:
|
|||||||
const uint16_t mPort;
|
const uint16_t mPort;
|
||||||
struct socket *mSock;
|
struct socket *mSock;
|
||||||
|
|
||||||
std::mutex mSendMutex;
|
Processor mProcessor;
|
||||||
|
std::atomic<int> mPendingRecvCount;
|
||||||
|
std::mutex mRecvMutex, mSendMutex;
|
||||||
Queue<message_ptr> mSendQueue;
|
Queue<message_ptr> mSendQueue;
|
||||||
std::map<uint16_t, size_t> mBufferedAmount;
|
std::map<uint16_t, size_t> mBufferedAmount;
|
||||||
amount_callback mBufferedAmountCallback;
|
amount_callback mBufferedAmountCallback;
|
||||||
@ -109,9 +113,7 @@ private:
|
|||||||
// Stats
|
// Stats
|
||||||
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
std::atomic<size_t> mBytesSent = 0, mBytesReceived = 0;
|
||||||
|
|
||||||
static int RecvCallback(struct socket *sock, union sctp_sockstore addr, void *data, size_t len,
|
static void UpcallCallback(struct socket *sock, void *arg, int flags);
|
||||||
struct sctp_rcvinfo recv_info, int flags, void *ulp_info);
|
|
||||||
static int SendCallback(struct socket *sock, uint32_t sb_free, void *ulp_info);
|
|
||||||
static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
|
static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
|
||||||
|
|
||||||
static std::unordered_set<SctpTransport *> Instances;
|
static std::unordered_set<SctpTransport *> Instances;
|
||||||
|
@ -18,15 +18,26 @@
|
|||||||
|
|
||||||
#include "threadpool.hpp"
|
#include "threadpool.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void joinThreadPoolInstance() {
|
||||||
|
rtc::ThreadPool::Instance().join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
ThreadPool &ThreadPool::Instance() {
|
ThreadPool &ThreadPool::Instance() {
|
||||||
// Init handles joining on cleanup
|
|
||||||
static ThreadPool *instance = new ThreadPool;
|
static ThreadPool *instance = new ThreadPool;
|
||||||
return *instance;
|
return *instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::~ThreadPool() { join(); }
|
ThreadPool::ThreadPool() {
|
||||||
|
std::atexit(joinThreadPoolInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool::~ThreadPool() {}
|
||||||
|
|
||||||
int ThreadPool::count() const {
|
int ThreadPool::count() const {
|
||||||
std::unique_lock lock(mWorkersMutex);
|
std::unique_lock lock(mWorkersMutex);
|
||||||
|
@ -56,7 +56,7 @@ public:
|
|||||||
auto enqueue(F &&f, Args &&... args) -> invoke_future_t<F, Args...>;
|
auto enqueue(F &&f, Args &&... args) -> invoke_future_t<F, Args...>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ThreadPool() = default;
|
ThreadPool();
|
||||||
~ThreadPool();
|
~ThreadPool();
|
||||||
|
|
||||||
std::function<void()> dequeue(); // returns null function if joining
|
std::function<void()> dequeue(); // returns null function if joining
|
||||||
|
@ -45,12 +45,30 @@ void Track::close() {
|
|||||||
setRtcpHandler(nullptr);
|
setRtcpHandler(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Track::send(message_variant data) { return outgoing(make_message(std::move(data))); }
|
bool Track::send(message_variant data) {
|
||||||
|
if (mIsClosed)
|
||||||
|
throw std::runtime_error("Track is closed");
|
||||||
|
|
||||||
bool Track::send(const byte *data, size_t size) {
|
auto direction = mMediaDescription.direction();
|
||||||
return outgoing(std::make_shared<Message>(data, data + size, Message::Binary));
|
if ((direction == Description::Direction::RecvOnly ||
|
||||||
|
direction == Description::Direction::Inactive)) {
|
||||||
|
PLOG_WARNING << "Track media direction does not allow transmission, dropping";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto message = make_message(std::move(data));
|
||||||
|
|
||||||
|
if (mRtcpHandler) {
|
||||||
|
message = mRtcpHandler->outgoing(message);
|
||||||
|
if (!message)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outgoing(std::move(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Track::send(const byte *data, size_t size) { return send(binary(data, data + size)); }
|
||||||
|
|
||||||
std::optional<message_variant> Track::receive() {
|
std::optional<message_variant> Track::receive() {
|
||||||
if (auto next = mRecvQueue.tryPop())
|
if (auto next = mRecvQueue.tryPop())
|
||||||
return to_variant(std::move(**next));
|
return to_variant(std::move(**next));
|
||||||
@ -88,50 +106,10 @@ void Track::open(shared_ptr<DtlsSrtpTransport> transport) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool Track::outgoing(message_ptr message) {
|
|
||||||
|
|
||||||
if (mRtcpHandler) {
|
|
||||||
message = mRtcpHandler->outgoing(message);
|
|
||||||
if (!message)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto direction = mMediaDescription.direction();
|
|
||||||
if ((direction == Description::Direction::RecvOnly ||
|
|
||||||
direction == Description::Direction::Inactive) &&
|
|
||||||
message->type != Message::Control) {
|
|
||||||
PLOG_WARNING << "Track media direction does not allow transmission, dropping";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIsClosed)
|
|
||||||
throw std::runtime_error("Track is closed");
|
|
||||||
|
|
||||||
if (message->size() > maxMessageSize())
|
|
||||||
throw std::runtime_error("Message size exceeds limit");
|
|
||||||
|
|
||||||
#if RTC_ENABLE_MEDIA
|
|
||||||
auto transport = mDtlsSrtpTransport.lock();
|
|
||||||
if (!transport)
|
|
||||||
throw std::runtime_error("Track transport is not open");
|
|
||||||
|
|
||||||
return transport->sendMedia(message);
|
|
||||||
#else
|
|
||||||
PLOG_WARNING << "Ignoring track send (not compiled with SRTP support)";
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Track::incoming(message_ptr message) {
|
void Track::incoming(message_ptr message) {
|
||||||
if (!message)
|
if (!message)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mRtcpHandler) {
|
|
||||||
message = mRtcpHandler->incoming(message);
|
|
||||||
if (!message)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto direction = mMediaDescription.direction();
|
auto direction = mMediaDescription.direction();
|
||||||
if ((direction == Description::Direction::SendOnly ||
|
if ((direction == Description::Direction::SendOnly ||
|
||||||
direction == Description::Direction::Inactive) &&
|
direction == Description::Direction::Inactive) &&
|
||||||
@ -140,30 +118,46 @@ void Track::incoming(message_ptr message) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tail drop if queue is full
|
if (mRtcpHandler) {
|
||||||
if (mRecvQueue.full())
|
message = mRtcpHandler->incoming(message);
|
||||||
|
if (!message)
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tail drop if queue is full
|
||||||
|
if (mRecvQueue.full()) {
|
||||||
|
PLOG_WARNING << "Track incoming queue is full, dropping";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mRecvQueue.push(message);
|
mRecvQueue.push(message);
|
||||||
triggerAvailable(mRecvQueue.size());
|
triggerAvailable(mRecvQueue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Track::setRtcpHandler(std::shared_ptr<RtcpHandler> handler) {
|
bool Track::outgoing([[maybe_unused]] message_ptr message) {
|
||||||
mRtcpHandler = std::move(handler);
|
|
||||||
if (mRtcpHandler) {
|
|
||||||
mRtcpHandler->onOutgoing([&]([[maybe_unused]] const rtc::message_ptr &message) {
|
|
||||||
#if RTC_ENABLE_MEDIA
|
#if RTC_ENABLE_MEDIA
|
||||||
auto transport = mDtlsSrtpTransport.lock();
|
auto transport = mDtlsSrtpTransport.lock();
|
||||||
if (!transport)
|
if (!transport)
|
||||||
throw std::runtime_error("Track transport is not open");
|
throw std::runtime_error("Track transport is not open");
|
||||||
|
|
||||||
|
// Set recommended medium-priority DSCP value
|
||||||
|
// See https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18
|
||||||
|
if (mMediaDescription.type() == "audio")
|
||||||
|
message->dscp = 46; // EF: Expedited Forwarding
|
||||||
|
else
|
||||||
|
message->dscp = 36; // AF42: Assured Forwarding class 4, medium drop probability
|
||||||
|
|
||||||
return transport->sendMedia(message);
|
return transport->sendMedia(message);
|
||||||
#else
|
#else
|
||||||
PLOG_WARNING << "Ignoring track send (not compiled with SRTP support)";
|
PLOG_WARNING << "Ignoring track send (not compiled with media support)";
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Track::setRtcpHandler(std::shared_ptr<RtcpHandler> handler) {
|
||||||
|
mRtcpHandler = std::move(handler);
|
||||||
|
if (mRtcpHandler)
|
||||||
|
mRtcpHandler->onOutgoing(std::bind(&Track::outgoing, this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Track::requestKeyframe() {
|
bool Track::requestKeyframe() {
|
||||||
|
@ -28,8 +28,6 @@
|
|||||||
|
|
||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
using namespace std::placeholders;
|
|
||||||
|
|
||||||
class Transport {
|
class Transport {
|
||||||
public:
|
public:
|
||||||
enum class State { Disconnected, Connecting, Connected, Completed, Failed };
|
enum class State { Disconnected, Connecting, Connected, Completed, Failed };
|
||||||
@ -57,7 +55,7 @@ public:
|
|||||||
void registerIncoming() {
|
void registerIncoming() {
|
||||||
if (mLower) {
|
if (mLower) {
|
||||||
PLOG_VERBOSE << "Registering incoming callback";
|
PLOG_VERBOSE << "Registering incoming callback";
|
||||||
mLower->onRecv(std::bind(&Transport::incoming, this, _1));
|
mLower->onRecv(std::bind(&Transport::incoming, this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
namespace rtc {
|
namespace rtc {
|
||||||
|
|
||||||
using std::shared_ptr;
|
using std::shared_ptr;
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
WebSocket::WebSocket(std::optional<Configuration> config)
|
WebSocket::WebSocket(std::optional<Configuration> config)
|
||||||
: mConfig(config ? std::move(*config) : Configuration()),
|
: mConfig(config ? std::move(*config) : Configuration()),
|
||||||
@ -117,6 +118,10 @@ void WebSocket::remoteClose() {
|
|||||||
|
|
||||||
bool WebSocket::send(message_variant data) { return outgoing(make_message(std::move(data))); }
|
bool WebSocket::send(message_variant data) { return outgoing(make_message(std::move(data))); }
|
||||||
|
|
||||||
|
bool WebSocket::send(const byte *data, size_t size) {
|
||||||
|
return outgoing(make_message(data, data + size));
|
||||||
|
}
|
||||||
|
|
||||||
bool WebSocket::isOpen() const { return mState == State::Open; }
|
bool WebSocket::isOpen() const { return mState == State::Open; }
|
||||||
|
|
||||||
bool WebSocket::isClosed() const { return mState == State::Closed; }
|
bool WebSocket::isClosed() const { return mState == State::Closed; }
|
||||||
@ -286,8 +291,14 @@ shared_ptr<WsTransport> WebSocket::initWsTransport() {
|
|||||||
shared_ptr<Transport> lower = std::atomic_load(&mTlsTransport);
|
shared_ptr<Transport> lower = std::atomic_load(&mTlsTransport);
|
||||||
if (!lower)
|
if (!lower)
|
||||||
lower = std::atomic_load(&mTcpTransport);
|
lower = std::atomic_load(&mTcpTransport);
|
||||||
|
|
||||||
|
WsTransport::Configuration wsConfig = {};
|
||||||
|
wsConfig.host = mHost;
|
||||||
|
wsConfig.path = mPath;
|
||||||
|
wsConfig.protocols = mConfig.protocols;
|
||||||
|
|
||||||
auto transport = std::make_shared<WsTransport>(
|
auto transport = std::make_shared<WsTransport>(
|
||||||
lower, mHost, mPath, weak_bind(&WebSocket::incoming, this, _1),
|
lower, wsConfig, weak_bind(&WebSocket::incoming, this, _1),
|
||||||
[this, weak_this = weak_from_this()](State state) {
|
[this, weak_this = weak_from_this()](State state) {
|
||||||
auto shared_this = weak_this.lock();
|
auto shared_this = weak_this.lock();
|
||||||
if (!shared_this)
|
if (!shared_this)
|
||||||
|
@ -24,8 +24,10 @@
|
|||||||
#if RTC_ENABLE_WEBSOCKET
|
#if RTC_ENABLE_WEBSOCKET
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <iterator>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <numeric>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
@ -52,17 +54,17 @@ using std::to_string;
|
|||||||
using random_bytes_engine =
|
using random_bytes_engine =
|
||||||
std::independent_bits_engine<std::default_random_engine, CHAR_BIT, unsigned short>;
|
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, Configuration config,
|
||||||
message_callback recvCallback, state_callback stateCallback)
|
message_callback recvCallback, state_callback stateCallback)
|
||||||
: Transport(lower, std::move(stateCallback)), mHost(std::move(host)), mPath(std::move(path)) {
|
: Transport(lower, std::move(stateCallback)), mConfig(std::move(config)) {
|
||||||
onRecv(recvCallback);
|
onRecv(recvCallback);
|
||||||
|
|
||||||
PLOG_DEBUG << "Initializing WebSocket transport";
|
PLOG_DEBUG << "Initializing WebSocket transport";
|
||||||
|
|
||||||
if (mHost.empty())
|
if (mConfig.host.empty())
|
||||||
throw std::invalid_argument("WebSocket HTTP host cannot be empty");
|
throw std::invalid_argument("WebSocket HTTP host cannot be empty");
|
||||||
|
|
||||||
if (mPath.empty())
|
if (mConfig.path.empty())
|
||||||
throw std::invalid_argument("WebSocket HTTP path cannot be empty");
|
throw std::invalid_argument("WebSocket HTTP path cannot be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +155,7 @@ void WsTransport::close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WsTransport::sendHttpRequest() {
|
bool WsTransport::sendHttpRequest() {
|
||||||
PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mPath;
|
PLOG_DEBUG << "Sending WebSocket HTTP request for path " << mConfig.path;
|
||||||
changeState(State::Connecting);
|
changeState(State::Connecting);
|
||||||
|
|
||||||
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
auto seed = static_cast<unsigned int>(system_clock::now().time_since_epoch().count());
|
||||||
@ -163,18 +165,27 @@ bool WsTransport::sendHttpRequest() {
|
|||||||
auto k = reinterpret_cast<uint8_t *>(key.data());
|
auto k = reinterpret_cast<uint8_t *>(key.data());
|
||||||
std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); });
|
std::generate(k, k + key.size(), [&]() { return uint8_t(generator()); });
|
||||||
|
|
||||||
const string request = "GET " + mPath +
|
string appendHeader = "";
|
||||||
|
if (mConfig.protocols.size() > 0) {
|
||||||
|
appendHeader +=
|
||||||
|
"Sec-WebSocket-Protocol: " +
|
||||||
|
std::accumulate(mConfig.protocols.begin(), mConfig.protocols.end(), string(),
|
||||||
|
[](const string &a, const string &b) -> string {
|
||||||
|
return a + (a.length() > 0 ? "," : "") + b;
|
||||||
|
}) +
|
||||||
|
"\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
const string request = "GET " + mConfig.path +
|
||||||
" HTTP/1.1\r\n"
|
" HTTP/1.1\r\n"
|
||||||
"Host: " +
|
"Host: " +
|
||||||
mHost +
|
mConfig.host +
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"Connection: Upgrade\r\n"
|
"Connection: Upgrade\r\n"
|
||||||
"Upgrade: websocket\r\n"
|
"Upgrade: websocket\r\n"
|
||||||
"Sec-WebSocket-Version: 13\r\n"
|
"Sec-WebSocket-Version: 13\r\n"
|
||||||
"Sec-WebSocket-Key: " +
|
"Sec-WebSocket-Key: " +
|
||||||
to_base64(key) +
|
to_base64(key) + "\r\n" + std::move(appendHeader) + "\r\n";
|
||||||
"\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
auto data = reinterpret_cast<const byte *>(request.data());
|
auto data = reinterpret_cast<const byte *>(request.data());
|
||||||
auto size = request.size();
|
auto size = request.size();
|
||||||
@ -227,8 +238,14 @@ size_t WsTransport::readHttpResponse(const byte *buffer, size_t size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto h = headers.find("upgrade");
|
auto h = headers.find("upgrade");
|
||||||
if (h == headers.end() || h->second != "websocket")
|
if (h == headers.end())
|
||||||
throw std::runtime_error("WebSocket update header missing or mismatching");
|
throw std::runtime_error("WebSocket update header missing");
|
||||||
|
|
||||||
|
string upgrade;
|
||||||
|
std::transform(h->second.begin(), h->second.end(), std::back_inserter(upgrade),
|
||||||
|
[](char c) { return std::tolower(c); });
|
||||||
|
if (upgrade != "websocket")
|
||||||
|
throw std::runtime_error("WebSocket update header mismatching: " + h->second);
|
||||||
|
|
||||||
h = headers.find("sec-websocket-accept");
|
h = headers.find("sec-websocket-accept");
|
||||||
if (h == headers.end())
|
if (h == headers.end())
|
||||||
|
@ -31,7 +31,13 @@ class TlsTransport;
|
|||||||
|
|
||||||
class WsTransport : public Transport {
|
class WsTransport : public Transport {
|
||||||
public:
|
public:
|
||||||
WsTransport(std::shared_ptr<Transport> lower, string host, string path,
|
struct Configuration {
|
||||||
|
string host;
|
||||||
|
string path = "/";
|
||||||
|
std::vector<string> protocols;
|
||||||
|
};
|
||||||
|
|
||||||
|
WsTransport(std::shared_ptr<Transport> lower, Configuration config,
|
||||||
message_callback recvCallback, state_callback stateCallback);
|
message_callback recvCallback, state_callback stateCallback);
|
||||||
~WsTransport();
|
~WsTransport();
|
||||||
|
|
||||||
@ -68,12 +74,12 @@ private:
|
|||||||
void recvFrame(const Frame &frame);
|
void recvFrame(const Frame &frame);
|
||||||
bool sendFrame(const Frame &frame);
|
bool sendFrame(const Frame &frame);
|
||||||
|
|
||||||
const string mHost;
|
const Configuration mConfig;
|
||||||
const string mPath;
|
|
||||||
|
|
||||||
binary mBuffer;
|
binary mBuffer;
|
||||||
binary mPartial;
|
binary mPartial;
|
||||||
Opcode mPartialOpcode;
|
Opcode mPartialOpcode;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
@ -100,14 +100,48 @@ static void RTC_API messageCallback(int id, const char *message, int size, void
|
|||||||
|
|
||||||
static void RTC_API dataChannelCallback(int pc, int dc, void *ptr) {
|
static void RTC_API dataChannelCallback(int pc, int dc, void *ptr) {
|
||||||
Peer *peer = (Peer *)ptr;
|
Peer *peer = (Peer *)ptr;
|
||||||
peer->dc = dc;
|
|
||||||
peer->connected = true;
|
char label[256];
|
||||||
|
if (rtcGetDataChannelLabel(dc, label, 256) < 0) {
|
||||||
|
fprintf(stderr, "rtcGetDataChannelLabel failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char protocol[256];
|
||||||
|
if (rtcGetDataChannelProtocol(dc, protocol, 256) < 0) {
|
||||||
|
fprintf(stderr, "rtcGetDataChannelProtocol failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtcReliability reliability;
|
||||||
|
if (rtcGetDataChannelReliability(dc, &reliability) < 0) {
|
||||||
|
fprintf(stderr, "rtcGetDataChannelReliability failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("DataChannel %d: Received with label \"%s\" and protocol \"%s\"\n",
|
||||||
|
peer == peer1 ? 1 : 2, label, protocol);
|
||||||
|
|
||||||
|
if (strcmp(label, "test") != 0) {
|
||||||
|
fprintf(stderr, "Wrong DataChannel label\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(protocol, "protocol") != 0) {
|
||||||
|
fprintf(stderr, "Wrong DataChannel protocol\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reliability.unordered == false) {
|
||||||
|
fprintf(stderr, "Wrong DataChannel reliability\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
rtcSetClosedCallback(dc, closedCallback);
|
rtcSetClosedCallback(dc, closedCallback);
|
||||||
rtcSetMessageCallback(dc, messageCallback);
|
rtcSetMessageCallback(dc, messageCallback);
|
||||||
|
|
||||||
char buffer[256];
|
peer->dc = dc;
|
||||||
if (rtcGetDataChannelLabel(dc, buffer, 256) >= 0)
|
peer->connected = true;
|
||||||
printf("DataChannel %d: Received with label \"%s\"\n", peer == peer1 ? 1 : 2, buffer);
|
|
||||||
|
|
||||||
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
|
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
|
||||||
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
|
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
|
||||||
@ -127,6 +161,7 @@ static Peer *createPeer(const rtcConfiguration *config) {
|
|||||||
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
|
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
|
||||||
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
|
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
|
||||||
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
|
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
|
||||||
|
rtcSetSignalingStateChangeCallback(peer->pc, signalingStateCallback);
|
||||||
|
|
||||||
return peer;
|
return peer;
|
||||||
}
|
}
|
||||||
@ -173,7 +208,12 @@ int test_capi_connectivity_main() {
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
// Peer 1: Create data channel
|
// Peer 1: Create data channel
|
||||||
peer1->dc = rtcCreateDataChannel(peer1->pc, "test");
|
rtcDataChannelInit init;
|
||||||
|
memset(&init, 0, sizeof(init));
|
||||||
|
init.protocol = "protocol";
|
||||||
|
init.reliability.unordered = true;
|
||||||
|
|
||||||
|
peer1->dc = rtcCreateDataChannelEx(peer1->pc, "test", &init);
|
||||||
rtcSetOpenCallback(peer1->dc, openCallback);
|
rtcSetOpenCallback(peer1->dc, openCallback);
|
||||||
rtcSetClosedCallback(peer1->dc, closedCallback);
|
rtcSetClosedCallback(peer1->dc, closedCallback);
|
||||||
rtcSetMessageCallback(peer1->dc, messageCallback);
|
rtcSetMessageCallback(peer1->dc, messageCallback);
|
||||||
@ -187,12 +227,6 @@ int test_capi_connectivity_main() {
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peer1->signalingState != RTC_SIGNALING_STABLE ||
|
|
||||||
peer2->signalingState != RTC_SIGNALING_STABLE) {
|
|
||||||
fprintf(stderr, "Signaling state is not stable\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!peer1->connected || !peer2->connected) {
|
if (!peer1->connected || !peer2->connected) {
|
||||||
fprintf(stderr, "DataChannel is not connected\n");
|
fprintf(stderr, "DataChannel is not connected\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
Reference in New Issue
Block a user