diff --git a/html/webrtc.html b/html/webrtc.html
index 08e6692..4a03b82 100644
--- a/html/webrtc.html
+++ b/html/webrtc.html
@@ -73,6 +73,14 @@
sdpSemantics: 'unified-plan',
iceServers: request.iceServers
});
+
+ pc.addEventListener('datachannel', function(e) {
+ const dc = e.channel;
+ dc.addEventListener('message', function(e) {
+ dc.send('pong');
+ });
+ });
+
pc.remote_pc_id = request.id;
pc.addTransceiver('video', { direction: 'recvonly' });
pc.addEventListener('track', function(evt) {
diff --git a/output/webrtc/webrtc.cc b/output/webrtc/webrtc.cc
index ab36b45..19a1ac9 100644
--- a/output/webrtc/webrtc.cc
+++ b/output/webrtc/webrtc.cc
@@ -37,6 +37,7 @@ using namespace std::chrono_literals;
class Client;
+static pthread_t heartbeat_thread;
static webrtc_options_t *webrtc_options;
static std::set > webrtc_clients;
static std::mutex webrtc_clients_lock;
@@ -95,6 +96,8 @@ public:
}
id = "rtc-" + id;
name = strdup(id.c_str());
+ dc = pc->createDataChannel("pingpong");
+ last_heartbeat_s = get_monotonic_time_us(NULL, NULL) / 1000 / 1000;
}
~Client()
@@ -164,6 +167,7 @@ public:
char *name = NULL;
std::string id;
std::shared_ptr pc;
+ std::shared_ptr dc;
std::shared_ptr video;
std::mutex lock;
std::condition_variable wait_for_complete;
@@ -171,6 +175,7 @@ public:
bool has_set_sdp_answer = false;
bool had_key_frame = false;
bool requested_key_frame = false;
+ uint64_t last_heartbeat_s;
};
std::shared_ptr webrtc_find_client(std::string id)
@@ -260,6 +265,20 @@ static std::shared_ptr webrtc_peer_connection(rtc::Configuration config,
auto client = std::make_shared(pc);
auto wclient = std::weak_ptr(client);
+ client->dc->onOpen([wclient]() {
+ if(auto client = wclient.lock()) {
+ LOG_DEBUG(client.get(), "data channel onOpen");
+ }
+ });
+
+ client->dc->onMessage([wclient](auto message) {
+ auto client = wclient.lock();
+ if(client && std::holds_alternative(message)) {
+ LOG_DEBUG(client.get(), "data channel onMessage: %s", std::get(message).c_str());
+ client->last_heartbeat_s = get_monotonic_time_us(NULL, NULL) / 1000 / 1000;
+ }
+ });
+
pc->onTrack([wclient](std::shared_ptr track) {
if(auto client = wclient.lock()) {
LOG_DEBUG(client.get(), "onTrack: %s", track->mid().c_str());
@@ -471,6 +490,38 @@ static void http_webrtc_remote_candidate(http_worker_t *worker, FILE *stream, co
http_write_response(stream, "200 OK", "application/json", "{}", 0);
}
+static void *heartbeat_checker(void *)
+{
+ uint64_t disconnect = 10;
+ while (true) {
+ {
+ uint64_t now_s = get_monotonic_time_us(NULL, NULL) / 1000 / 1000;
+ std::unique_lock lk(webrtc_clients_lock);
+ auto it = webrtc_clients.begin();
+ while (it != webrtc_clients.end()) {
+ auto client = *it;
+ if (!client) {
+ continue;
+ }
+
+ uint64_t heartbeat_detla = now_s - client->last_heartbeat_s;
+ if (heartbeat_detla >= disconnect) {
+ LOG_INFO(client.get(), "No heartbeat from client, removing.");
+ it = webrtc_clients.erase(it);
+ } else {
+ if (heartbeat_detla > disconnect / 2) {
+ LOG_DEBUG(client.get(), "Checking if client still alive.");
+ client->dc->send("ping");
+ }
+ it++;
+ }
+ }
+ }
+ sleep(1);
+ }
+ return NULL;
+}
+
extern "C" void http_webrtc_offer(http_worker_t *worker, FILE *stream)
{
auto message = http_parse_json_body(worker, stream, webrtc_client_max_json_body);
@@ -507,6 +558,8 @@ extern "C" int webrtc_server(webrtc_options_t *options)
buffer_lock_register_check_streaming(&video_lock, webrtc_h264_needs_buffer);
buffer_lock_register_notify_buffer(&video_lock, webrtc_h264_capture);
+
+ pthread_create(&heartbeat_thread, NULL, heartbeat_checker, NULL);
options->running = true;
return 0;
}