mirror of
https://gitee.com/xia-chu/ZLMediaKit.git
synced 2026-05-27 03:57:48 +08:00
Compare commits
3 Commits
fa7b0639d8
...
e1d3c21529
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1d3c21529 | ||
|
|
1bc40690c9 | ||
|
|
752f705b36 |
@ -595,7 +595,7 @@ void getStatisticJson(const function<void(Value &val)> &cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count, bool force,
|
void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count, bool force,
|
||||||
const ProtocolOption &option, int rtp_type, float timeout_sec, const mINI &args,
|
const ProtocolOption &option, float timeout_sec, const mINI &args,
|
||||||
const function<void(const SockException &ex, const string &key)> &cb) {
|
const function<void(const SockException &ex, const string &key)> &cb) {
|
||||||
auto key = tuple.shortUrl();
|
auto key = tuple.shortUrl();
|
||||||
if (s_player_proxy.find(key)) {
|
if (s_player_proxy.find(key)) {
|
||||||
@ -614,10 +614,6 @@ void addStreamProxy(const MediaTuple &tuple, const string &url, int retry_count,
|
|||||||
(*player)[pr.first] = pr.second;
|
(*player)[pr.first] = pr.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 指定RTP over TCP(播放rtsp时有效) [AUTO-TRANSLATED:1a062656]
|
|
||||||
// Specify RTP over TCP (effective when playing RTSP)
|
|
||||||
(*player)[Client::kRtpType] = rtp_type;
|
|
||||||
|
|
||||||
if (timeout_sec > 0.1f) {
|
if (timeout_sec > 0.1f) {
|
||||||
// 播放握手超时时间 [AUTO-TRANSLATED:5a29ae1f]
|
// 播放握手超时时间 [AUTO-TRANSLATED:5a29ae1f]
|
||||||
// Play handshake timeout
|
// Play handshake timeout
|
||||||
@ -1284,7 +1280,6 @@ void installWebApi() {
|
|||||||
retry_count,
|
retry_count,
|
||||||
allArgs["force"],
|
allArgs["force"],
|
||||||
option,
|
option,
|
||||||
allArgs["rtp_type"],
|
|
||||||
allArgs["timeout_sec"],
|
allArgs["timeout_sec"],
|
||||||
args,
|
args,
|
||||||
[invoker,val,headerOut](const SockException &ex,const string &key) mutable {
|
[invoker,val,headerOut](const SockException &ex,const string &key) mutable {
|
||||||
|
|||||||
@ -56,6 +56,7 @@ typedef enum {
|
|||||||
|
|
||||||
extern const std::string kSecret;
|
extern const std::string kSecret;
|
||||||
extern const std::string kLegacyAuth;
|
extern const std::string kLegacyAuth;
|
||||||
|
extern const std::string kApiDebug;
|
||||||
} // namespace API
|
} // namespace API
|
||||||
|
|
||||||
class ApiRetException : public std::runtime_error {
|
class ApiRetException : public std::runtime_error {
|
||||||
@ -249,7 +250,7 @@ Json::Value makeMediaSourceJson(mediakit::MediaSource &media);
|
|||||||
ApiArgsType getAllArgs(const mediakit::Parser &parser);
|
ApiArgsType getAllArgs(const mediakit::Parser &parser);
|
||||||
void getStatisticJson(const std::function<void(Json::Value &val)> &cb);
|
void getStatisticJson(const std::function<void(Json::Value &val)> &cb);
|
||||||
void addStreamProxy(const mediakit::MediaTuple &tuple, const std::string &url, int retry_count, bool force,
|
void addStreamProxy(const mediakit::MediaTuple &tuple, const std::string &url, int retry_count, bool force,
|
||||||
const mediakit::ProtocolOption &option, int rtp_type, float timeout_sec, const toolkit::mINI &args,
|
const mediakit::ProtocolOption &option, float timeout_sec, const toolkit::mINI &args,
|
||||||
const std::function<void(const toolkit::SockException &ex, const std::string &key)> &cb);
|
const std::function<void(const toolkit::SockException &ex, const std::string &key)> &cb);
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
|
|||||||
@ -321,7 +321,7 @@ static void pullStreamFromOrigin(const vector<string> &urls, size_t index, size_
|
|||||||
option.enable_hls = option.enable_hls || (args.schema == HLS_SCHEMA);
|
option.enable_hls = option.enable_hls || (args.schema == HLS_SCHEMA);
|
||||||
option.enable_mp4 = false;
|
option.enable_mp4 = false;
|
||||||
|
|
||||||
addStreamProxy(args, url, retry_count, false, option, Rtsp::RTP_TCP, timeout_sec, mINI{}, [=](const SockException &ex, const string &key) mutable {
|
addStreamProxy(args, url, retry_count, false, option, timeout_sec, mINI{}, [=](const SockException &ex, const string &key) mutable {
|
||||||
if (!ex) {
|
if (!ex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,10 @@
|
|||||||
#include "Util/util.h"
|
#include "Util/util.h"
|
||||||
#include "Util/File.h"
|
#include "Util/File.h"
|
||||||
#include "Common/Parser.h"
|
#include "Common/Parser.h"
|
||||||
|
#include "Common/macros.h"
|
||||||
#include "Http/HttpSession.h"
|
#include "Http/HttpSession.h"
|
||||||
|
#include "Poller/EventPoller.h"
|
||||||
|
#include "WebApi.h"
|
||||||
|
|
||||||
using namespace toolkit;
|
using namespace toolkit;
|
||||||
using namespace mediakit;
|
using namespace mediakit;
|
||||||
@ -102,6 +105,30 @@ mINI to_native(const py::dict &opt) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void python_api_debug(const Parser &parser, const std::string &body) {
|
||||||
|
GET_CONFIG(bool, api_debug, API::kApiDebug);
|
||||||
|
if (!api_debug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ssize_t size = body.size();
|
||||||
|
LogContextCapture log(getLogger(), toolkit::LDebug, __FILE__, "python http api debug", __LINE__);
|
||||||
|
log << "\r\n# request:\r\n" << parser.method() << " " << parser.fullUrl() << "\r\n";
|
||||||
|
log << "# header:\r\n";
|
||||||
|
|
||||||
|
for (auto &pr : parser.getHeader()) {
|
||||||
|
log << pr.first << " : " << pr.second << "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &content = parser.content();
|
||||||
|
log << "# content:\r\n" << (content.size() > 4 * 1024 ? content.substr(0, 4 * 1024) : content) << "\r\n";
|
||||||
|
|
||||||
|
if (size > 0 && size < 4 * 1024) {
|
||||||
|
log << "# response:\r\n" << body << "\r\n";
|
||||||
|
} else {
|
||||||
|
log << "# response size:" << size << "\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void handle_http_request(const py::object &check_route, const py::object &submit_coro, const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, toolkit::SockInfo &sender) {
|
void handle_http_request(const py::object &check_route, const py::object &submit_coro, const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, bool &consumed, toolkit::SockInfo &sender) {
|
||||||
py::gil_scoped_acquire guard;
|
py::gil_scoped_acquire guard;
|
||||||
|
|
||||||
@ -113,7 +140,8 @@ void handle_http_request(const py::object &check_route, const py::object &submit
|
|||||||
scope["query_string"] = parser.params();
|
scope["query_string"] = parser.params();
|
||||||
py::list hdrs;
|
py::list hdrs;
|
||||||
for (auto &kv : parser.getHeader()) {
|
for (auto &kv : parser.getHeader()) {
|
||||||
hdrs.append(py::make_tuple(py::bytes(kv.first), py::bytes(kv.second)));
|
// Starlette/ASGI 规范要求 headers 的 key 必须全小写字节串
|
||||||
|
hdrs.append(py::make_tuple(py::bytes(toolkit::strToLower(kv.first.data())), py::bytes(kv.second)));
|
||||||
}
|
}
|
||||||
scope["headers"] = hdrs;
|
scope["headers"] = hdrs;
|
||||||
|
|
||||||
@ -150,7 +178,7 @@ void handle_http_request(const py::object &check_route, const py::object &submit
|
|||||||
StrCaseMap resp_headers;
|
StrCaseMap resp_headers;
|
||||||
std::string resp_body;
|
std::string resp_body;
|
||||||
int status = 500;
|
int status = 500;
|
||||||
auto send = py::cpp_function([invoker, status, resp_body, resp_headers](const py::dict &msg) mutable {
|
auto send = py::cpp_function([parser, invoker, status, resp_body, resp_headers](const py::dict &msg) mutable {
|
||||||
auto type = msg["type"].cast<std::string>();
|
auto type = msg["type"].cast<std::string>();
|
||||||
if (type == "http.response.start") {
|
if (type == "http.response.start") {
|
||||||
status = msg["status"].cast<int>();
|
status = msg["status"].cast<int>();
|
||||||
@ -166,6 +194,7 @@ void handle_http_request(const py::object &check_route, const py::object &submit
|
|||||||
// 💥 只在 more_body=False 时回调
|
// 💥 只在 more_body=False 时回调
|
||||||
bool more = msg.contains("more_body") && msg["more_body"].cast<bool>();
|
bool more = msg.contains("more_body") && msg["more_body"].cast<bool>();
|
||||||
if (!more) {
|
if (!more) {
|
||||||
|
python_api_debug(parser, resp_body);
|
||||||
invoker(status, resp_headers, resp_body);
|
invoker(status, resp_headers, resp_body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,6 +331,52 @@ PYBIND11_EMBEDDED_MODULE(mk_loader, m) {
|
|||||||
invoker(errMsg, accessPath, cookieLifeSecond);
|
invoker(errMsg, accessPath, cookieLifeSecond);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// add_stream_proxy(vhost, app, stream, url, cb, retry_count=-1, force=False,
|
||||||
|
// rtp_type=0, timeout_sec=0, opt={})
|
||||||
|
// opt 字典可包含 ProtocolOption 的所有字段,以及其他透传给 Player 的 key-value 参数
|
||||||
|
m.def("add_stream_proxy",
|
||||||
|
[](const std::string &vhost, const std::string &app, const std::string &stream,
|
||||||
|
const std::string &url, const py::object &cb,
|
||||||
|
int retry_count, bool force, float timeout_sec,
|
||||||
|
const py::dict &opt) {
|
||||||
|
mINI args = to_native(opt);
|
||||||
|
ProtocolOption option;
|
||||||
|
option.load(args);
|
||||||
|
MediaTuple tuple { vhost.empty() ? DEFAULT_VHOST : vhost, app, stream, "" };
|
||||||
|
|
||||||
|
// 用 shared_ptr 包裹 py::object,使其析构(dec_ref)可在受控环境下执行。
|
||||||
|
// 必须在 GIL 持有期间创建该 shared_ptr(此处仍在 GIL 内)。
|
||||||
|
// 自定义 deleter 保证即使在非 Python 线程析构时也会先获取 GIL。
|
||||||
|
auto cb_ptr = std::shared_ptr<py::object>(
|
||||||
|
new py::object(cb),
|
||||||
|
[](py::object *p) {
|
||||||
|
// dec_ref / 析构 py::object 需要 GIL
|
||||||
|
py::gil_scoped_acquire guard;
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
py::gil_scoped_release release;
|
||||||
|
EventPollerPool::Instance().getPoller(false)->async([=]() mutable {
|
||||||
|
addStreamProxy(tuple, url, retry_count, force, option, timeout_sec, args,
|
||||||
|
[cb_ptr](const SockException &ex, const std::string &key) {
|
||||||
|
// cb_ptr 按值捕获(shared_ptr 的拷贝,纯 C++ 操作,无需 GIL)
|
||||||
|
// inc_ref/dec_ref/调用 Python 对象均在 gil_scoped_acquire 保护下进行
|
||||||
|
py::gil_scoped_acquire guard;
|
||||||
|
try {
|
||||||
|
(*cb_ptr)(ex ? ex.what() : "", key);
|
||||||
|
} catch (py::error_already_set &e) {
|
||||||
|
WarnL << "Python exception in add_stream_proxy callback: " << e.what();
|
||||||
|
}
|
||||||
|
// cb_ptr 在此析构(局部副本),dec_ref 由自定义 deleter 在 GIL 下执行
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
py::arg("vhost"), py::arg("app"), py::arg("stream"), py::arg("url"), py::arg("cb"),
|
||||||
|
py::arg("retry_count") = -1, py::arg("force") = false,
|
||||||
|
py::arg("timeout_sec") = 0.0f, py::arg("opt") = py::dict()
|
||||||
|
);
|
||||||
|
|
||||||
m.def("set_fastapi", [](const py::object &check_route, const py::object &submit_coro) {
|
m.def("set_fastapi", [](const py::object &check_route, const py::object &submit_coro) {
|
||||||
static void *fastapi_tag = nullptr;
|
static void *fastapi_tag = nullptr;
|
||||||
NoticeCenter::Instance().delListener(&fastapi_tag, Broadcast::kBroadcastHttpRequest);
|
NoticeCenter::Instance().delListener(&fastapi_tag, Broadcast::kBroadcastHttpRequest);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user