2022-06-18 14:09:38 +08:00
|
|
|
|
/*
|
2023-12-09 16:23:51 +08:00
|
|
|
|
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
|
2019-06-11 09:25:54 +08:00
|
|
|
|
*
|
2023-12-09 16:23:51 +08:00
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
2019-06-11 09:25:54 +08:00
|
|
|
|
*
|
2023-12-09 16:23:51 +08:00
|
|
|
|
* Use of this source code is governed by MIT-like license that can be found in the
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|
|
|
|
* may be found in the AUTHORS file in the root of the source tree.
|
2019-06-11 09:25:54 +08:00
|
|
|
|
*/
|
|
|
|
|
|
2019-05-20 11:22:59 +08:00
|
|
|
|
#include <sstream>
|
|
|
|
|
#include "Util/logger.h"
|
|
|
|
|
#include "Util/onceToken.h"
|
|
|
|
|
#include "Util/NoticeCenter.h"
|
|
|
|
|
#include "Common/config.h"
|
|
|
|
|
#include "Common/MediaSource.h"
|
2023-04-18 10:33:22 +08:00
|
|
|
|
#include "Http/HttpSession.h"
|
2019-05-20 11:22:59 +08:00
|
|
|
|
#include "Http/HttpRequester.h"
|
2022-11-19 09:33:10 +08:00
|
|
|
|
#include "Network/Session.h"
|
2019-05-20 16:26:04 +08:00
|
|
|
|
#include "Rtsp/RtspSession.h"
|
2019-06-24 14:51:49 +08:00
|
|
|
|
#include "WebHook.h"
|
2021-04-08 17:34:13 +08:00
|
|
|
|
#include "WebApi.h"
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
2022-02-02 20:34:50 +08:00
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace Json;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
using namespace toolkit;
|
|
|
|
|
using namespace mediakit;
|
|
|
|
|
|
|
|
|
|
namespace Hook {
|
|
|
|
|
#define HOOK_FIELD "hook."
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
const string kEnable = HOOK_FIELD "enable";
|
|
|
|
|
const string kTimeoutSec = HOOK_FIELD "timeoutSec";
|
|
|
|
|
const string kOnPublish = HOOK_FIELD "on_publish";
|
|
|
|
|
const string kOnPlay = HOOK_FIELD "on_play";
|
|
|
|
|
const string kOnFlowReport = HOOK_FIELD "on_flow_report";
|
|
|
|
|
const string kOnRtspRealm = HOOK_FIELD "on_rtsp_realm";
|
|
|
|
|
const string kOnRtspAuth = HOOK_FIELD "on_rtsp_auth";
|
|
|
|
|
const string kOnStreamChanged = HOOK_FIELD "on_stream_changed";
|
2023-08-14 00:05:32 +08:00
|
|
|
|
const string kStreamChangedSchemas = HOOK_FIELD "stream_changed_schemas";
|
2023-04-18 10:33:22 +08:00
|
|
|
|
const string kOnStreamNotFound = HOOK_FIELD "on_stream_not_found";
|
|
|
|
|
const string kOnRecordMp4 = HOOK_FIELD "on_record_mp4";
|
|
|
|
|
const string kOnRecordTs = HOOK_FIELD "on_record_ts";
|
|
|
|
|
const string kOnShellLogin = HOOK_FIELD "on_shell_login";
|
|
|
|
|
const string kOnStreamNoneReader = HOOK_FIELD "on_stream_none_reader";
|
|
|
|
|
const string kOnHttpAccess = HOOK_FIELD "on_http_access";
|
|
|
|
|
const string kOnServerStarted = HOOK_FIELD "on_server_started";
|
2023-06-28 11:36:41 +08:00
|
|
|
|
const string kOnServerExited = HOOK_FIELD "on_server_exited";
|
2023-04-18 10:33:22 +08:00
|
|
|
|
const string kOnServerKeepalive = HOOK_FIELD "on_server_keepalive";
|
|
|
|
|
const string kOnSendRtpStopped = HOOK_FIELD "on_send_rtp_stopped";
|
|
|
|
|
const string kOnRtpServerTimeout = HOOK_FIELD "on_rtp_server_timeout";
|
|
|
|
|
const string kAliveInterval = HOOK_FIELD "alive_interval";
|
|
|
|
|
const string kRetry = HOOK_FIELD "retry";
|
|
|
|
|
const string kRetryDelay = HOOK_FIELD "retry_delay";
|
|
|
|
|
|
|
|
|
|
static onceToken token([]() {
|
2019-08-16 15:34:34 +08:00
|
|
|
|
mINI::Instance()[kEnable] = false;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
mINI::Instance()[kTimeoutSec] = 10;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 默认hook地址设置为空,采用默认行为(例如不鉴权)
|
2020-11-08 09:28:46 +08:00
|
|
|
|
mINI::Instance()[kOnPublish] = "";
|
|
|
|
|
mINI::Instance()[kOnPlay] = "";
|
|
|
|
|
mINI::Instance()[kOnFlowReport] = "";
|
|
|
|
|
mINI::Instance()[kOnRtspRealm] = "";
|
|
|
|
|
mINI::Instance()[kOnRtspAuth] = "";
|
|
|
|
|
mINI::Instance()[kOnStreamChanged] = "";
|
|
|
|
|
mINI::Instance()[kOnStreamNotFound] = "";
|
|
|
|
|
mINI::Instance()[kOnRecordMp4] = "";
|
|
|
|
|
mINI::Instance()[kOnRecordTs] = "";
|
|
|
|
|
mINI::Instance()[kOnShellLogin] = "";
|
|
|
|
|
mINI::Instance()[kOnStreamNoneReader] = "";
|
|
|
|
|
mINI::Instance()[kOnHttpAccess] = "";
|
|
|
|
|
mINI::Instance()[kOnServerStarted] = "";
|
2023-06-28 11:36:41 +08:00
|
|
|
|
mINI::Instance()[kOnServerExited] = "";
|
2021-08-20 14:52:48 +08:00
|
|
|
|
mINI::Instance()[kOnServerKeepalive] = "";
|
2022-08-27 10:53:47 +08:00
|
|
|
|
mINI::Instance()[kOnSendRtpStopped] = "";
|
2022-11-10 16:58:02 +08:00
|
|
|
|
mINI::Instance()[kOnRtpServerTimeout] = "";
|
2021-08-20 14:52:48 +08:00
|
|
|
|
mINI::Instance()[kAliveInterval] = 30.0;
|
2022-06-18 14:09:38 +08:00
|
|
|
|
mINI::Instance()[kRetry] = 1;
|
|
|
|
|
mINI::Instance()[kRetryDelay] = 3.0;
|
2023-08-14 00:05:32 +08:00
|
|
|
|
mINI::Instance()[kStreamChangedSchemas] = "rtsp/rtmp/fmp4/ts/hls/hls.fmp4";
|
2023-04-18 10:33:22 +08:00
|
|
|
|
});
|
|
|
|
|
} // namespace Hook
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
2022-01-12 16:45:47 +08:00
|
|
|
|
namespace Cluster {
|
|
|
|
|
#define CLUSTER_FIELD "cluster."
|
|
|
|
|
const string kOriginUrl = CLUSTER_FIELD "origin_url";
|
2022-01-12 17:58:07 +08:00
|
|
|
|
const string kTimeoutSec = CLUSTER_FIELD "timeout_sec";
|
2022-05-28 09:52:31 +08:00
|
|
|
|
const string kRetryCount = CLUSTER_FIELD "retry_count";
|
2022-01-12 17:58:07 +08:00
|
|
|
|
|
2022-01-12 16:45:47 +08:00
|
|
|
|
static onceToken token([]() {
|
|
|
|
|
mINI::Instance()[kOriginUrl] = "";
|
2022-01-12 17:58:07 +08:00
|
|
|
|
mINI::Instance()[kTimeoutSec] = 15;
|
2022-05-30 12:44:11 +08:00
|
|
|
|
mINI::Instance()[kRetryCount] = 3;
|
2022-01-12 16:45:47 +08:00
|
|
|
|
});
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
} // namespace Cluster
|
2022-01-12 16:45:47 +08:00
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
static void parse_http_response(const SockException &ex, const Parser &res, const function<void(const Value &, const string &, bool)> &fun) {
|
2023-04-17 20:01:45 +08:00
|
|
|
|
bool should_retry = true;
|
2021-09-30 16:10:09 +08:00
|
|
|
|
if (ex) {
|
2023-04-23 00:10:18 +08:00
|
|
|
|
auto errStr = StrPrinter << "[network err]:" << ex << endl;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
fun(Json::nullValue, errStr, should_retry);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-06-10 11:04:52 +08:00
|
|
|
|
if (res.status() != "200") {
|
|
|
|
|
auto errStr = StrPrinter << "[bad http status code]:" << res.status() << endl;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
fun(Json::nullValue, errStr, should_retry);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2021-10-14 16:35:06 +08:00
|
|
|
|
Value result;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
try {
|
2023-06-10 11:04:52 +08:00
|
|
|
|
stringstream ss(res.content());
|
2019-05-20 11:22:59 +08:00
|
|
|
|
ss >> result;
|
2021-09-30 16:10:09 +08:00
|
|
|
|
} catch (std::exception &ex) {
|
2019-05-20 11:22:59 +08:00
|
|
|
|
auto errStr = StrPrinter << "[parse json failed]:" << ex.what() << endl;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
fun(Json::nullValue, errStr, should_retry);
|
2021-10-14 16:35:06 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-17 20:01:45 +08:00
|
|
|
|
auto code = result["code"];
|
|
|
|
|
|
|
|
|
|
if (!code.isInt64()) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
auto errStr = StrPrinter << "[json code]:" << "code not int:" << code << endl;
|
|
|
|
|
fun(Json::nullValue, errStr, should_retry);
|
2023-04-17 20:01:45 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
should_retry = false;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
if (code.asInt64() != 0) {
|
|
|
|
|
auto errStr = StrPrinter << "[auth failed]: code:" << code << " msg:" << result["msg"] << endl;
|
|
|
|
|
fun(Json::nullValue, errStr, should_retry);
|
2021-10-14 16:35:06 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-17 20:01:45 +08:00
|
|
|
|
|
2021-10-14 16:35:06 +08:00
|
|
|
|
try {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
fun(result, "", should_retry);
|
2021-10-14 16:35:06 +08:00
|
|
|
|
} catch (std::exception &ex) {
|
|
|
|
|
auto errStr = StrPrinter << "[do hook invoker failed]:" << ex.what() << endl;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 如果还是抛异常,那么再上抛异常
|
|
|
|
|
fun(Json::nullValue, errStr, should_retry);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
string to_string(const Value &value) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return value.toStyledString();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
string to_string(const HttpArgs &value) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return value.make();
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
const char *getContentType(const Value &value) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return "application/json";
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
const char *getContentType(const HttpArgs &value) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return "application/x-www-form-urlencoded";
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-22 09:53:31 +08:00
|
|
|
|
string getVhost(const Value &value) {
|
|
|
|
|
const char *key = VHOST_KEY;
|
|
|
|
|
auto val = value.find(key, key + sizeof(VHOST_KEY) - 1);
|
|
|
|
|
return val ? val->asString() : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string getVhost(const HttpArgs &value) {
|
|
|
|
|
auto val = value.find(VHOST_KEY);
|
|
|
|
|
return val != value.end() ? val->second : "";
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-13 20:46:08 +08:00
|
|
|
|
static atomic<uint64_t> s_hook_index { 0 };
|
|
|
|
|
|
2022-06-18 14:09:38 +08:00
|
|
|
|
void do_http_hook(const string &url, const ArgsType &body, const function<void(const Value &, const string &)> &func, uint32_t retry) {
|
2020-12-27 22:14:59 +08:00
|
|
|
|
GET_CONFIG(string, mediaServerId, General::kMediaServerId);
|
|
|
|
|
GET_CONFIG(float, hook_timeoutSec, Hook::kTimeoutSec);
|
2022-06-18 14:09:38 +08:00
|
|
|
|
GET_CONFIG(float, retry_delay, Hook::kRetryDelay);
|
2020-10-01 14:37:52 +08:00
|
|
|
|
|
2020-12-27 22:14:59 +08:00
|
|
|
|
const_cast<ArgsType &>(body)["mediaServerId"] = mediaServerId;
|
2023-12-09 16:23:51 +08:00
|
|
|
|
const_cast<ArgsType &>(body)["hook_index"] = (Json::UInt64)(s_hook_index++);
|
2023-08-13 20:46:08 +08:00
|
|
|
|
|
2023-04-01 23:54:11 +08:00
|
|
|
|
auto requester = std::make_shared<HttpRequester>();
|
2019-05-20 11:22:59 +08:00
|
|
|
|
requester->setMethod("POST");
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto bodyStr = to_string(body);
|
|
|
|
|
requester->setBody(bodyStr);
|
2020-12-27 22:14:59 +08:00
|
|
|
|
requester->addHeader("Content-Type", getContentType(body));
|
2021-05-22 09:53:31 +08:00
|
|
|
|
auto vhost = getVhost(body);
|
|
|
|
|
if (!vhost.empty()) {
|
|
|
|
|
requester->addHeader("X-VHOST", vhost);
|
|
|
|
|
}
|
2022-06-18 14:09:38 +08:00
|
|
|
|
Ticker ticker;
|
|
|
|
|
requester->startRequester(url, [url, func, bodyStr, body, requester, ticker, retry](const SockException &ex, const Parser &res) mutable {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
onceToken token(nullptr, [&]() mutable { requester.reset(); });
|
|
|
|
|
parse_http_response(ex, res, [&](const Value &obj, const string &err, bool should_retry) {
|
2022-06-18 14:09:38 +08:00
|
|
|
|
if (!err.empty()) {
|
|
|
|
|
// hook失败
|
|
|
|
|
WarnL << "hook " << url << " " << ticker.elapsedTime() << "ms,failed" << err << ":" << bodyStr;
|
|
|
|
|
|
2023-04-17 20:01:45 +08:00
|
|
|
|
if (retry-- > 0 && should_retry) {
|
2022-06-18 14:09:38 +08:00
|
|
|
|
requester->getPoller()->doDelayTask(MAX(retry_delay, 0.0) * 1000, [url, body, func, retry] {
|
|
|
|
|
do_http_hook(url, body, func, retry);
|
|
|
|
|
return 0;
|
|
|
|
|
});
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 重试不需要触发回调
|
2022-06-18 14:09:38 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (ticker.elapsedTime() > 500) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// hook成功,但是hook响应超过500ms,打印警告日志
|
2022-06-18 14:09:38 +08:00
|
|
|
|
DebugL << "hook " << url << " " << ticker.elapsedTime() << "ms,success:" << bodyStr;
|
|
|
|
|
}
|
2022-06-15 15:14:36 +08:00
|
|
|
|
|
2020-12-27 22:14:59 +08:00
|
|
|
|
if (func) {
|
|
|
|
|
func(obj, err);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
2022-06-18 14:09:38 +08:00
|
|
|
|
}, hook_timeoutSec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void do_http_hook(const string &url, const ArgsType &body, const function<void(const Value &, const string &)> &func) {
|
|
|
|
|
GET_CONFIG(uint32_t, hook_retry, Hook::kRetry);
|
|
|
|
|
do_http_hook(url, body, func, hook_retry);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-06-17 10:28:01 +08:00
|
|
|
|
void dumpMediaTuple(const MediaTuple &tuple, Json::Value& item);
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
static ArgsType make_json(const MediaInfo &args) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
ArgsType body;
|
2023-05-25 16:23:24 +08:00
|
|
|
|
body["schema"] = args.schema;
|
2023-06-17 10:28:01 +08:00
|
|
|
|
dumpMediaTuple(args, body);
|
2024-03-30 14:41:20 +08:00
|
|
|
|
body["params"] = args.params;
|
2020-09-21 14:32:56 +08:00
|
|
|
|
return body;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
static void reportServerStarted() {
|
|
|
|
|
GET_CONFIG(bool, hook_enable, Hook::kEnable);
|
|
|
|
|
GET_CONFIG(string, hook_server_started, Hook::kOnServerStarted);
|
|
|
|
|
if (!hook_enable || hook_server_started.empty()) {
|
2019-11-29 10:12:20 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
for (auto &pr : mINI::Instance()) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
body[pr.first] = (string &)pr.second;
|
2019-11-29 10:12:20 +08:00
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_server_started, body, nullptr);
|
2019-11-29 10:12:20 +08:00
|
|
|
|
}
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
2023-06-28 11:36:41 +08:00
|
|
|
|
static void reportServerExited() {
|
|
|
|
|
GET_CONFIG(bool, hook_enable, Hook::kEnable);
|
|
|
|
|
GET_CONFIG(string, hook_server_exited, Hook::kOnServerExited);
|
|
|
|
|
if (!hook_enable || hook_server_exited.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ArgsType body;
|
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_server_exited, body, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-20 14:52:48 +08:00
|
|
|
|
// 服务器定时保活定时器
|
2021-08-21 19:11:20 +08:00
|
|
|
|
static Timer::Ptr g_keepalive_timer;
|
2021-08-20 14:52:48 +08:00
|
|
|
|
static void reportServerKeepalive() {
|
2021-08-21 19:11:20 +08:00
|
|
|
|
GET_CONFIG(bool, hook_enable, Hook::kEnable);
|
2021-08-20 14:52:48 +08:00
|
|
|
|
GET_CONFIG(string, hook_server_keepalive, Hook::kOnServerKeepalive);
|
2021-08-21 19:11:20 +08:00
|
|
|
|
if (!hook_enable || hook_server_keepalive.empty()) {
|
2021-08-20 14:52:48 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GET_CONFIG(float, alive_interval, Hook::kAliveInterval);
|
2023-04-18 10:33:22 +08:00
|
|
|
|
g_keepalive_timer = std::make_shared<Timer>(alive_interval,[]() {
|
2021-12-27 17:40:15 +08:00
|
|
|
|
getStatisticJson([](const Value &data) mutable {
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["data"] = data;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
2021-12-27 17:40:15 +08:00
|
|
|
|
do_http_hook(hook_server_keepalive, body, nullptr);
|
|
|
|
|
});
|
2021-08-20 14:52:48 +08:00
|
|
|
|
return true;
|
|
|
|
|
}, nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 17:43:07 +08:00
|
|
|
|
static const string kEdgeServerParam = "edge=1";
|
|
|
|
|
|
|
|
|
|
static string getPullUrl(const string &origin_fmt, const MediaInfo &info) {
|
|
|
|
|
char url[1024] = { 0 };
|
2023-05-25 16:23:24 +08:00
|
|
|
|
if ((ssize_t)origin_fmt.size() > snprintf(url, sizeof(url), origin_fmt.data(), info.app.data(), info.stream.data())) {
|
2022-01-12 17:43:07 +08:00
|
|
|
|
WarnL << "get origin url failed, origin_fmt:" << origin_fmt;
|
|
|
|
|
return "";
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 告知源站这是来自边沿站的拉流请求,如果未找到流请立即返回拉流失败
|
2024-03-30 14:41:20 +08:00
|
|
|
|
return string(url) + '?' + kEdgeServerParam + '&' + VHOST_KEY + '=' + info.vhost + '&' + info.params;
|
2022-01-12 17:43:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
static void pullStreamFromOrigin(const vector<string> &urls, size_t index, size_t failed_cnt, const MediaInfo &args, const function<void()> &closePlayer) {
|
2022-01-12 17:58:07 +08:00
|
|
|
|
GET_CONFIG(float, cluster_timeout_sec, Cluster::kTimeoutSec);
|
2022-05-28 09:52:31 +08:00
|
|
|
|
GET_CONFIG(int, retry_count, Cluster::kRetryCount);
|
|
|
|
|
|
2022-01-12 17:43:07 +08:00
|
|
|
|
auto url = getPullUrl(urls[index % urls.size()], args);
|
2022-01-12 17:58:07 +08:00
|
|
|
|
auto timeout_sec = cluster_timeout_sec / urls.size();
|
2022-01-12 17:43:07 +08:00
|
|
|
|
InfoL << "pull stream from origin, failed_cnt: " << failed_cnt << ", timeout_sec: " << timeout_sec << ", url: " << url;
|
|
|
|
|
|
2022-03-12 13:19:21 +08:00
|
|
|
|
ProtocolOption option;
|
2023-05-25 16:23:24 +08:00
|
|
|
|
option.enable_hls = option.enable_hls || (args.schema == HLS_SCHEMA);
|
2022-03-12 13:19:21 +08:00
|
|
|
|
option.enable_mp4 = false;
|
|
|
|
|
|
2024-07-14 09:32:41 +08:00
|
|
|
|
addStreamProxy(args, url, retry_count, option, Rtsp::RTP_TCP, timeout_sec, mINI{}, [=](const SockException &ex, const string &key) mutable {
|
2022-01-12 17:43:07 +08:00
|
|
|
|
if (!ex) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 拉流失败
|
2022-01-12 17:43:07 +08:00
|
|
|
|
if (++failed_cnt == urls.size()) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 已经重试所有源站了
|
2022-01-12 17:43:07 +08:00
|
|
|
|
WarnL << "pull stream from origin final failed: " << url;
|
|
|
|
|
closePlayer();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
pullStreamFromOrigin(urls, index + 1, failed_cnt, args, closePlayer);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 16:23:35 +08:00
|
|
|
|
static void *web_hook_tag = nullptr;
|
|
|
|
|
|
2022-09-16 23:30:55 +08:00
|
|
|
|
static mINI jsonToMini(const Value &obj) {
|
|
|
|
|
mINI ret;
|
|
|
|
|
if (obj.isObject()) {
|
|
|
|
|
for (auto it = obj.begin(); it != obj.end(); ++it) {
|
2022-10-20 10:59:43 +08:00
|
|
|
|
try {
|
|
|
|
|
auto str = (*it).asString();
|
|
|
|
|
ret[it.name()] = std::move(str);
|
|
|
|
|
} catch (std::exception &) {
|
|
|
|
|
WarnL << "Json is not convertible to string, key: " << it.name() << ", value: " << (*it);
|
|
|
|
|
}
|
2022-09-16 23:30:55 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
void installWebHook() {
|
|
|
|
|
GET_CONFIG(bool, hook_enable, Hook::kEnable);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
2022-07-29 16:23:35 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaPublish, [](BroadcastMediaPublishArgs) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
GET_CONFIG(string, hook_publish, Hook::kOnPublish);
|
2023-07-27 15:51:07 +08:00
|
|
|
|
if (!hook_enable || hook_publish.empty()) {
|
2022-03-12 13:19:21 +08:00
|
|
|
|
invoker("", ProtocolOption());
|
2019-05-20 11:22:59 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 异步执行该hook api,防止阻塞NoticeCenter
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto body = make_json(args);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
body["originType"] = (int)type;
|
2022-03-02 18:03:44 +08:00
|
|
|
|
body["originTypeStr"] = getOriginTypeString(type);
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
2022-03-12 13:19:21 +08:00
|
|
|
|
do_http_hook(hook_publish, body, [invoker](const Value &obj, const string &err) mutable {
|
|
|
|
|
if (err.empty()) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 推流鉴权成功
|
2022-09-16 23:30:55 +08:00
|
|
|
|
invoker(err, ProtocolOption(jsonToMini(obj)));
|
2020-09-12 19:09:56 +08:00
|
|
|
|
} else {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 推流鉴权失败
|
2022-09-16 23:30:55 +08:00
|
|
|
|
invoker(err, ProtocolOption());
|
2019-09-10 11:06:31 +08:00
|
|
|
|
}
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaPlayed, [](BroadcastMediaPlayedArgs) {
|
|
|
|
|
GET_CONFIG(string, hook_play, Hook::kOnPlay);
|
2023-07-27 15:51:07 +08:00
|
|
|
|
if (!hook_enable || hook_play.empty()) {
|
2019-05-20 11:22:59 +08:00
|
|
|
|
invoker("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto body = make_json(args);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_play, body, [invoker](const Value &obj, const string &err) { invoker(err); });
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastFlowReport, [](BroadcastFlowReportArgs) {
|
|
|
|
|
GET_CONFIG(string, hook_flowreport, Hook::kOnFlowReport);
|
2023-07-27 15:51:07 +08:00
|
|
|
|
if (!hook_enable || hook_flowreport.empty()) {
|
2019-05-20 11:22:59 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto body = make_json(args);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["totalBytes"] = (Json::UInt64)totalBytes;
|
|
|
|
|
body["duration"] = (Json::UInt64)totalDuration;
|
2019-05-20 17:46:06 +08:00
|
|
|
|
body["player"] = isPlayer;
|
2020-04-23 22:04:59 +08:00
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_flowreport, body, nullptr);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
2019-05-20 16:26:04 +08:00
|
|
|
|
|
|
|
|
|
static const string unAuthedRealm = "unAuthedRealm";
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问
|
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastOnGetRtspRealm, [](BroadcastOnGetRtspRealmArgs) {
|
|
|
|
|
GET_CONFIG(string, hook_rtsp_realm, Hook::kOnRtspRealm);
|
2023-07-27 15:51:07 +08:00
|
|
|
|
if (!hook_enable || hook_rtsp_realm.empty()) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 无需认证
|
2019-05-20 16:26:04 +08:00
|
|
|
|
invoker("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto body = make_json(args);
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_rtsp_realm, body, [invoker](const Value &obj, const string &err) {
|
|
|
|
|
if (!err.empty()) {
|
|
|
|
|
// 如果接口访问失败,那么该rtsp流认证失败
|
2019-05-20 17:34:39 +08:00
|
|
|
|
invoker(unAuthedRealm);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
invoker(obj["realm"].asString());
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码
|
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastOnRtspAuth, [](BroadcastOnRtspAuthArgs) {
|
|
|
|
|
GET_CONFIG(string, hook_rtsp_auth, Hook::kOnRtspAuth);
|
|
|
|
|
if (unAuthedRealm == realm || !hook_enable || hook_rtsp_auth.empty()) {
|
|
|
|
|
// 认证失败
|
|
|
|
|
invoker(false, makeRandStr(12));
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto body = make_json(args);
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
|
|
|
|
body["user_name"] = user_name;
|
|
|
|
|
body["must_no_encrypt"] = must_no_encrypt;
|
|
|
|
|
body["realm"] = realm;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_rtsp_auth, body, [invoker](const Value &obj, const string &err) {
|
|
|
|
|
if (!err.empty()) {
|
|
|
|
|
// 认证失败
|
|
|
|
|
invoker(false, makeRandStr(12));
|
2019-05-20 17:34:39 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
invoker(obj["encrypted"].asBool(), obj["passwd"].asString());
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 监听rtsp、rtmp源注册或注销事件
|
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) {
|
2023-08-14 00:05:32 +08:00
|
|
|
|
GET_CONFIG(string, hook_stream_changed, Hook::kOnStreamChanged);
|
|
|
|
|
if (!hook_enable || hook_stream_changed.empty()) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-08-14 00:05:32 +08:00
|
|
|
|
GET_CONFIG_FUNC(std::set<std::string>, stream_changed_set, Hook::kStreamChangedSchemas, [](const std::string &str) {
|
|
|
|
|
std::set<std::string> ret;
|
|
|
|
|
auto vec = split(str, "/");
|
|
|
|
|
for (auto &schema : vec) {
|
|
|
|
|
trim(schema);
|
|
|
|
|
if (!schema.empty()) {
|
|
|
|
|
ret.emplace(schema);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
});
|
|
|
|
|
if (!stream_changed_set.empty() && stream_changed_set.find(sender.getSchema()) == stream_changed_set.end()) {
|
|
|
|
|
// 该协议注册注销事件被忽略
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-20 16:26:04 +08:00
|
|
|
|
ArgsType body;
|
2021-04-08 17:34:13 +08:00
|
|
|
|
if (bRegist) {
|
|
|
|
|
body = makeMediaSourceJson(sender);
|
2021-04-11 11:39:49 +08:00
|
|
|
|
body["regist"] = bRegist;
|
2021-04-08 17:34:13 +08:00
|
|
|
|
} else {
|
|
|
|
|
body["schema"] = sender.getSchema();
|
2023-06-17 10:28:01 +08:00
|
|
|
|
dumpMediaTuple(sender.getMediaTuple(), body);
|
2021-04-11 11:39:49 +08:00
|
|
|
|
body["regist"] = bRegist;
|
2021-04-08 17:34:13 +08:00
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
2023-08-14 00:05:32 +08:00
|
|
|
|
do_http_hook(hook_stream_changed, body, nullptr);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
|
2022-01-12 17:43:07 +08:00
|
|
|
|
GET_CONFIG_FUNC(vector<string>, origin_urls, Cluster::kOriginUrl, [](const string &str) {
|
|
|
|
|
vector<string> ret;
|
|
|
|
|
for (auto &url : split(str, ";")) {
|
|
|
|
|
trim(url);
|
|
|
|
|
if (!url.empty()) {
|
|
|
|
|
ret.emplace_back(url);
|
|
|
|
|
}
|
2022-01-12 16:45:47 +08:00
|
|
|
|
}
|
2022-01-12 17:43:07 +08:00
|
|
|
|
return ret;
|
|
|
|
|
});
|
2022-01-12 16:45:47 +08:00
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 监听播放失败(未找到特定的流)事件
|
2022-07-29 16:23:35 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastNotFoundStream, [](BroadcastNotFoundStreamArgs) {
|
2022-01-12 17:43:07 +08:00
|
|
|
|
if (!origin_urls.empty()) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 设置了源站,那么尝试溯源
|
2022-01-12 17:43:07 +08:00
|
|
|
|
static atomic<uint8_t> s_index { 0 };
|
|
|
|
|
pullStreamFromOrigin(origin_urls, s_index.load(), 0, args, closePlayer);
|
|
|
|
|
++s_index;
|
2022-01-12 16:45:47 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-30 14:41:20 +08:00
|
|
|
|
if (start_with(args.params, kEdgeServerParam)) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 源站收到来自边沿站的溯源请求,流不存在时立即返回拉流失败
|
2022-01-12 20:24:19 +08:00
|
|
|
|
closePlayer();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 16:45:47 +08:00
|
|
|
|
GET_CONFIG(string, hook_stream_not_found, Hook::kOnStreamNotFound);
|
|
|
|
|
if (!hook_enable || hook_stream_not_found.empty()) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto body = make_json(args);
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2022-09-09 10:55:35 +08:00
|
|
|
|
|
|
|
|
|
// Hook回复立即关闭流
|
|
|
|
|
auto res_cb = [closePlayer](const Value &res, const string &err) {
|
|
|
|
|
bool flag = res["close"].asBool();
|
|
|
|
|
if (flag) {
|
|
|
|
|
closePlayer();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
2022-09-09 10:55:35 +08:00
|
|
|
|
do_http_hook(hook_stream_not_found, body, res_cb);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
|
2020-09-20 11:40:42 +08:00
|
|
|
|
static auto getRecordInfo = [](const RecordInfo &info) {
|
|
|
|
|
ArgsType body;
|
2023-04-18 10:33:22 +08:00
|
|
|
|
body["start_time"] = (Json::UInt64)info.start_time;
|
|
|
|
|
body["file_size"] = (Json::UInt64)info.file_size;
|
2020-09-20 11:40:42 +08:00
|
|
|
|
body["time_len"] = info.time_len;
|
|
|
|
|
body["file_path"] = info.file_path;
|
|
|
|
|
body["file_name"] = info.file_name;
|
|
|
|
|
body["folder"] = info.folder;
|
|
|
|
|
body["url"] = info.url;
|
2023-06-17 10:28:01 +08:00
|
|
|
|
dumpMediaTuple(info, body);
|
2020-09-20 11:40:42 +08:00
|
|
|
|
return body;
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-03 20:45:58 +08:00
|
|
|
|
#ifdef ENABLE_MP4
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 录制mp4文件成功后广播
|
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRecordMP4, [](BroadcastRecordMP4Args) {
|
|
|
|
|
GET_CONFIG(string, hook_record_mp4, Hook::kOnRecordMp4);
|
2020-09-20 11:40:42 +08:00
|
|
|
|
if (!hook_enable || hook_record_mp4.empty()) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
2020-09-20 11:40:42 +08:00
|
|
|
|
do_http_hook(hook_record_mp4, getRecordInfo(info), nullptr);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
2023-04-18 10:33:22 +08:00
|
|
|
|
#endif // ENABLE_MP4
|
2019-05-20 16:26:04 +08:00
|
|
|
|
|
2022-07-29 16:23:35 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRecordTs, [](BroadcastRecordTsArgs) {
|
2023-04-18 10:33:22 +08:00
|
|
|
|
GET_CONFIG(string, hook_record_ts, Hook::kOnRecordTs);
|
2020-09-13 14:08:05 +08:00
|
|
|
|
if (!hook_enable || hook_record_ts.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 执行 hook
|
2020-09-20 11:40:42 +08:00
|
|
|
|
do_http_hook(hook_record_ts, getRecordInfo(info), nullptr);
|
2020-09-13 14:08:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastShellLogin, [](BroadcastShellLoginArgs) {
|
|
|
|
|
GET_CONFIG(string, hook_shell_login, Hook::kOnShellLogin);
|
2023-07-27 15:51:07 +08:00
|
|
|
|
if (!hook_enable || hook_shell_login.empty()) {
|
2019-06-06 15:33:11 +08:00
|
|
|
|
invoker("");
|
2019-05-27 13:56:37 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
|
|
|
|
body["user_name"] = user_name;
|
|
|
|
|
body["passwd"] = passwd;
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_shell_login, body, [invoker](const Value &, const string &err) { invoker(err); });
|
2019-05-27 13:56:37 +08:00
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastStreamNoneReader, [](BroadcastStreamNoneReaderArgs) {
|
2023-11-19 14:35:18 +08:00
|
|
|
|
if (!origin_urls.empty() && sender.getOriginType() == MediaOriginType::pull) {
|
|
|
|
|
// 边沿站无人观看时如果是拉流的则立即停止溯源
|
2022-01-12 16:45:47 +08:00
|
|
|
|
sender.close(false);
|
|
|
|
|
WarnL << "无人观看主动关闭流:" << sender.getOriginUrl();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
GET_CONFIG(string, hook_stream_none_reader, Hook::kOnStreamNoneReader);
|
|
|
|
|
if (!hook_enable || hook_stream_none_reader.empty()) {
|
2019-05-27 18:39:43 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["schema"] = sender.getSchema();
|
2023-06-17 10:28:01 +08:00
|
|
|
|
dumpMediaTuple(sender.getMediaTuple(), body);
|
2019-05-27 18:39:43 +08:00
|
|
|
|
weak_ptr<MediaSource> weakSrc = sender.shared_from_this();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_stream_none_reader, body, [weakSrc](const Value &obj, const string &err) {
|
2019-05-27 18:39:43 +08:00
|
|
|
|
bool flag = obj["close"].asBool();
|
|
|
|
|
auto strongSrc = weakSrc.lock();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
if (!flag || !err.empty() || !strongSrc) {
|
2019-05-27 18:39:43 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
strongSrc->close(false);
|
2022-01-12 16:45:47 +08:00
|
|
|
|
WarnL << "无人观看主动关闭流:" << strongSrc->getOriginUrl();
|
2019-05-27 18:39:43 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-07-08 22:34:09 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastSendRtpStopped, [](BroadcastSendRtpStoppedArgs) {
|
2022-08-27 10:53:47 +08:00
|
|
|
|
GET_CONFIG(string, hook_send_rtp_stopped, Hook::kOnSendRtpStopped);
|
|
|
|
|
if (!hook_enable || hook_send_rtp_stopped.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
2023-06-17 10:28:01 +08:00
|
|
|
|
dumpMediaTuple(sender.getMediaTuple(), body);
|
2022-08-27 10:53:47 +08:00
|
|
|
|
body["ssrc"] = ssrc;
|
|
|
|
|
body["originType"] = (int)sender.getOriginType(MediaSource::NullMediaSource());
|
|
|
|
|
body["originTypeStr"] = getOriginTypeString(sender.getOriginType(MediaSource::NullMediaSource()));
|
|
|
|
|
body["originUrl"] = sender.getOriginUrl(MediaSource::NullMediaSource());
|
|
|
|
|
body["msg"] = ex.what();
|
|
|
|
|
body["err"] = ex.getErrCode();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
2022-08-27 10:53:47 +08:00
|
|
|
|
do_http_hook(hook_send_rtp_stopped, body, nullptr);
|
|
|
|
|
});
|
|
|
|
|
|
2019-06-14 15:19:02 +08:00
|
|
|
|
/**
|
|
|
|
|
* kBroadcastHttpAccess事件触发机制
|
|
|
|
|
* 1、根据http请求头查找cookie,找到进入步骤3
|
2019-11-30 11:44:05 +08:00
|
|
|
|
* 2、根据http url参数查找cookie,如果还是未找到cookie则进入步骤5
|
2019-06-14 15:19:02 +08:00
|
|
|
|
* 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件
|
|
|
|
|
* 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码
|
|
|
|
|
* 5、触发kBroadcastHttpAccess事件
|
|
|
|
|
*/
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 开发者应该通过该事件判定http客户端是否有权限访问http服务器上的特定文件
|
|
|
|
|
// ZLMediaKit会记录本次鉴权的结果至cookie
|
|
|
|
|
// 如果鉴权成功,在cookie有效期内,那么下次客户端再访问授权目录时,ZLMediaKit会直接返回文件
|
|
|
|
|
// 如果鉴权失败,在cookie有效期内,如果http url参数不变(否则会立即再次触发鉴权事件),ZLMediaKit会直接返回错误码
|
|
|
|
|
// 如果用户客户端不支持cookie,那么ZLMediaKit会根据url参数查找cookie并追踪用户,
|
|
|
|
|
// 如果没有url参数,客户端又不支持cookie,那么会根据ip和端口追踪用户
|
|
|
|
|
// 追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能
|
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastHttpAccess, [](BroadcastHttpAccessArgs) {
|
|
|
|
|
GET_CONFIG(string, hook_http_access, Hook::kOnHttpAccess);
|
|
|
|
|
if (!hook_enable || hook_http_access.empty()) {
|
|
|
|
|
// 未开启http文件访问鉴权,那么允许访问,但是每次访问都要鉴权;
|
|
|
|
|
// 因为后续随时都可能开启鉴权(重载配置文件后可能重新开启鉴权)
|
2023-07-27 15:39:43 +08:00
|
|
|
|
if (!HttpFileManager::isIPAllowed(sender.get_peer_ip())) {
|
|
|
|
|
invoker("Your ip is not allowed to access the service.", "", 0);
|
|
|
|
|
} else {
|
|
|
|
|
invoker("", "", 0);
|
|
|
|
|
}
|
2019-06-12 17:53:48 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
|
|
|
|
body["path"] = path;
|
|
|
|
|
body["is_dir"] = is_dir;
|
2023-06-10 11:04:52 +08:00
|
|
|
|
body["params"] = parser.params();
|
2023-04-18 10:33:22 +08:00
|
|
|
|
for (auto &pr : parser.getHeader()) {
|
2019-06-13 14:29:25 +08:00
|
|
|
|
body[string("header.") + pr.first] = pr.second;
|
2019-06-12 17:53:48 +08:00
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 执行hook
|
|
|
|
|
do_http_hook(hook_http_access, body, [invoker](const Value &obj, const string &err) {
|
|
|
|
|
if (!err.empty()) {
|
|
|
|
|
// 如果接口访问失败,那么仅限本次没有访问http服务器的权限
|
|
|
|
|
invoker(err, "", 0);
|
2019-06-12 17:53:48 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// err参数代表不能访问的原因,空则代表可以访问
|
|
|
|
|
// path参数是该客户端能访问或被禁止的顶端目录,如果path为空字符串,则表述为当前目录
|
|
|
|
|
// second参数规定该cookie超时时间,如果second为0,本次鉴权结果不缓存
|
|
|
|
|
invoker(obj["err"].asString(), obj["path"].asString(), obj["second"].asInt());
|
2019-06-12 17:53:48 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
2019-11-29 10:12:20 +08:00
|
|
|
|
|
2023-10-19 17:55:30 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastRtpServerTimeout, [](BroadcastRtpServerTimeoutArgs) {
|
2022-11-12 09:52:49 +08:00
|
|
|
|
GET_CONFIG(string, rtp_server_timeout, Hook::kOnRtpServerTimeout);
|
|
|
|
|
if (!hook_enable || rtp_server_timeout.empty()) {
|
2022-11-10 16:58:02 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["local_port"] = local_port;
|
2024-07-09 10:42:10 +08:00
|
|
|
|
body[VHOST_KEY] = tuple.vhost;
|
|
|
|
|
body["app"] = tuple.app;
|
|
|
|
|
body["stream_id"] = tuple.stream;
|
2022-11-10 16:58:02 +08:00
|
|
|
|
body["tcp_mode"] = tcp_mode;
|
|
|
|
|
body["re_use_port"] = re_use_port;
|
|
|
|
|
body["ssrc"] = ssrc;
|
2022-11-12 09:52:49 +08:00
|
|
|
|
do_http_hook(rtp_server_timeout, body);
|
2022-11-10 16:58:02 +08:00
|
|
|
|
});
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 汇报服务器重新启动
|
2019-11-29 10:12:20 +08:00
|
|
|
|
reportServerStarted();
|
2021-08-20 14:52:48 +08:00
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
// 定时上报保活
|
2021-08-20 14:52:48 +08:00
|
|
|
|
reportServerKeepalive();
|
2019-05-20 17:03:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-18 10:33:22 +08:00
|
|
|
|
void unInstallWebHook() {
|
2021-08-20 14:52:48 +08:00
|
|
|
|
g_keepalive_timer.reset();
|
2022-07-29 16:23:35 +08:00
|
|
|
|
NoticeCenter::Instance().delListener(&web_hook_tag);
|
2019-06-13 18:39:57 +08:00
|
|
|
|
}
|
2023-06-28 11:36:41 +08:00
|
|
|
|
|
|
|
|
|
void onProcessExited() {
|
|
|
|
|
reportServerExited();
|
2023-10-19 17:55:30 +08:00
|
|
|
|
}
|