websocket-server-2pass.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /**
  2. * Copyright FunASR (https://github.com/alibaba-damo-academy/FunASR). All Rights
  3. * Reserved. MIT License (https://opensource.org/licenses/MIT)
  4. */
  5. /* 2022-2023 by zhaomingwork */
  6. // websocket server for asr engine
  7. // take some ideas from https://github.com/k2-fsa/sherpa-onnx
  8. // online-websocket-server-impl.cc, thanks. The websocket server has two threads
  9. // pools, one for handle network data and one for asr decoder.
  10. // now only support offline engine.
  11. #include "websocket-server-2pass.h"
  12. #include <thread>
  13. #include <utility>
  14. #include <vector>
  15. extern std::unordered_map<std::string, int> hws_map_;
  16. extern int fst_inc_wts_;
  17. extern float global_beam_, lattice_beam_, am_scale_;
  18. context_ptr WebSocketServer::on_tls_init(tls_mode mode,
  19. websocketpp::connection_hdl hdl,
  20. std::string& s_certfile,
  21. std::string& s_keyfile) {
  22. namespace asio = websocketpp::lib::asio;
  23. LOG(INFO) << "on_tls_init called with hdl: " << hdl.lock().get();
  24. LOG(INFO) << "using TLS mode: "
  25. << (mode == MOZILLA_MODERN ? "Mozilla Modern"
  26. : "Mozilla Intermediate");
  27. context_ptr ctx = websocketpp::lib::make_shared<asio::ssl::context>(
  28. asio::ssl::context::sslv23);
  29. try {
  30. if (mode == MOZILLA_MODERN) {
  31. // Modern disables TLSv1
  32. ctx->set_options(
  33. asio::ssl::context::default_workarounds |
  34. asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3 |
  35. asio::ssl::context::no_tlsv1 | asio::ssl::context::single_dh_use);
  36. } else {
  37. ctx->set_options(asio::ssl::context::default_workarounds |
  38. asio::ssl::context::no_sslv2 |
  39. asio::ssl::context::no_sslv3 |
  40. asio::ssl::context::single_dh_use);
  41. }
  42. ctx->use_certificate_chain_file(s_certfile);
  43. ctx->use_private_key_file(s_keyfile, asio::ssl::context::pem);
  44. } catch (std::exception& e) {
  45. LOG(INFO) << "Exception: " << e.what();
  46. }
  47. return ctx;
  48. }
  49. nlohmann::json handle_result(FUNASR_RESULT result) {
  50. websocketpp::lib::error_code ec;
  51. nlohmann::json jsonresult;
  52. jsonresult["text"] = "";
  53. std::string tmp_online_msg = FunASRGetResult(result, 0);
  54. if (tmp_online_msg != "") {
  55. LOG(INFO) << "online_res :" << tmp_online_msg;
  56. jsonresult["text"] = tmp_online_msg;
  57. jsonresult["mode"] = "2pass-online";
  58. }
  59. std::string tmp_tpass_msg = FunASRGetTpassResult(result, 0);
  60. if (tmp_tpass_msg != "") {
  61. LOG(INFO) << "offline results : " << tmp_tpass_msg;
  62. jsonresult["text"] = tmp_tpass_msg;
  63. jsonresult["mode"] = "2pass-offline";
  64. }
  65. std::string tmp_stamp_msg = FunASRGetStamp(result);
  66. if (tmp_stamp_msg != "") {
  67. LOG(INFO) << "offline stamps : " << tmp_stamp_msg;
  68. jsonresult["timestamp"] = tmp_stamp_msg;
  69. }
  70. std::string tmp_stamp_sents = FunASRGetStampSents(result);
  71. if (tmp_stamp_sents != "") {
  72. LOG(INFO) << "offline stamp_sents : " << tmp_stamp_sents;
  73. jsonresult["stamp_sents"] = tmp_stamp_sents;
  74. }
  75. return jsonresult;
  76. }
  77. // feed buffer to asr engine for decoder
  78. void WebSocketServer::do_decoder(
  79. std::vector<char>& buffer,
  80. websocketpp::connection_hdl& hdl,
  81. nlohmann::json& msg,
  82. std::vector<std::vector<std::string>>& punc_cache,
  83. std::vector<std::vector<float>> &hotwords_embedding,
  84. websocketpp::lib::mutex& thread_lock,
  85. bool& is_final,
  86. std::string wav_name,
  87. std::string modetype,
  88. bool itn,
  89. int audio_fs,
  90. std::string wav_format,
  91. FUNASR_HANDLE& tpass_online_handle,
  92. FUNASR_DEC_HANDLE& decoder_handle) {
  93. // lock for each connection
  94. if(!tpass_online_handle){
  95. scoped_lock guard(thread_lock);
  96. LOG(INFO) << "tpass_online_handle is free, return";
  97. msg["access_num"]=(int)msg["access_num"]-1;
  98. return;
  99. }
  100. try {
  101. FUNASR_RESULT Result = nullptr;
  102. int asr_mode_ = 2;
  103. if (modetype == "offline") {
  104. asr_mode_ = 0;
  105. } else if (modetype == "online") {
  106. asr_mode_ = 1;
  107. } else if (modetype == "2pass") {
  108. asr_mode_ = 2;
  109. }
  110. while (buffer.size() >= 800 * 2 && !msg["is_eof"]) {
  111. std::vector<char> subvector = {buffer.begin(), buffer.begin() + 800 * 2};
  112. buffer.erase(buffer.begin(), buffer.begin() + 800 * 2);
  113. try {
  114. if (tpass_online_handle) {
  115. Result = FunTpassInferBuffer(tpass_handle, tpass_online_handle,
  116. subvector.data(), subvector.size(),
  117. punc_cache, false, audio_fs,
  118. wav_format, (ASR_TYPE)asr_mode_,
  119. hotwords_embedding, itn, decoder_handle);
  120. } else {
  121. scoped_lock guard(thread_lock);
  122. msg["access_num"]=(int)msg["access_num"]-1;
  123. return;
  124. }
  125. } catch (std::exception const& e) {
  126. scoped_lock guard(thread_lock);
  127. LOG(ERROR) << e.what();
  128. msg["access_num"]=(int)msg["access_num"]-1;
  129. return;
  130. }
  131. if (Result) {
  132. websocketpp::lib::error_code ec;
  133. nlohmann::json jsonresult = handle_result(Result);
  134. jsonresult["wav_name"] = wav_name;
  135. jsonresult["is_final"] = false;
  136. if (jsonresult["text"] != "") {
  137. if (is_ssl) {
  138. wss_server_->send(hdl, jsonresult.dump(),
  139. websocketpp::frame::opcode::text, ec);
  140. } else {
  141. server_->send(hdl, jsonresult.dump(),
  142. websocketpp::frame::opcode::text, ec);
  143. }
  144. }
  145. FunASRFreeResult(Result);
  146. }
  147. }
  148. if (is_final && !msg["is_eof"]) {
  149. try {
  150. if (tpass_online_handle) {
  151. Result = FunTpassInferBuffer(tpass_handle, tpass_online_handle,
  152. buffer.data(), buffer.size(), punc_cache,
  153. is_final, audio_fs,
  154. wav_format, (ASR_TYPE)asr_mode_,
  155. hotwords_embedding, itn, decoder_handle);
  156. } else {
  157. scoped_lock guard(thread_lock);
  158. msg["access_num"]=(int)msg["access_num"]-1;
  159. return;
  160. }
  161. } catch (std::exception const& e) {
  162. scoped_lock guard(thread_lock);
  163. LOG(ERROR) << e.what();
  164. msg["access_num"]=(int)msg["access_num"]-1;
  165. return;
  166. }
  167. if(punc_cache.size()>0){
  168. for (auto& vec : punc_cache) {
  169. vec.clear();
  170. }
  171. }
  172. if (Result) {
  173. websocketpp::lib::error_code ec;
  174. nlohmann::json jsonresult = handle_result(Result);
  175. jsonresult["wav_name"] = wav_name;
  176. jsonresult["is_final"] = true;
  177. if (is_ssl) {
  178. wss_server_->send(hdl, jsonresult.dump(),
  179. websocketpp::frame::opcode::text, ec);
  180. } else {
  181. server_->send(hdl, jsonresult.dump(),
  182. websocketpp::frame::opcode::text, ec);
  183. }
  184. FunASRFreeResult(Result);
  185. }else{
  186. if(wav_format != "pcm" && wav_format != "PCM"){
  187. websocketpp::lib::error_code ec;
  188. nlohmann::json jsonresult;
  189. jsonresult["text"] = "ERROR. Real-time transcription service ONLY SUPPORT wav_format pcm.";
  190. jsonresult["wav_name"] = wav_name;
  191. jsonresult["is_final"] = true;
  192. if (is_ssl) {
  193. wss_server_->send(hdl, jsonresult.dump(),
  194. websocketpp::frame::opcode::text, ec);
  195. } else {
  196. server_->send(hdl, jsonresult.dump(),
  197. websocketpp::frame::opcode::text, ec);
  198. }
  199. }
  200. }
  201. }
  202. } catch (std::exception const& e) {
  203. std::cerr << "Error: " << e.what() << std::endl;
  204. }
  205. scoped_lock guard(thread_lock);
  206. msg["access_num"]=(int)msg["access_num"]-1;
  207. }
  208. void WebSocketServer::on_open(websocketpp::connection_hdl hdl) {
  209. scoped_lock guard(m_lock); // for threads safty
  210. try{
  211. std::shared_ptr<FUNASR_MESSAGE> data_msg =
  212. std::make_shared<FUNASR_MESSAGE>(); // put a new data vector for new
  213. // connection
  214. data_msg->samples = std::make_shared<std::vector<char>>();
  215. data_msg->thread_lock = std::make_shared<websocketpp::lib::mutex>();
  216. data_msg->msg = nlohmann::json::parse("{}");
  217. data_msg->msg["wav_format"] = "pcm";
  218. data_msg->msg["wav_name"] = "wav-default-id";
  219. data_msg->msg["mode"] = "2pass";
  220. data_msg->msg["itn"] = true;
  221. data_msg->msg["audio_fs"] = 16000; // default is 16k
  222. data_msg->msg["access_num"] = 0; // the number of access for this object, when it is 0, we can free it saftly
  223. data_msg->msg["is_eof"]=false; // if this connection is closed
  224. FUNASR_DEC_HANDLE decoder_handle =
  225. FunASRWfstDecoderInit(tpass_handle, ASR_TWO_PASS, global_beam_, lattice_beam_, am_scale_);
  226. data_msg->decoder_handle = decoder_handle;
  227. data_msg->punc_cache =
  228. std::make_shared<std::vector<std::vector<std::string>>>(2);
  229. data_msg->strand_ = std::make_shared<asio::io_context::strand>(io_decoder_);
  230. data_map.emplace(hdl, data_msg);
  231. }catch (std::exception const& e) {
  232. std::cerr << "Error: " << e.what() << std::endl;
  233. }
  234. }
  235. void remove_hdl(
  236. websocketpp::connection_hdl hdl,
  237. std::map<websocketpp::connection_hdl, std::shared_ptr<FUNASR_MESSAGE>,
  238. std::owner_less<websocketpp::connection_hdl>>& data_map) {
  239. std::shared_ptr<FUNASR_MESSAGE> data_msg = nullptr;
  240. auto it_data = data_map.find(hdl);
  241. if (it_data != data_map.end()) {
  242. data_msg = it_data->second;
  243. } else {
  244. return;
  245. }
  246. // scoped_lock guard_decoder(*(data_msg->thread_lock)); //wait for do_decoder
  247. // finished and avoid access freed tpass_online_handle
  248. unique_lock guard_decoder(*(data_msg->thread_lock));
  249. if (data_msg->msg["access_num"]==0 && data_msg->msg["is_eof"]==true) {
  250. FunWfstDecoderUnloadHwsRes(data_msg->decoder_handle);
  251. FunASRWfstDecoderUninit(data_msg->decoder_handle);
  252. data_msg->decoder_handle = nullptr;
  253. FunTpassOnlineUninit(data_msg->tpass_online_handle);
  254. data_msg->tpass_online_handle = nullptr;
  255. data_map.erase(hdl);
  256. }
  257. guard_decoder.unlock();
  258. }
  259. void WebSocketServer::on_close(websocketpp::connection_hdl hdl) {
  260. scoped_lock guard(m_lock);
  261. std::shared_ptr<FUNASR_MESSAGE> data_msg = nullptr;
  262. auto it_data = data_map.find(hdl);
  263. if (it_data != data_map.end()) {
  264. data_msg = it_data->second;
  265. } else {
  266. return;
  267. }
  268. unique_lock guard_decoder(*(data_msg->thread_lock));
  269. data_msg->msg["is_eof"]=true;
  270. guard_decoder.unlock();
  271. }
  272. // remove closed connection
  273. void WebSocketServer::check_and_clean_connection() {
  274. while(true){
  275. std::this_thread::sleep_for(std::chrono::milliseconds(5000));
  276. std::vector<websocketpp::connection_hdl> to_remove; // remove list
  277. auto iter = data_map.begin();
  278. while (iter != data_map.end()) { // loop to find closed connection
  279. websocketpp::connection_hdl hdl = iter->first;
  280. try{
  281. if (is_ssl) {
  282. wss_server::connection_ptr con = wss_server_->get_con_from_hdl(hdl);
  283. if (con->get_state() != 1) { // session::state::open ==1
  284. to_remove.push_back(hdl);
  285. }
  286. } else {
  287. server::connection_ptr con = server_->get_con_from_hdl(hdl);
  288. if (con->get_state() != 1) { // session::state::open ==1
  289. to_remove.push_back(hdl);
  290. }
  291. }
  292. }
  293. catch (std::exception const &e)
  294. {
  295. // if connection is close, we set is_eof = true
  296. std::shared_ptr<FUNASR_MESSAGE> data_msg = nullptr;
  297. auto it_data = data_map.find(hdl);
  298. if (it_data != data_map.end()) {
  299. data_msg = it_data->second;
  300. } else {
  301. continue;
  302. }
  303. unique_lock guard_decoder(*(data_msg->thread_lock));
  304. data_msg->msg["is_eof"]=true;
  305. guard_decoder.unlock();
  306. to_remove.push_back(hdl);
  307. LOG(INFO)<<"connection is closed.";
  308. }
  309. iter++;
  310. }
  311. for (auto hdl : to_remove) {
  312. {
  313. unique_lock lock(m_lock);
  314. remove_hdl(hdl, data_map);
  315. }
  316. }
  317. }
  318. }
  319. void WebSocketServer::on_message(websocketpp::connection_hdl hdl,
  320. message_ptr msg) {
  321. unique_lock lock(m_lock);
  322. // find the sample data vector according to one connection
  323. std::shared_ptr<FUNASR_MESSAGE> msg_data = nullptr;
  324. auto it_data = data_map.find(hdl);
  325. if (it_data != data_map.end()) {
  326. msg_data = it_data->second;
  327. if(msg_data->msg["is_eof"]){
  328. lock.unlock();
  329. return;
  330. }
  331. } else {
  332. lock.unlock();
  333. return;
  334. }
  335. std::shared_ptr<std::vector<char>> sample_data_p = msg_data->samples;
  336. std::shared_ptr<std::vector<std::vector<std::string>>> punc_cache_p =
  337. msg_data->punc_cache;
  338. std::shared_ptr<websocketpp::lib::mutex> thread_lock_p = msg_data->thread_lock;
  339. lock.unlock();
  340. if (sample_data_p == nullptr) {
  341. LOG(INFO) << "error when fetch sample data vector";
  342. return;
  343. }
  344. const std::string& payload = msg->get_payload(); // get msg type
  345. unique_lock guard_decoder(*(thread_lock_p)); // mutex for one connection
  346. switch (msg->get_opcode()) {
  347. case websocketpp::frame::opcode::text: {
  348. nlohmann::json jsonresult;
  349. try{
  350. jsonresult = nlohmann::json::parse(payload);
  351. }catch (std::exception const &e)
  352. {
  353. LOG(ERROR)<<e.what();
  354. msg_data->msg["is_eof"]=true;
  355. guard_decoder.unlock();
  356. return;
  357. }
  358. if (jsonresult.contains("wav_name")) {
  359. msg_data->msg["wav_name"] = jsonresult["wav_name"];
  360. }
  361. if (jsonresult.contains("mode")) {
  362. msg_data->msg["mode"] = jsonresult["mode"];
  363. }
  364. if (jsonresult.contains("wav_format")) {
  365. msg_data->msg["wav_format"] = jsonresult["wav_format"];
  366. }
  367. // hotwords: fst/nn
  368. if(msg_data->hotwords_embedding == NULL){
  369. std::unordered_map<std::string, int> merged_hws_map;
  370. std::string nn_hotwords = "";
  371. if (jsonresult["hotwords"] != nullptr) {
  372. std::string json_string = jsonresult["hotwords"];
  373. if (!json_string.empty()){
  374. nlohmann::json json_fst_hws;
  375. try{
  376. json_fst_hws = nlohmann::json::parse(json_string);
  377. if(json_fst_hws.type() == nlohmann::json::value_t::object){
  378. // fst
  379. try{
  380. std::unordered_map<std::string, int> client_hws_map = json_fst_hws;
  381. merged_hws_map.insert(client_hws_map.begin(), client_hws_map.end());
  382. } catch (const std::exception& e) {
  383. LOG(INFO) << e.what();
  384. }
  385. }
  386. } catch (std::exception const &e)
  387. {
  388. LOG(ERROR)<<e.what();
  389. // nn
  390. std::string client_nn_hws = jsonresult["hotwords"];
  391. nn_hotwords += " " + client_nn_hws;
  392. // LOG(INFO) << "nn hotwords: " << client_nn_hws;
  393. }
  394. }
  395. }
  396. merged_hws_map.insert(hws_map_.begin(), hws_map_.end());
  397. // fst
  398. LOG(INFO) << "hotwords: ";
  399. for (const auto& pair : merged_hws_map) {
  400. nn_hotwords += " " + pair.first;
  401. LOG(INFO) << pair.first << " : " << pair.second;
  402. }
  403. FunWfstDecoderLoadHwsRes(msg_data->decoder_handle, fst_inc_wts_, merged_hws_map);
  404. // nn
  405. std::vector<std::vector<float>> new_hotwords_embedding = CompileHotwordEmbedding(tpass_handle, nn_hotwords, ASR_TWO_PASS);
  406. msg_data->hotwords_embedding =
  407. std::make_shared<std::vector<std::vector<float>>>(new_hotwords_embedding);
  408. }
  409. if (jsonresult.contains("audio_fs")) {
  410. msg_data->msg["audio_fs"] = jsonresult["audio_fs"];
  411. }
  412. if (jsonresult.contains("chunk_size")) {
  413. if (msg_data->tpass_online_handle == NULL) {
  414. std::vector<int> chunk_size_vec =
  415. jsonresult["chunk_size"].get<std::vector<int>>();
  416. // check chunk_size_vec
  417. if(chunk_size_vec.size() == 3 && chunk_size_vec[1] != 0){
  418. FUNASR_HANDLE tpass_online_handle =
  419. FunTpassOnlineInit(tpass_handle, chunk_size_vec);
  420. msg_data->tpass_online_handle = tpass_online_handle;
  421. }else{
  422. LOG(ERROR) << "Wrong chunk_size!";
  423. break;
  424. }
  425. }
  426. }
  427. if (jsonresult.contains("itn")) {
  428. msg_data->msg["itn"] = jsonresult["itn"];
  429. }
  430. LOG(INFO) << "jsonresult=" << jsonresult
  431. << ", msg_data->msg=" << msg_data->msg;
  432. if ((jsonresult["is_speaking"] == false ||
  433. jsonresult["is_finished"] == true) &&
  434. msg_data->msg["is_eof"] != true &&
  435. msg_data->hotwords_embedding != NULL) {
  436. LOG(INFO) << "client done";
  437. // if it is in final message, post the sample_data to decode
  438. try{
  439. std::vector<std::vector<float>> hotwords_embedding_(*(msg_data->hotwords_embedding));
  440. msg_data->strand_->post(
  441. std::bind(&WebSocketServer::do_decoder, this,
  442. std::move(*(sample_data_p.get())), std::move(hdl),
  443. std::ref(msg_data->msg), std::ref(*(punc_cache_p.get())),
  444. std::move(hotwords_embedding_),
  445. std::ref(*thread_lock_p), std::move(true),
  446. msg_data->msg["wav_name"],
  447. msg_data->msg["mode"],
  448. msg_data->msg["itn"],
  449. msg_data->msg["audio_fs"],
  450. msg_data->msg["wav_format"],
  451. std::ref(msg_data->tpass_online_handle),
  452. std::ref(msg_data->decoder_handle)));
  453. msg_data->msg["access_num"]=(int)(msg_data->msg["access_num"])+1;
  454. }
  455. catch (std::exception const &e)
  456. {
  457. LOG(ERROR)<<e.what();
  458. }
  459. }
  460. break;
  461. }
  462. case websocketpp::frame::opcode::binary: {
  463. // recived binary data
  464. const auto* pcm_data = static_cast<const char*>(payload.data());
  465. int32_t num_samples = payload.size();
  466. if (isonline) {
  467. sample_data_p->insert(sample_data_p->end(), pcm_data,
  468. pcm_data + num_samples);
  469. int setpsize =
  470. 800 * 2; // TODO, need get from client
  471. // if sample_data size > setpsize, we post data to decode
  472. if (sample_data_p->size() > setpsize) {
  473. int chunksize = floor(sample_data_p->size() / setpsize);
  474. // make sure the subvector size is an integer multiple of setpsize
  475. std::vector<char> subvector = {
  476. sample_data_p->begin(),
  477. sample_data_p->begin() + chunksize * setpsize};
  478. // keep remain in sample_data
  479. sample_data_p->erase(sample_data_p->begin(),
  480. sample_data_p->begin() + chunksize * setpsize);
  481. try{
  482. // post to decode
  483. if (msg_data->msg["is_eof"] != true && msg_data->hotwords_embedding != NULL) {
  484. std::vector<std::vector<float>> hotwords_embedding_(*(msg_data->hotwords_embedding));
  485. msg_data->strand_->post(
  486. std::bind(&WebSocketServer::do_decoder, this,
  487. std::move(subvector), std::move(hdl),
  488. std::ref(msg_data->msg),
  489. std::ref(*(punc_cache_p.get())),
  490. std::move(hotwords_embedding_),
  491. std::ref(*thread_lock_p), std::move(false),
  492. msg_data->msg["wav_name"],
  493. msg_data->msg["mode"],
  494. msg_data->msg["itn"],
  495. msg_data->msg["audio_fs"],
  496. msg_data->msg["wav_format"],
  497. std::ref(msg_data->tpass_online_handle),
  498. std::ref(msg_data->decoder_handle)));
  499. msg_data->msg["access_num"]=(int)(msg_data->msg["access_num"])+1;
  500. }
  501. }
  502. catch (std::exception const &e)
  503. {
  504. LOG(ERROR)<<e.what();
  505. }
  506. }
  507. } else {
  508. sample_data_p->insert(sample_data_p->end(), pcm_data,
  509. pcm_data + num_samples);
  510. }
  511. break;
  512. }
  513. default:
  514. break;
  515. }
  516. guard_decoder.unlock();
  517. }
  518. // init asr model
  519. void WebSocketServer::initAsr(std::map<std::string, std::string>& model_path,
  520. int thread_num) {
  521. try {
  522. tpass_handle = FunTpassInit(model_path, thread_num);
  523. if (!tpass_handle) {
  524. LOG(ERROR) << "FunTpassInit init failed";
  525. exit(-1);
  526. }
  527. LOG(INFO) << "initAsr run check_and_clean_connection";
  528. std::thread clean_thread(&WebSocketServer::check_and_clean_connection,this);
  529. clean_thread.detach();
  530. LOG(INFO) << "initAsr run check_and_clean_connection finished";
  531. } catch (const std::exception& e) {
  532. LOG(INFO) << e.what();
  533. }
  534. }