From a3ed021e65cb5ccd721bba5853f18f7ff5507260 Mon Sep 17 00:00:00 2001 From: Maye Date: Mon, 13 Nov 2023 20:13:04 +0800 Subject: [PATCH] fix live crash (#232) * Fix error handling in get_live_s function * Remove unnecessary cout statement and replace with logger * Fix error handling in getDanmuInfo and extract_messages functions. * fix live crash * use getResultAsync and update wait time --- wiliwili/include/api/live/danmaku_live.hpp | 28 ++- .../source/activity/live_player_activity.cpp | 35 ++- wiliwili/source/api/danmaku_live.cpp | 205 +++++++++--------- wiliwili/source/api/util/extract_messages.cpp | 10 +- 4 files changed, 148 insertions(+), 130 deletions(-) diff --git a/wiliwili/include/api/live/danmaku_live.hpp b/wiliwili/include/api/live/danmaku_live.hpp index 60770435..8818fd38 100644 --- a/wiliwili/include/api/live/danmaku_live.hpp +++ b/wiliwili/include/api/live/danmaku_live.hpp @@ -9,12 +9,30 @@ #include #include #include -#include #include #include #include "mongoose.h" +#include +#include +using json = nlohmann::json; + +class LiveDanmakuHostinfo { +public: + std::string host; + int ws_port; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LiveDanmakuHostinfo, host, ws_port); + +class LiveDanmakuinfo { +public: + std::vector host_list; + std::string token = ""; +}; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LiveDanmakuinfo, host_list, token); + +typedef void (*on_message_func_t)(const std::string &); class LiveDanmaku : public brls::Singleton { public: int room_id; @@ -26,11 +44,11 @@ class LiveDanmaku : public brls::Singleton { void send_heartbeat(); void send_text_message(const std::string &message); - void setonMessage(std::function func); - std::function onMessage; + void setonMessage(on_message_func_t func); + on_message_func_t onMessage = nullptr; void set_wait_time(size_t time); - size_t wait_time = 800; + size_t wait_time = 100; LiveDanmaku(); ~LiveDanmaku(); @@ -40,6 +58,8 @@ class LiveDanmaku : public brls::Singleton { bool is_evOK(); std::atomic_bool ms_ev_ok{false}; + LiveDanmakuinfo info; + std::thread mongoose_thread; std::thread task_thread; std::mutex mongoose_mutex; diff --git a/wiliwili/source/activity/live_player_activity.cpp b/wiliwili/source/activity/live_player_activity.cpp index 3983bb6b..3eff6a49 100644 --- a/wiliwili/source/activity/live_player_activity.cpp +++ b/wiliwili/source/activity/live_player_activity.cpp @@ -36,8 +36,7 @@ static void process_danmaku(const std::vector& danmaku_list) { // danmaku_t_free(dan); } -static void onDanmakuReceived(std::string&& message) { - const std::string& msg = message; +static void onDanmakuReceived(const std::string& msg) { std::vector payload(msg.begin(), msg.end()); std::vector messages = parse_packet(payload); @@ -62,7 +61,7 @@ static void onDanmakuReceived(std::string&& message) { } static void showDialog(const std::string& msg, const std::string& pic, - bool forceQuit) { + bool forceQuit) { brls::Dialog* dialog; if (pic.empty()) { dialog = new brls::Dialog(msg); @@ -90,14 +89,10 @@ static void showDialog(const std::string& msg, const std::string& pic, dialog->open(); } - LiveActivity::LiveActivity(const bilibili::LiveVideoResult& live) : liveData(live) { brls::Logger::debug("LiveActivity: create: {}", live.roomid); this->setCommonData(); - GA("open_live", {{"id", std::to_string(live.roomid)}}) - GA("open_live", {{"live_id", std::to_string(live.roomid)}}) - LiveDanmaku::instance().setonMessage(onDanmakuReceived); } LiveActivity::LiveActivity(int roomid, const std::string& name, @@ -107,12 +102,13 @@ LiveActivity::LiveActivity(int roomid, const std::string& name, this->liveData.title = name; this->liveData.watched_show.text_large = views; this->setCommonData(); - GA("open_live", {{"id", std::to_string(roomid)}}) - GA("open_live", {{"live_id", std::to_string(roomid)}}) - LiveDanmaku::instance().setonMessage(onDanmakuReceived); } void LiveActivity::setCommonData() { + GA("open_live", {{"id", std::to_string(this->liveData.roomid)}}) + GA("open_live", {{"live_id", std::to_string(this->liveData.roomid)}}) + + LiveDanmaku::instance().setonMessage(onDanmakuReceived); LiveDanmaku::instance().connect( liveData.roomid, std::stoll(ProgramConfig::instance().getUserID())); @@ -261,10 +257,9 @@ void LiveActivity::onLiveData(const bilibili::LiveRoomPlayInfo& result) { if (result.is_locked) { brls::Logger::error("LiveActivity: live {} is locked", result.room_id); this->video->showOSD(false); - showDialog( - fmt::format("这个房间已经被封禁(至 {})!(╯°口°)╯(┴—┴", - wiliwili::sec2FullDate(result.lock_till)), - "pictures/room-block.png", true); + showDialog(fmt::format("这个房间已经被封禁(至 {})!(╯°口°)╯(┴—┴", + wiliwili::sec2FullDate(result.lock_till)), + "pictures/room-block.png", true); return; } // 0: 未开播 1: 直播中 2: 轮播中 @@ -306,17 +301,17 @@ void LiveActivity::onError(const std::string& error) { } void LiveActivity::onNeedPay(const std::string& msg, const std::string& link, - const std::string& startTime, - const std::string& endTime) { + const std::string& startTime, + const std::string& endTime) { if (link.empty()) { showDialog(msg, "", true); return; } - auto box = new brls::Box(); - auto img = new QRImage(); - auto label = new brls::Label(); - auto header = new brls::Label(); + auto box = new brls::Box(); + auto img = new QRImage(); + auto label = new brls::Label(); + auto header = new brls::Label(); auto subtitle = new brls::Label(); header->setFontSize(24); header->setMargins(10, 0, 20, 0); diff --git a/wiliwili/source/api/danmaku_live.cpp b/wiliwili/source/api/danmaku_live.cpp index 15431c5b..54b4f851 100644 --- a/wiliwili/source/api/danmaku_live.cpp +++ b/wiliwili/source/api/danmaku_live.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -17,40 +16,34 @@ #include #endif -#include - -using json = nlohmann::json; - -static std::string url = "ws://broadcastlv.chat.bilibili.com:2244/sub"; -static std::string key = ""; - -static void get_live_s(int room_id) { - auto res = bilibili::HTTP::get( - "https://api.live.bilibili.com/xlive/web-room/v1/index/" - "getDanmuInfo?type=0&id=" + - std::to_string(room_id)); - - if (res.status_code != 200) { - brls::Logger::error("getDanmuInfo error:{}", res.status_code); - } else { - json _json; - try { - _json = json::parse(res.text); - } catch (const std::exception &e) { - std::cout << "getDanmuInfo json parse error" << std::endl; - } - if (_json["code"].get() == 0) { - // url = "ws://" + - // _json["data"]["host_list"][0]["host"] - // .get_ref() + - // ":" + - // std::to_string( - // _json["data"]["host_list"][0]["ws_port"].get()) + - // "/sub"; - key = _json["data"]["token"].get_ref(); - } - } -} +// static void get_live_s(int room_id) { +// auto res = bilibili::HTTP::get( +// "https://api.live.bilibili.com/xlive/web-room/v1/index/" +// "getDanmuInfo?type=0&id=" + +// std::to_string(room_id)); + +// if (res.status_code != 200) { +// brls::Logger::error("getDanmuInfo error:{}", res.status_code); +// } else { +// json _json; +// try { +// _json = json::parse(res.text); +// } catch (...) { +// brls::Logger::error("getDanmuInfo json parse error"); +// return; +// } +// if (_json["code"].get() == 0) { +// // url = "ws://" + +// // _json["data"]["host_list"][0]["host"] +// // .get_ref() + +// // ":" + +// // std::to_string( +// // _json["data"]["host_list"][0]["ws_port"].get()) + +// // "/sub"; +// key = _json["data"]["token"].get_ref(); +// } +// } +// } static void mongoose_event_handler(struct mg_connection *nc, int ev, void *ev_data, void *user_data); @@ -62,23 +55,13 @@ static void heartbeat_timer(void *param) { } } -typedef struct task { - // 函数 - const std::function onMessage; - // 参数 - std::string arg; - // 优先级,暂时都设置0 - int priority; -} task; - -static std::queue task_q; +static std::queue msg_q; static std::condition_variable cv; -static std::mutex task_mutex; +static std::mutex msg_q_mutex; -static void add_task(const std::function &func, - std::string &&a) { - std::lock_guard lock(task_mutex); - task_q.emplace(task{func, a, 0}); +static void add_msg(std::string &&a) { + std::lock_guard lock(msg_q_mutex); + msg_q.emplace(std::move(a)); cv.notify_one(); } @@ -112,56 +95,75 @@ void LiveDanmaku::connect(int room_id, int64_t uid) { return; } - get_live_s(room_id); - + // get_live_s(room_id); mg_log_set(MG_LL_NONE); - mg_mgr_init(this->mgr); - this->nc = mg_ws_connect(this->mgr, url.c_str(), mongoose_event_handler, - this, nullptr); - - if (this->nc == nullptr) { - std::cout << "nc is null" << std::endl; - disconnect(); - mg_mgr_free(this->mgr); - delete this->mgr; - return; - } - - this->room_id = room_id; - this->uid = uid; - //mg_mgr_poll(this->mgr, 10); + bilibili::HTTP::getResultAsync( + "https://api.live.bilibili.com/xlive/web-room/v1/index/" + "getDanmuInfo?type=0&id=" + + std::to_string(room_id), + {}, + [this, room_id, uid](const LiveDanmakuinfo &info) { + this->info.host_list = std::move(info.host_list); + this->info.token = std::move(info.token); + mg_mgr_init(this->mgr); + + std::string host = + "ws://" + + this->info.host_list[this->info.host_list.size() - 1].host + + ":" + + std::to_string( + this->info.host_list[this->info.host_list.size() - 1] + .ws_port) + + "/sub"; + this->nc = mg_ws_connect(this->mgr, host.c_str(), + mongoose_event_handler, this, nullptr); - // Start Mongoose event loop and heartbeat thread - mongoose_thread = std::thread([this]() { - while (this->is_connected()) { - this->mongoose_mutex.lock(); if (this->nc == nullptr) { - break; + brls::Logger::error("(LiveDanmaku) nc is null"); + this->disconnect(); + mg_mgr_free(this->mgr); + delete this->mgr; + return; } - this->mongoose_mutex.unlock(); - mg_mgr_poll(this->mgr, this->wait_time); - } - mg_mgr_free(this->mgr); - delete this->mgr; - }); - - task_thread = std::thread([this]() { - while (true) { - std::unique_lock lock(task_mutex); - cv.wait(lock, [this] { - return !task_q.empty() or !this->is_connected(); + + this->room_id = room_id; + this->uid = uid; + + // Start Mongoose event loop and heartbeat thread + this->mongoose_thread = std::thread([this]() { + while (this->is_connected()) { + this->mongoose_mutex.lock(); + if (this->nc == nullptr) { + break; + } + this->mongoose_mutex.unlock(); + mg_mgr_poll(this->mgr, this->wait_time); + } + mg_mgr_free(this->mgr); + delete this->mgr; }); - if (!this->is_connected()) break; - auto task = task_q.front(); - task_q.pop(); - lock.unlock(); - task.onMessage(std::move(task.arg)); - } - }); + this->task_thread = std::thread([this]() { + while (true) { + std::unique_lock lock(msg_q_mutex); + cv.wait(lock, [this] { + return !msg_q.empty() or !this->is_connected(); + }); + if (!this->is_connected()) break; + auto msg = std::move(msg_q.front()); + msg_q.pop(); + lock.unlock(); + + this->onMessage(msg); + } + }); - brls::Logger::info("(LiveDanmaku) connect step finish"); + brls::Logger::info("(LiveDanmaku) connect step finish"); + }, + [this](const std::string &code) { + brls::Logger::error("getDanmuInfo error:{}", code); + }); } void LiveDanmaku::disconnect() { @@ -194,11 +196,13 @@ bool LiveDanmaku::is_connected() { bool LiveDanmaku::is_evOK() { return ms_ev_ok.load(std::memory_order_acquire); } void LiveDanmaku::send_join_request(const int room_id, const int64_t uid) { - json join_request = { - {"uid", uid}, {"roomid", room_id}, - {"protover", 2}, {"buvid", ProgramConfig::instance().getBuvid3()}, - {"platform", "web"}, {"type", 2}, - {"key", key}}; + json join_request = {{"uid", uid}, + {"roomid", room_id}, + {"protover", 2}, + {"buvid", ProgramConfig::instance().getBuvid3()}, + {"platform", "web"}, + {"type", 2}, + {"key", this->info.token}}; std::string join_request_str = join_request.dump(); brls::Logger::info("(LiveDanmaku) join_request:{}", join_request_str); std::vector packet = encode_packet(0, 7, join_request_str); @@ -247,14 +251,11 @@ static void mongoose_event_handler(struct mg_connection *nc, int ev, } else if (ev == MG_EV_WS_MSG) { MG_DEBUG(("%p %s", nc->fd, (char *)ev_data)); struct mg_ws_message *wm = (struct mg_ws_message *)ev_data; - add_task(liveDanmaku->onMessage, - std::string(wm->data.ptr, wm->data.len)); + add_msg(std::string(wm->data.ptr, wm->data.len)); } else if (ev == MG_EV_CLOSE) { MG_DEBUG(("%p %s", nc->fd, (char *)ev_data)); liveDanmaku->ms_ev_ok.store(false, std::memory_order_release); } } -void LiveDanmaku::setonMessage(std::function func) { - onMessage = func; -} \ No newline at end of file +void LiveDanmaku::setonMessage(on_message_func_t func) { onMessage = func; } \ No newline at end of file diff --git a/wiliwili/source/api/util/extract_messages.cpp b/wiliwili/source/api/util/extract_messages.cpp index d603df62..24b1d4ff 100644 --- a/wiliwili/source/api/util/extract_messages.cpp +++ b/wiliwili/source/api/util/extract_messages.cpp @@ -71,12 +71,14 @@ std::vector extract_messages(const std::vector &messages) { std::vector live_messages; live_messages.reserve(messages.size() / 5); - for (auto &message : messages) { + for (const auto &message : messages) { nlohmann::json json_message; try { json_message = nlohmann::json::parse(message); - } catch (nlohmann::json::parse_error &e) { + } catch (const std::exception &e) { + continue; + } catch (...) { continue; } @@ -84,7 +86,7 @@ std::vector extract_messages(const std::vector &messages) { if (it == json_message.end()) continue; - if (it->get() == "WATCHED_CHANGE") { + if (it->get_ref() == "WATCHED_CHANGE") { if (json_message["data"]["num"].is_number()) continue; auto num = json_message["data"]["num"].get(); @@ -99,7 +101,7 @@ std::vector extract_messages(const std::vector &messages) { wc->num = num; live_messages.emplace_back(live_t{watched_change, wc}); - } else if (it->get() == "DANMU_MSG") { + } else if (it->get_ref() == "DANMU_MSG") { auto &info = json_message["info"]; if (!info.is_array() || info.size() != 17) continue;