13 #include <p2p/base/basic_packet_socket_factory.h>
14 #include <p2p/base/stun_server.h>
15 #include <p2p/base/turn_server.h>
16 #include <rtc_base/ssl_adapter.h>
17 #include <rtc_base/thread.h>
22 #include <unordered_map>
32 namespace visualization {
33 namespace webrtc_server {
39 "stun:stun.l.google.com:19302"};
44 std::string(
"turn:user:password@34.69") +
".27.100:3478",
45 std::string(
"turn:user:password@34.69") +
".27.100:3478?transport=tcp",
64 if (
const char *env_p = std::getenv(
"WEBRTC_STUN_SERVER")) {
65 return std::string(env_p);
72 if (
const char *env_p = std::getenv(
"WEBRTC_IP")) {
73 return std::string(env_p);
80 if (
const char *env_p = std::getenv(
"WEBRTC_PORT")) {
81 return std::string(env_p);
88 std::unordered_map<WebRTCWindowSystem::OSWindow, std::string>
91 static std::atomic<size_t>
count{0};
107 std::unordered_map<std::string, std::function<std::string(std::string)>>
116 WebRTCWindowSystem::WebRTCWindowSystem()
117 : BitmapWindowSystem(
118 #if !defined(__APPLE__) && !defined(_WIN32) && !defined(_WIN64)
119 BitmapWindowSystem::Rendering::HEADLESS
121 BitmapWindowSystem::Rendering::NORMAL
128 impl_->http_handshake_enabled_ =
true;
132 auto draw_callback = [
this](
const gui::Window *window,
133 std::shared_ptr<core::Tensor> im) ->
void {
140 "MouseEvent", [
this](
const std::string &message) -> std::string {
142 const std::string window_uid =
143 value.get(
"window_uid",
"").asString();
145 if (value.get(
"class_name",
"").asString() ==
"MouseEvent" &&
146 os_window !=
nullptr) {
156 [
this](
const std::string &message) -> std::string {
158 if (value.get(
"class_name",
"").asString() !=
"SyncMouseEvent")
160 value[
"class_name"] =
"MouseEvent";
162 if (!me.FromJson(value))
return "Bad MouseEvent. Ignoring.";
163 for (
const auto &json_window_uid :
164 value.get(
"window_uid_list",
"")) {
165 const auto os_window =
173 "ResizeEvent", [
this](
const std::string &message) -> std::string {
175 const std::string window_uid =
176 value.get(
"window_uid",
"").asString();
178 if (value.get(
"class_name",
"").asString() ==
"ResizeEvent" &&
179 os_window !=
nullptr) {
180 const int height = value.get(
"height", 0).asInt();
181 const int width = value.get(
"width", 0).asInt();
184 "Invalid height {} or width {}, ResizeEvent "
188 return "[CloudViewer WARNING] " + reply;
200 impl_->peer_connection_manager_ =
nullptr;
201 rtc::Thread::Current()->Quit();
214 std::string window_uid = impl_->GenerateUID();
215 impl_->os_window_to_uid_.insert({os_window, window_uid});
221 std::string window_uid = impl_->os_window_to_uid_.at(w);
223 impl_->os_window_to_uid_.erase(w);
224 BitmapWindowSystem::DestroyWindow(w);
229 std::vector<std::string> uids;
230 for (
const auto &it : impl_->os_window_to_uid_) {
231 uids.push_back(it.second);
238 if (impl_->os_window_to_uid_.count(w) == 0) {
239 return "window_undefined";
241 return impl_->os_window_to_uid_.at(w);
246 const std::string &uid)
const {
249 for (
const auto &it : impl_->os_window_to_uid_) {
250 if (it.second == uid) {
258 if (!impl_->sever_started_) {
259 auto start_webrtc_thread = [
this]() {
261 std::string resource_path(
263 impl_->web_root_ = resource_path +
"/html";
267 rtc::LogMessage::LogToDebug((rtc::LoggingSeverity)rtc::LS_ERROR);
269 rtc::LogMessage::LogTimestamps();
270 rtc::LogMessage::LogThreads();
273 rtc::Thread *thread = rtc::Thread::Current();
274 rtc::InitializeSSL();
276 std::list<std::string> ice_servers;
280 std::vector<std::string> custom_servers =
282 ice_servers.insert(ice_servers.end(), custom_servers.begin(),
283 custom_servers.end());
285 ice_servers.insert(ice_servers.end(),
290 impl_->peer_connection_manager_ =
291 std::make_unique<PeerConnectionManager>(
292 ice_servers, config[
"urls"],
".*",
"");
293 if (!impl_->peer_connection_manager_->InitializePeerConnection()) {
298 "Set WEBRTC_STUN_SERVER environment variable add a "
299 "customized WebRTC STUN server.",
300 impl_->http_address_);
305 if (impl_->http_handshake_enabled_) {
307 std::vector<std::string> options{
"document_root",
309 "enable_directory_listing",
312 "X-Frame-Options: SAMEORIGIN",
313 "access_control_allow_origin",
316 impl_->http_address_,
319 "keep_alive_timeout_ms",
326 std::map<std::string,
328 func = impl_->peer_connection_manager_
333 "CloudViewer WebVisualizer is serving at "
335 impl_->http_address_);
337 "Set WEBRTC_IP and WEBRTC_PORT environment "
338 "variable to customize the HTTP server address.",
339 impl_->http_address_);
342 }
catch (
const CivetException &ex) {
352 impl_->webrtc_thread_ = std::thread(start_webrtc_thread);
353 impl_->sever_started_ =
true;
358 const std::string &message) {
360 std::string reply(
"");
363 const std::string class_name = value.get(
"class_name",
"").asString();
364 const std::string window_uid = value.get(
"window_uid",
"").asString();
366 if (impl_->data_channel_message_callbacks_.count(class_name) != 0) {
367 reply = impl_->data_channel_message_callbacks_.at(class_name)(
374 "OnDataChannelMessage: {}. Message cannot be parsed, as "
375 "the class_name {} is invalid.",
376 message, class_name);
378 }
catch (std::exception &e) {
380 "OnDataChannelMessage: {}. Error processing message: {}",
384 "OnDataChannelMessage: {}. Message cannot be parsed, or "
385 "the target GUI event failed to execute.",
389 return "[CloudViewer WARNING] " +
394 const std::string &class_name,
395 const std::function<std::string(
const std::string &)>
callback) {
397 "WebRTCWindowSystem::RegisterDataChannelMessageCallback: {}",
399 impl_->data_channel_message_callbacks_[class_name] =
callback;
403 const std::shared_ptr<core::Tensor> &im) {
404 impl_->peer_connection_manager_->OnFrame(window_uid, im);
409 static const int s_max_initial_frames = 5;
410 static const int s_sleep_between_frames_ms = 100;
412 if (!os_window)
return;
413 for (
int i = 0; os_window !=
nullptr && i < s_max_initial_frames; ++i) {
415 std::this_thread::sleep_for(
416 std::chrono::milliseconds(s_sleep_between_frames_ms));
422 const std::string &query_string,
423 const std::string &data)
const {
426 std::string query_string_trimmed =
"";
427 if (!query_string.empty() && query_string[0] ==
'?') {
428 query_string_trimmed =
429 query_string.substr(1, query_string.length() - 1);
436 if (entry_point ==
"/api/getMediaList") {
438 impl_->peer_connection_manager_->GetMediaList());
439 }
else if (entry_point ==
"/api/getIceServers") {
441 impl_->peer_connection_manager_->GetIceServers());
442 }
else if (entry_point ==
"/api/getIceCandidate") {
444 if (!query_string_trimmed.empty()) {
445 CivetServer::getParam(query_string_trimmed.c_str(),
"peerid",
449 impl_->peer_connection_manager_->GetIceCandidateList(peerid));
450 }
else if (entry_point ==
"/api/hangup") {
452 if (!query_string_trimmed.empty()) {
453 CivetServer::getParam(query_string_trimmed.c_str(),
"peerid",
457 impl_->peer_connection_manager_->HangUp(peerid));
458 }
else if (entry_point ==
"/api/call") {
462 if (!query_string_trimmed.empty()) {
463 CivetServer::getParam(query_string_trimmed.c_str(),
"peerid",
465 CivetServer::getParam(query_string_trimmed.c_str(),
"url", url);
466 CivetServer::getParam(query_string_trimmed.c_str(),
"options",
471 }
else if (entry_point ==
"/api/addIceCandidate") {
473 if (!query_string_trimmed.empty()) {
474 CivetServer::getParam(query_string_trimmed.c_str(),
"peerid",
478 impl_->peer_connection_manager_->AddIceCandidate(
494 impl_->http_handshake_enabled_ =
false;
498 impl_->peer_connection_manager_->CloseWindowConnections(window_uid);
std::function< void(std::shared_ptr< core::Tensor >)> callback
filament::Texture::InternalFormat format
static Application & GetInstance()
void SetWindowSystem(std::shared_ptr< WindowSystem > ws)
void SetOnWindowDraw(OnDrawCallback callback)
void SetWindowSize(OSWindow w, int width, int height) override
void PostRedrawEvent(OSWindow w) override
void PostMouseEvent(OSWindow w, const MouseEvent &e)
WindowSystem::OSWindow GetOSWindow() const
std::function< Json::Value(const struct mg_request_info *req_info, const Json::Value &)> HttpFunction
WebRTCWindowSystem is a BitmapWindowSystem with a WebRTC server that sends video frames to remote cli...
std::string GetWindowUID(OSWindow w) const
std::vector< std::string > GetWindowUIDs() const
List available windows.
OSWindow GetOSWindowByUID(const std::string &uid) const
std::string CallHttpAPI(const std::string &entry_point, const std::string &query_string="", const std::string &data="") const
Call PeerConnectionManager's web request API.
void SendInitFrames(const std::string &window_uid)
virtual ~WebRTCWindowSystem()
std::string OnDataChannelMessage(const std::string &message)
static std::shared_ptr< WebRTCWindowSystem > GetInstance()
void RegisterDataChannelMessageCallback(const std::string &class_name, const std::function< std::string(const std::string &)> callback)
void CloseWindowConnections(const std::string &window_uid)
Close all WebRTC connections that correspond to a Window.
void StartWebRTCServer()
Start WebRTC server in a background thread.
OSWindow CreateOSWindow(gui::Window *o3d_window, int width, int height, const char *title, int flags) override
void OnFrame(const std::string &window_uid, const std::shared_ptr< core::Tensor > &im)
Server -> client frame.
void DisableHttpHandshake()
void DestroyWindow(OSWindow w) override
Helper functions for the ml ops.
void SplitString(std::vector< std::string > &tokens, const std::string &str, const std::string &delimiters=" ", bool trim_empty_str=true)
Json::Value StringToJson(const std::string &json_str)
Parse string and conver to Json::value. Throws exception if the conversion is invalid.
std::string JsonToString(const Json::Value &json)
Serialize a Json::Value to a string.
static std::string GetEnvWebRTCPort()
static const std::list< std::string > s_cloudViewer_ice_servers
static std::string GetEnvWebRTCIP()
static std::string GetCustomSTUNServer()
static const std::list< std::string > s_public_ice_servers
Generic file read and write utility for python interface.
std::string to_string(const T &n)
bool FromJson(const Json::Value &value)
std::unordered_map< std::string, std::function< std::string(std::string)> > data_channel_message_callbacks_
std::unique_ptr< PeerConnectionManager > peer_connection_manager_
std::unordered_map< WebRTCWindowSystem::OSWindow, std::string > os_window_to_uid_
bool http_handshake_enabled_
std::string http_address_
std::string GenerateUID()
std::thread webrtc_thread_