hls直播支持文件根目录设置在http根目录之外

解决hls保存根目录必须和http根目录一致才能播放的问题
This commit is contained in:
xia-chu 2026-03-09 11:36:51 +08:00
parent b8301bd085
commit eef858ffd3
2 changed files with 43 additions and 37 deletions

View File

@ -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 &params) {
return params;
}
/**
* http客户端是否有权限访问文件的逻辑步骤
* 1http请求头查找cookie3
@ -335,7 +342,7 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo
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);
}
@ -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
@ -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;
}

View File

@ -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 "";
}
}