mirror of
https://gitee.com/xia-chu/ZLMediaKit.git
synced 2026-05-06 10:57:50 +08:00
Compare commits
4 Commits
b8301bd085
...
ca47a1f8b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca47a1f8b2 | ||
|
|
d3bf11b4ee | ||
|
|
f48caf90e8 | ||
|
|
eef858ffd3 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,6 +13,3 @@
|
||||
[submodule "3rdpart/pybind11"]
|
||||
path = 3rdpart/pybind11
|
||||
url = https://gitee.com/mirrors/pybind11.git
|
||||
[submodule "python/StreamUI"]
|
||||
path = python/StreamUI
|
||||
url = https://gitee.com/xia-chu/StreamUI.git
|
||||
|
||||
@ -12,7 +12,4 @@
|
||||
url = https://github.com/1002victor/zlm_webassist
|
||||
[submodule "3rdpart/pybind11"]
|
||||
path = 3rdpart/pybind11
|
||||
url = https://github.com/pybind/pybind11.git
|
||||
[submodule "python/StreamUI"]
|
||||
path = python/StreamUI
|
||||
url = https://github.com/xia-chu/StreamUI.git
|
||||
url = https://github.com/pybind/pybind11.git
|
||||
@ -604,15 +604,6 @@ endif ()
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/www" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/conf/config.ini" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/default.pem" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
|
||||
if (ENABLE_PYTHON)
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/python" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
|
||||
file(GLOB FRONTEND_FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/python/StreamUI/frontend/*"
|
||||
)
|
||||
file(COPY ${FRONTEND_FILES}
|
||||
DESTINATION "${EXECUTABLE_OUTPUT_PATH}/www/StreamUI/"
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_FFMPEG)
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/DejaVuSans.ttf" DESTINATION ${EXECUTABLE_OUTPUT_PATH})
|
||||
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit d055e925a5c061d3ce785f60f07b59ded08b4e55
|
||||
@ -1,27 +0,0 @@
|
||||
import inspect
|
||||
|
||||
try:
|
||||
import mk_loader
|
||||
USE_PLUGIN_LOGGER = True
|
||||
except ImportError:
|
||||
USE_PLUGIN_LOGGER = False
|
||||
|
||||
def _do_log(level: int, *args):
|
||||
frame_info = inspect.stack()[2]
|
||||
filename = frame_info.filename
|
||||
lineno = frame_info.lineno
|
||||
funcname = frame_info.function
|
||||
|
||||
# 把所有参数转成字符串后用空格拼接
|
||||
msg = " ".join(str(arg) for arg in args)
|
||||
|
||||
if USE_PLUGIN_LOGGER:
|
||||
mk_loader.log(level, filename, lineno, funcname, msg)
|
||||
else:
|
||||
print(f"[{filename}:{lineno}] {funcname} | {msg}")
|
||||
|
||||
def log_trace(*args): _do_log(0, *args)
|
||||
def log_debug(*args): _do_log(1, *args)
|
||||
def log_info(*args): _do_log(2, *args)
|
||||
def log_warn(*args): _do_log(3, *args)
|
||||
def log_error(*args): _do_log(4, *args)
|
||||
@ -1,168 +0,0 @@
|
||||
import mk_logger
|
||||
import mk_loader
|
||||
import asyncio
|
||||
import threading
|
||||
from StreamUI.backend.main import app
|
||||
from starlette.routing import Match
|
||||
|
||||
def start_background_loop(loop):
|
||||
asyncio.set_event_loop(loop)
|
||||
loop.run_forever()
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
threading.Thread(target=start_background_loop, args=(loop,), daemon=True).start()
|
||||
|
||||
def submit_coro(scope, body, send):
|
||||
async def run():
|
||||
# 包装 send 函数,确保它总是可等待的
|
||||
async def async_send(message):
|
||||
# 调用原始的 send 函数,它现在应该返回一个协程
|
||||
result = send(message)
|
||||
if result is not None:
|
||||
await result
|
||||
|
||||
async def receive():
|
||||
return {
|
||||
"type": "http.request",
|
||||
"body": body,
|
||||
"more_body": False,
|
||||
}
|
||||
|
||||
try:
|
||||
await app(scope, receive, async_send)
|
||||
except Exception as e:
|
||||
mk_logger.log_warn(f"FastAPI failed: {e}")
|
||||
# 发送错误响应
|
||||
await async_send({
|
||||
"type": "http.response.start",
|
||||
"status": 500,
|
||||
"headers": [(b"content-type", b"text/plain")],
|
||||
})
|
||||
await async_send({
|
||||
"type": "http.response.body",
|
||||
"body": b"Internal Server Error",
|
||||
"more_body": False,
|
||||
})
|
||||
return asyncio.run_coroutine_threadsafe(run(), loop)
|
||||
|
||||
def check_route(scope) -> bool:
|
||||
for route in app.routes:
|
||||
if hasattr(route, "matches"):
|
||||
match, _ = route.matches(scope)
|
||||
if match == Match.FULL:
|
||||
return True
|
||||
return False
|
||||
|
||||
def on_start():
|
||||
mk_logger.log_info(f"on_start, secret: {mk_loader.get_config('api.secret')}")
|
||||
# mk_loader.set_config('api.secret', "new_secret_from_python")
|
||||
# mk_loader.update_config()
|
||||
mk_loader.set_fastapi(check_route, submit_coro)
|
||||
|
||||
def on_exit():
|
||||
mk_logger.log_info("on_exit")
|
||||
|
||||
def on_publish(type: str, args: dict, invoker, sender: dict) -> bool:
|
||||
mk_logger.log_info(f"type: {type}, args: {args}, sender: {sender}")
|
||||
# opt 控制转协议,请参考配置文件[protocol]下字段
|
||||
opt = {
|
||||
"enable_rtmp": "1"
|
||||
}
|
||||
# 响应推流鉴权结果
|
||||
mk_loader.publish_auth_invoker_do(invoker, "", opt)
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_play(args: dict, invoker, sender: dict) -> bool:
|
||||
mk_logger.log_info(f"args: {args}, sender: {sender}")
|
||||
# 响应播放鉴权结果
|
||||
mk_loader.play_auth_invoker_do(invoker, "")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_flow_report(args: dict, totalBytes: int, totalDuration: int, isPlayer: bool, sender: dict) -> bool:
|
||||
mk_logger.log_info(f"args: {args}, totalBytes: {totalBytes}, totalDuration: {totalDuration}, isPlayer: {isPlayer}, sender: {sender}")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_media_changed(is_register: bool, sender: mk_loader.MediaSource) -> bool:
|
||||
mk_logger.log_info(f"is_register: {is_register}, sender: {sender.getUrl()}")
|
||||
# 该事件在c++中也处理下
|
||||
return False
|
||||
|
||||
def on_player_proxy_failed(url: str, media_tuple: mk_loader.MediaTuple , ex: mk_loader.SockException) -> bool:
|
||||
mk_logger.log_info(f"on_player_proxy_failed: {url}, {media_tuple.shortUrl()}, {ex.what()}")
|
||||
# 该事件在c++中也处理下
|
||||
return False
|
||||
|
||||
def on_get_rtsp_realm(args: dict, invoker, sender: dict) -> bool:
|
||||
mk_logger.log_info(f"on_get_rtsp_realm, args: {args}, sender: {sender}")
|
||||
mk_loader.rtsp_get_realm_invoker_do(invoker, "zlmediakit")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_rtsp_auth(args: dict, realm: str, user_name: str, must_no_encrypt: bool, invoker, sender:dict) -> bool:
|
||||
mk_logger.log_info(f"on_rtsp_auth, args: {args}, realm: {realm}, user_name: {user_name}, must_no_encrypt: {must_no_encrypt}, sender: {sender}")
|
||||
mk_loader.rtsp_auth_invoker_do(invoker, False, "zlmediakit")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_stream_not_found(args: dict, sender:dict, invoker) -> bool:
|
||||
mk_logger.log_info(f"on_stream_not_found, args: {args}, sender: {sender}")
|
||||
# 立即通知播放器流不存在并关闭
|
||||
mk_loader.close_player_invoker_do(invoker)
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_record_mp4(info: dict) -> bool:
|
||||
mk_logger.log_info(f"on_record_mp4, info: {info}")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
def on_record_ts(info: dict) -> bool:
|
||||
mk_logger.log_info(f"on_record_ts, info: {info}")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_stream_none_reader(sender: mk_loader.MediaSource) -> bool:
|
||||
mk_logger.log_info(f"on_stream_none_reader: {sender.getUrl()}")
|
||||
# 无人观看自动关闭
|
||||
sender.close(False)
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_send_rtp_stopped(sender: mk_loader.MultiMediaSourceMuxer, ssrc: str, ex: mk_loader.SockException) -> bool:
|
||||
mk_logger.log_info(f"on_send_rtp_stopped, ssrc: {ssrc}, ex: {ex.what()}, url: {sender.getMediaTuple().getUrl()}")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_http_access(parser: mk_loader.Parser, path: str, is_dir: bool, invoker, sender: dict) -> bool:
|
||||
mk_logger.log_info(f"on_http_access, path: {path}, is_dir: {is_dir}, sender: {sender}, http header: {parser.getHeader()}")
|
||||
# 允许访问该文件/目录1小时, cookie有效期内,访问该目录下的文件或路径不再触发该事件
|
||||
mk_loader.http_access_invoker_do(invoker, "", path, 60 * 60)
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_rtp_server_timeout(local_port: int, tuple: mk_loader.MediaTuple, tcp_mode: int, re_use_port: bool, ssrc: int) -> bool:
|
||||
mk_logger.log_info(f"on_rtp_server_timeout, local_port: {local_port}, tuple: {tuple.shortUrl()}, tcp_mode: {tcp_mode}, re_use_port: {re_use_port}, ssrc: {ssrc}")
|
||||
# 返回True代表此事件被python拦截
|
||||
return True
|
||||
|
||||
def on_reload_config():
|
||||
mk_logger.log_info(f"on_reload_config")
|
||||
|
||||
class PyMultiMediaSourceMuxer:
|
||||
def __init__(self, sender: mk_loader.MultiMediaSourceMuxer):
|
||||
mk_logger.log_info(f"PyMultiMediaSourceMuxer: {sender.getMediaTuple().shortUrl()}")
|
||||
def destroy(self):
|
||||
mk_logger.log_info(f"~PyMultiMediaSourceMuxer")
|
||||
|
||||
def addTrack(self, track: mk_loader.Track):
|
||||
mk_logger.log_info(f"addTrack: {track.getCodecName()}")
|
||||
return True
|
||||
def addTrackCompleted(self):
|
||||
mk_logger.log_info(f"addTrackCompleted")
|
||||
def inputFrame(self, frame: mk_loader.Frame):
|
||||
mk_logger.log_info(f"inputFrame: {frame.getCodecName()} {frame.dts()}")
|
||||
return True
|
||||
def on_create_muxer(sender: mk_loader.MultiMediaSourceMuxer):
|
||||
return PyMultiMediaSourceMuxer(sender)
|
||||
@ -2374,15 +2374,20 @@ void installWebApi() {
|
||||
}
|
||||
};
|
||||
|
||||
bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, allArgs.parser, file_path, false, file_invoker, sender);
|
||||
if (!flag) {
|
||||
// 文件下载鉴权事件无人监听,不允许下载 [AUTO-TRANSLATED:5e02f0ce]
|
||||
// No one is listening to the file download authentication event, download is not allowed
|
||||
invoker(401, StrCaseMap {}, "None http access event listener");
|
||||
try {
|
||||
CHECK_SECRET();
|
||||
// 校验secret成功,文件下载鉴权成功
|
||||
file_invoker("", "", 0);
|
||||
} catch (...) {
|
||||
bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, allArgs.parser, allArgs.parser.url(), file_path, false, file_invoker, sender);
|
||||
if (!flag) {
|
||||
// 文件下载鉴权事件无人监听,不允许下载 [AUTO-TRANSLATED:5e02f0ce]
|
||||
// No one is listening to the file download authentication event, download is not allowed
|
||||
invoker(401, StrCaseMap {}, "None http access event listener");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
api_regist("/index/api/searchOnvifDevice",[](API_ARGS_MAP_ASYNC){
|
||||
CHECK_SECRET();
|
||||
CHECK_ARGS("timeout_ms");
|
||||
|
||||
@ -783,7 +783,7 @@ void installWebHook() {
|
||||
// The purpose of tracking users is to cache the last authentication result, reduce the number of authentication times, and improve performance
|
||||
NoticeCenter::Instance().addListener(&web_hook_tag, Broadcast::kBroadcastHttpAccess, [](BroadcastHttpAccessArgs) {
|
||||
#if defined(ENABLE_PYTHON)
|
||||
if (PythonInvoker::Instance().on_http_access(parser, path, is_dir, invoker, sender)) {
|
||||
if (PythonInvoker::Instance().on_http_access(parser, path, file_path, is_dir, invoker, sender)) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@ -806,6 +806,7 @@ void installWebHook() {
|
||||
body["port"] = sender.get_peer_port();
|
||||
body["id"] = sender.getIdentifier();
|
||||
body["path"] = path;
|
||||
body["file_path"] = file_path;
|
||||
body["is_dir"] = is_dir;
|
||||
body["params"] = parser.params();
|
||||
for (auto &pr : parser.getHeader()) {
|
||||
|
||||
@ -692,7 +692,7 @@ bool PythonInvoker::on_http_access(BroadcastHttpAccessArgs) const {
|
||||
if (!_on_http_access) {
|
||||
return false;
|
||||
}
|
||||
return _on_http_access(to_python_ref(parser), path, is_dir, to_python(invoker), to_python(sender)).cast<bool>();
|
||||
return _on_http_access(to_python_ref(parser), path, file_path, is_dir, to_python(invoker), to_python(sender)).cast<bool>();
|
||||
}
|
||||
|
||||
bool PythonInvoker::on_rtp_server_timeout(BroadcastRtpServerTimeoutArgs) const {
|
||||
|
||||
@ -60,7 +60,7 @@ extern const std::string kBroadcastHttpRequest;
|
||||
// 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 [AUTO-TRANSLATED:2de426b4]
|
||||
// In the http file server, broadcast for receiving http access to files or directories. Control access permissions to the http directory through this event.
|
||||
extern const std::string kBroadcastHttpAccess;
|
||||
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, toolkit::SockInfo &sender
|
||||
#define BroadcastHttpAccessArgs const Parser &parser, const std::string &path, const std::string &file_path, const bool &is_dir, const HttpSession::HttpAccessPathInvoker &invoker, toolkit::SockInfo &sender
|
||||
|
||||
// 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 [AUTO-TRANSLATED:0294d0c5]
|
||||
// In the http file server, broadcast before receiving http access to files or directories. Control the mapping from http url to file path through this event.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "HttpConst.h"
|
||||
#include "HttpSession.h"
|
||||
#include "HttpFileManager.h"
|
||||
#include "Common/MultiMediaSourceMuxer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace toolkit;
|
||||
@ -48,6 +49,8 @@ struct HttpCookieAttachment {
|
||||
// 上次鉴权失败信息,为空则上次鉴权成功 [AUTO-TRANSLATED:de48b753]
|
||||
// Last authentication failure information, empty means last authentication succeeded
|
||||
string _err_msg;
|
||||
// hls文件根目录
|
||||
string _hls_root_path;
|
||||
// hls直播时的其他一些信息,主要用于播放器个数计数以及流量计数 [AUTO-TRANSLATED:790de53a]
|
||||
// Other information during hls live broadcast, mainly used for player count and traffic count
|
||||
HlsCookieData::Ptr _hls_data;
|
||||
@ -315,6 +318,10 @@ static bool emitHlsPlayed(const Parser &parser, const MediaInfo &media_info, con
|
||||
return flag;
|
||||
}
|
||||
|
||||
static std::string getUidFromParams(const string ¶ms) {
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断http客户端是否有权限访问文件的逻辑步骤
|
||||
* 1、根据http请求头查找cookie,找到进入步骤3
|
||||
@ -331,11 +338,11 @@ static bool emitHlsPlayed(const Parser &parser, const MediaInfo &media_info, con
|
||||
|
||||
* [AUTO-TRANSLATED:dfc0f15f]
|
||||
*/
|
||||
static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo &media_info, bool is_dir,
|
||||
static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo &media_info, const std::string &file_path, bool is_dir,
|
||||
const function<void(const string &err_msg, const HttpServerCookie::Ptr &cookie)> &callback) {
|
||||
// 获取用户唯一id [AUTO-TRANSLATED:5b1cf4bf]
|
||||
// Get the user's unique id
|
||||
auto uid = parser.params();
|
||||
auto uid = getUidFromParams(parser.params());
|
||||
auto path = parser.url();
|
||||
|
||||
// 先根据http头中的cookie字段获取cookie [AUTO-TRANSLATED:155cf682]
|
||||
@ -370,7 +377,7 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo
|
||||
}
|
||||
// 上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 [AUTO-TRANSLATED:df9bd345]
|
||||
// Last authentication failed, but if the url parameter changes, then re-authenticate
|
||||
if (parser.params().empty() || parser.params() == cookie->getUid()) {
|
||||
if (parser.params().empty() || getUidFromParams(parser.params()) == cookie->getUid()) {
|
||||
// url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 [AUTO-TRANSLATED:f46b4fca]
|
||||
// The url parameter has not changed, or there is no url parameter at all, then determine that the current request is a duplicate request and has no access permission
|
||||
callback(attach._err_msg, update_cookie ? cookie : nullptr);
|
||||
@ -419,9 +426,9 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo
|
||||
// hls related information
|
||||
attach->_hls_data = std::make_shared<HlsCookieData>(media_info, strong_session);
|
||||
}
|
||||
toolkit::Any any;
|
||||
any.set(std::move(attach));
|
||||
callback(err_msg, HttpCookieManager::Instance().addCookie(kCookieName, uid, life_second, std::move(any)));
|
||||
toolkit::Any any;
|
||||
any.set(std::move(attach));
|
||||
callback(err_msg, HttpCookieManager::Instance().addCookie(kCookieName, uid, life_second, std::move(any)));
|
||||
} else {
|
||||
callback(err_msg, nullptr);
|
||||
}
|
||||
@ -436,7 +443,7 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo
|
||||
|
||||
// 事件未被拦截,则认为是http下载请求 [AUTO-TRANSLATED:7d449ccc]
|
||||
// The event was not intercepted, it is considered an http download request
|
||||
bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, parser, path, is_dir, accessPathInvoker, sender);
|
||||
bool flag = NOTICE_EMIT(BroadcastHttpAccessArgs, Broadcast::kBroadcastHttpAccess, parser, path, file_path, is_dir, accessPathInvoker, sender);
|
||||
if (!flag) {
|
||||
// 此事件无人监听,我们默认都有权限访问 [AUTO-TRANSLATED:e1524c0f]
|
||||
// No one is listening to this event, we assume that everyone has permission to access it by default
|
||||
@ -468,6 +475,8 @@ static string pathCat(const string &a, const string &b){
|
||||
return a + '/' + b;
|
||||
}
|
||||
|
||||
static string getFilePath(const Parser &parser,const MediaInfo &media_info, Session *sender, const string &customRootPath = "");
|
||||
|
||||
/**
|
||||
* 访问文件
|
||||
* @param sender 事件触发者
|
||||
@ -481,17 +490,11 @@ static string pathCat(const string &a, const string &b){
|
||||
* @param media_info http url information
|
||||
* @param file_path Absolute file path
|
||||
* @param cb Callback object
|
||||
|
||||
|
||||
* [AUTO-TRANSLATED:2d840fe6]
|
||||
*/
|
||||
static void accessFile(Session &sender, const Parser &parser, const MediaInfo &media_info, const string &file_path, const HttpFileManager::invoker &cb) {
|
||||
bool is_hls = end_with(file_path, kHlsSuffix) || end_with(file_path, kHlsFMP4Suffix);
|
||||
if (!is_hls && !File::fileExist(file_path)) {
|
||||
// 文件不存在且不是hls,那么直接返回404 [AUTO-TRANSLATED:7aae578b]
|
||||
// The file does not exist and is not hls, so directly return 404
|
||||
sendNotFound(cb);
|
||||
return;
|
||||
}
|
||||
if (is_hls) {
|
||||
// hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS [AUTO-TRANSLATED:94b5818a]
|
||||
// hls, then remove the suffix to get the real stream_id and change the protocol to HLS
|
||||
@ -507,7 +510,7 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m
|
||||
weak_ptr<Session> weakSession = static_pointer_cast<Session>(sender.shared_from_this());
|
||||
// 判断是否有权限访问该文件 [AUTO-TRANSLATED:b7f595f5]
|
||||
// Determine whether you have permission to access this file
|
||||
canAccessPath(sender, parser, media_info, false, [cb, file_path, parser, is_hls, media_info, weakSession](const string &err_msg, const HttpServerCookie::Ptr &cookie) {
|
||||
canAccessPath(sender, parser, media_info, file_path, false, [cb, file_path, parser, is_hls, media_info, weakSession](const string &err_msg, const HttpServerCookie::Ptr &cookie) {
|
||||
auto strongSession = weakSession.lock();
|
||||
if (!strongSession) {
|
||||
// http客户端已经断开,不需要回复 [AUTO-TRANSLATED:9a252e21]
|
||||
@ -552,6 +555,13 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m
|
||||
invoker.responseFile(parser.getHeader(), httpHeader, file_content.empty() ? file_path : file_content, !is_hls && !is_forbid_cache, file_content.empty());
|
||||
};
|
||||
|
||||
if (cookie) {
|
||||
auto &attach = cookie->getAttach<HttpCookieAttachment>();
|
||||
if (!attach._hls_root_path.empty()) {
|
||||
// 强制替换为真实hls路径
|
||||
const_cast<std::string &>(file_path) = getFilePath(parser, media_info, nullptr, attach._hls_root_path);
|
||||
}
|
||||
}
|
||||
if (!is_hls || !cookie) {
|
||||
// 不是hls或访问m3u8文件不带cookie, 直接回复文件或404 [AUTO-TRANSLATED:64e5d19b]
|
||||
// Not hls or accessing m3u8 files without cookies, directly reply to the file or 404
|
||||
@ -601,6 +611,10 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m
|
||||
// Reset the MediaSource search timer
|
||||
attach._find_src_ticker.resetTime();
|
||||
|
||||
auto muxer = hls->getMuxer();
|
||||
if (muxer) {
|
||||
attach._hls_root_path = muxer->getOption().hls_save_path;
|
||||
}
|
||||
// m3u8文件可能不存在, 等待m3u8索引文件按需生成 [AUTO-TRANSLATED:0dbd4df2]
|
||||
// The m3u8 file may not exist, wait for the m3u8 index file to be generated on demand
|
||||
hls->getIndexFile([response_file, file_path, cookie, cb, parser](const string &file) {
|
||||
@ -610,13 +624,15 @@ static void accessFile(Session &sender, const Parser &parser, const MediaInfo &m
|
||||
});
|
||||
}
|
||||
|
||||
static string getFilePath(const Parser &parser,const MediaInfo &media_info, Session &sender) {
|
||||
static string getFilePath(const Parser &parser,const MediaInfo &media_info, Session *sender, const string &customRootPath) {
|
||||
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
|
||||
GET_CONFIG(string, rootPath, Http::kRootPath);
|
||||
GET_CONFIG(string, httpRootPath, Http::kRootPath);
|
||||
GET_CONFIG_FUNC(StrCaseMap, virtualPathMap, Http::kVirtualPath, [](const string &str) {
|
||||
return Parser::parseArgs(str, ";", ",");
|
||||
});
|
||||
|
||||
auto rootPath = customRootPath.empty() ? httpRootPath : customRootPath;
|
||||
|
||||
string url, path, virtual_app;
|
||||
auto it = virtualPathMap.find(media_info.app);
|
||||
if (it != virtualPathMap.end()) {
|
||||
@ -645,10 +661,12 @@ static string getFilePath(const Parser &parser,const MediaInfo &media_info, Sess
|
||||
// The accessed http file must not be outside the http root directory
|
||||
throw std::runtime_error("Attempting to access files outside of the http root directory");
|
||||
}
|
||||
// 替换url,防止返回的目录索引网页被注入非法内容 [AUTO-TRANSLATED:463ad1b1]
|
||||
// Replace the url to prevent the returned directory index page from being injected with illegal content
|
||||
const_cast<Parser&>(parser).setUrl("/" + virtual_app + ret.substr(http_root.size()));
|
||||
NOTICE_EMIT(BroadcastHttpBeforeAccessArgs, Broadcast::kBroadcastHttpBeforeAccess, parser, ret, sender);
|
||||
if (sender) {
|
||||
// 替换url,防止返回的目录索引网页被注入非法内容 [AUTO-TRANSLATED:463ad1b1]
|
||||
// Replace the url to prevent the returned directory index page from being injected with illegal content
|
||||
const_cast<Parser&>(parser).setUrl("/" + virtual_app + ret.substr(http_root.size()));
|
||||
NOTICE_EMIT(BroadcastHttpBeforeAccessArgs, Broadcast::kBroadcastHttpBeforeAccess, parser, ret, *sender);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -661,14 +679,14 @@ static string getFilePath(const Parser &parser,const MediaInfo &media_info, Sess
|
||||
* @param sender Event trigger
|
||||
* @param parser http request
|
||||
* @param cb Callback object
|
||||
|
||||
|
||||
* [AUTO-TRANSLATED:a79c824d]
|
||||
*/
|
||||
void HttpFileManager::onAccessPath(Session &sender, Parser &parser, const HttpFileManager::invoker &cb) {
|
||||
auto fullUrl = "http://" + parser["Host"] + parser.fullUrl();
|
||||
MediaInfo media_info(fullUrl);
|
||||
auto file_path = getFilePath(parser, media_info, sender);
|
||||
if (file_path.size() == 0) {
|
||||
auto file_path = getFilePath(parser, media_info, &sender);
|
||||
if (file_path.empty()) {
|
||||
sendNotFound(cb);
|
||||
return;
|
||||
}
|
||||
@ -699,7 +717,7 @@ void HttpFileManager::onAccessPath(Session &sender, Parser &parser, const HttpFi
|
||||
}
|
||||
// 判断是否有权限访问该目录 [AUTO-TRANSLATED:963d02a6]
|
||||
// Determine if there is permission to access this directory
|
||||
canAccessPath(sender, parser, media_info, true, [strMenu, cb](const string &err_msg, const HttpServerCookie::Ptr &cookie) mutable{
|
||||
canAccessPath(sender, parser, media_info, file_path, true, [strMenu, cb](const string &err_msg, const HttpServerCookie::Ptr &cookie) mutable{
|
||||
if (!err_msg.empty()) {
|
||||
strMenu = err_msg;
|
||||
}
|
||||
|
||||
@ -25,13 +25,15 @@ namespace mediakit {
|
||||
string Recorder::getRecordPath(Recorder::type type, const MediaTuple& tuple, const string &customized_path) {
|
||||
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
|
||||
switch (type) {
|
||||
case Recorder::type_hls_fmp4:
|
||||
case Recorder::type_hls: {
|
||||
GET_CONFIG(string, hlsPath, Protocol::kHlsSavePath);
|
||||
string m3u8FilePath;
|
||||
auto tail = type == Recorder::type_hls ? "/hls.m3u8" : "/hls.fmp4.m3u8";
|
||||
if (enableVhost) {
|
||||
m3u8FilePath = tuple.shortUrl() + "/hls.m3u8";
|
||||
m3u8FilePath = tuple.shortUrl() + tail;
|
||||
} else {
|
||||
m3u8FilePath = tuple.app + "/" + tuple.stream + "/hls.m3u8";
|
||||
m3u8FilePath = tuple.app + "/" + tuple.stream + tail;
|
||||
}
|
||||
//Here we use the customized file path.
|
||||
if (!customized_path.empty()) {
|
||||
@ -54,20 +56,6 @@ string Recorder::getRecordPath(Recorder::type type, const MediaTuple& tuple, con
|
||||
}
|
||||
return File::absolutePath(mp4FilePath, recordPath);
|
||||
}
|
||||
case Recorder::type_hls_fmp4: {
|
||||
GET_CONFIG(string, hlsPath, Protocol::kHlsSavePath);
|
||||
string m3u8FilePath;
|
||||
if (enableVhost) {
|
||||
m3u8FilePath = tuple.shortUrl() + "/hls.fmp4.m3u8";
|
||||
} else {
|
||||
m3u8FilePath = tuple.app + "/" + tuple.stream + "/hls.fmp4.m3u8";
|
||||
}
|
||||
// Here we use the customized file path.
|
||||
if (!customized_path.empty()) {
|
||||
return File::absolutePath(m3u8FilePath, customized_path);
|
||||
}
|
||||
return File::absolutePath(m3u8FilePath, hlsPath);
|
||||
}
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user