diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index a38e28fe..9bc4dae9 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -76,15 +76,17 @@ void HlsPlayer::fetchSegment() { } return Socket::createSocket(poller, true); }); - - _http_ts_player->setOnPacket([weak_self](const char *data, size_t len) { - auto strong_self = weak_self.lock(); - if (!strong_self) { - return; - } - //收到ts包 - strong_self->onPacket_l(data, len); - }); + auto benchmark_mode = (*this)[Client::kBenchmarkMode].as(); + if (!benchmark_mode) { + _http_ts_player->setOnPacket([weak_self](const char *data, size_t len) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + //收到ts包 + strong_self->onPacket_l(data, len); + }); + } if (!(*this)[Client::kNetAdapter].empty()) { _http_ts_player->setNetAdapter((*this)[Client::kNetAdapter]); @@ -349,7 +351,8 @@ void HlsPlayerImp::addTrackCompleted() { } void HlsPlayerImp::onPlayResult(const SockException &ex) { - if (ex) { + auto benchmark_mode = (*this)[Client::kBenchmarkMode].as(); + if (ex || benchmark_mode) { PlayerImp::onPlayResult(ex); } else { auto demuxer = std::make_shared(); @@ -364,6 +367,9 @@ void HlsPlayerImp::onShutdown(const SockException &ex) { } vector HlsPlayerImp::getTracks(bool ready) const { + if (!_demuxer) { + return vector(); + } return static_pointer_cast(_demuxer)->getTracks(ready); } diff --git a/src/Http/HttpBody.cpp b/src/Http/HttpBody.cpp index 922d4811..63e632eb 100644 --- a/src/Http/HttpBody.cpp +++ b/src/Http/HttpBody.cpp @@ -9,16 +9,24 @@ */ #include -#include "HttpBody.h" -#include "Util/util.h" -#include "Util/File.h" -#include "Util/uv_errno.h" -#include "Util/logger.h" -#include "Util/onceToken.h" -#include "HttpClient.h" +#include + #ifndef _WIN32 #include #endif +#if defined(__linux__) || defined(__linux) +#include +#endif + +#include "Util/File.h" +#include "Util/logger.h" +#include "Util/onceToken.h" +#include "Util/util.h" +#include "Util/uv_errno.h" + +#include "HttpBody.h" +#include "HttpClient.h" +#include "Common/macros.h" #ifndef _WIN32 #define ENABLE_MMAP @@ -29,94 +37,146 @@ using namespace toolkit; namespace mediakit { -HttpStringBody::HttpStringBody(string str){ +HttpStringBody::HttpStringBody(string str) { _str = std::move(str); } -ssize_t HttpStringBody::remainSize() { +int64_t HttpStringBody::remainSize() { return _str.size() - _offset; } Buffer::Ptr HttpStringBody::readData(size_t size) { size = MIN((size_t)remainSize(), size); - if(!size){ + if (!size) { //没有剩余字节了 return nullptr; } - auto ret = std::make_shared(_str,_offset,size); + auto ret = std::make_shared(_str, _offset, size); _offset += size; return ret; } ////////////////////////////////////////////////////////////////// -HttpFileBody::HttpFileBody(const string &filePath, bool use_mmap) { - std::shared_ptr fp(fopen(filePath.data(), "rb"), [](FILE *fp) { +#ifdef ENABLE_MMAP + +static mutex s_mtx; +static unordered_map /*mmap*/ > > s_shared_mmap; + +//删除mmap记录 +static void delSharedMmap(const string &file_path, char *ptr) { + lock_guard lck(s_mtx); + auto it = s_shared_mmap.find(file_path); + if (it != s_shared_mmap.end() && std::get<0>(it->second) == ptr) { + s_shared_mmap.erase(it); + } +} + +static std::shared_ptr getSharedMmap(const string &file_path, int64_t &file_size) { + { + lock_guard lck(s_mtx); + auto it = s_shared_mmap.find(file_path); + if (it != s_shared_mmap.end()) { + auto ret = std::get<2>(it->second).lock(); + if (ret) { + //命中mmap缓存 + file_size = std::get<1>(it->second); + return ret; + } + } + } + + //打开文件 + std::shared_ptr fp(fopen(file_path.data(), "rb"), [](FILE *fp) { if (fp) { fclose(fp); } }); if (!fp) { - init(fp, 0, 0, use_mmap); - } else { - init(fp, 0, File::fileSize(fp.get()), use_mmap); + //文件不存在 + file_size = -1; + return nullptr; + } + //获取文件大小 + file_size = File::fileSize(fp.get()); + + int fd = fileno(fp.get()); + if (fd < 0) { + WarnL << "fileno failed:" << get_uv_errmsg(false); + return nullptr; + } + auto ptr = (char *)mmap(NULL, file_size, PROT_READ, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) { + WarnL << "mmap " << file_path << " failed:" << get_uv_errmsg(false); + return nullptr; + } + std::shared_ptr ret(ptr, [file_size, fp, file_path](char *ptr) { + munmap(ptr, file_size); + delSharedMmap(file_path, ptr); + }); + +#if 0 + if (file_size < 10 * 1024 * 1024 && file_path.rfind(".ts") != string::npos) { + //如果是小ts文件,那么尝试先加载到内存 + auto buf = BufferRaw::create(); + buf->assign(ret.get(), file_size); + ret.reset(buf->data(), [buf, file_path](char *ptr) { + delSharedMmap(file_path, ptr); + }); + } +#endif + { + lock_guard lck(s_mtx); + s_shared_mmap[file_path] = std::make_tuple(ret.get(), file_size, ret); + } + return ret; +} +#endif + +HttpFileBody::HttpFileBody(const string &file_path, bool use_mmap) { +#ifdef ENABLE_MMAP + if (use_mmap ) { + _map_addr = getSharedMmap(file_path, _read_to); + } +#endif + if (!_map_addr && _read_to != -1) { + _fp.reset(fopen(file_path.data(), "rb"), [](FILE *fp) { + if (fp) { + fclose(fp); + } + }); + if (!_fp) { + //文件不存在 + _read_to = -1; + return; + } + _read_to = File::fileSize(_fp.get()); } } -HttpFileBody::HttpFileBody(const std::shared_ptr &fp, size_t offset, size_t max_size, bool use_mmap) { - init(fp, offset, max_size, use_mmap); +void HttpFileBody::setRange(uint64_t offset, uint64_t max_size) { + CHECK((int64_t)offset <= _read_to && (int64_t)(max_size + offset) <= _read_to); + _read_to = max_size + offset; + _file_offset = offset; + if (_fp && !_map_addr) { + fseek64(_fp.get(), _file_offset, SEEK_SET); + } } -#if defined(__linux__) || defined(__linux) -#include -#endif - int HttpFileBody::sendFile(int fd) { #if defined(__linux__) || defined(__linux) - static onceToken s_token([]() { - signal(SIGPIPE, SIG_IGN); - }); + if (!_fp) { + return -1; + } + static onceToken s_token([]() { signal(SIGPIPE, SIG_IGN); }); off_t off = _file_offset; - return sendfile(fd, fileno(_fp.get()), &off, _max_size); + return sendfile(fd, fileno(_fp.get()), &off, _read_to - _file_offset); #else return -1; #endif } -void HttpFileBody::init(const std::shared_ptr &fp, size_t offset, size_t max_size, bool use_mmap) { - _fp = fp; - _max_size = max_size; - _file_offset = offset; -#ifdef ENABLE_MMAP - if (use_mmap) { - do { - if (!_fp) { - //文件不存在 - break; - } - int fd = fileno(fp.get()); - if (fd < 0) { - WarnL << "fileno failed:" << get_uv_errmsg(false); - break; - } - auto ptr = (char *) mmap(NULL, max_size, PROT_READ, MAP_SHARED, fd, offset); - if (ptr == MAP_FAILED) { - WarnL << "mmap failed:" << get_uv_errmsg(false); - break; - } - _map_addr.reset(ptr, [max_size, fp](char *ptr) { - munmap(ptr, max_size); - }); - } while (false); - } -#endif - if (!_map_addr && offset && fp.get()) { - //未映射,那么fseek设置偏移量 - fseek64(fp.get(), offset, SEEK_SET); - } -} - -class BufferMmap : public Buffer{ +class BufferMmap : public Buffer { public: typedef std::shared_ptr Ptr; BufferMmap(const std::shared_ptr &map_addr, size_t offset, size_t size) { @@ -124,103 +184,96 @@ public: _data = map_addr.get() + offset; _size = size; } - ~BufferMmap() override{}; + ~BufferMmap() override {}; //返回数据长度 - char *data() const override { - return _data; - } - size_t size() const override{ - return _size; - } + char *data() const override { return _data; } + size_t size() const override { return _size; } + private: - std::shared_ptr _map_addr; char *_data; size_t _size; + std::shared_ptr _map_addr; }; -ssize_t HttpFileBody::remainSize() { - return _max_size - _offset; +int64_t HttpFileBody::remainSize() { + return _read_to - _file_offset; } Buffer::Ptr HttpFileBody::readData(size_t size) { - size = MIN((size_t)remainSize(),size); - if(!size){ + size = MIN((size_t)remainSize(), size); + if (!size) { //没有剩余字节了 return nullptr; } - if(!_map_addr){ - //fread模式 + if (!_map_addr) { + // fread模式 ssize_t iRead; auto ret = _pool.obtain2(); ret->setCapacity(size + 1); - do{ + do { iRead = fread(ret->data(), 1, size, _fp.get()); - }while(-1 == iRead && UV_EINTR == get_uv_error(false)); + } while (-1 == iRead && UV_EINTR == get_uv_error(false)); - if(iRead > 0){ + if (iRead > 0) { //读到数据了 ret->setSize(iRead); - _offset += iRead; + _file_offset += iRead; return std::move(ret); } //读取文件异常,文件真实长度小于声明长度 - _offset = _max_size; + _file_offset = _read_to; WarnL << "read file err:" << get_uv_errmsg(); return nullptr; } - //mmap模式 - auto ret = std::make_shared(_map_addr,_offset,size); - _offset += size; + // mmap模式 + auto ret = std::make_shared(_map_addr, _file_offset, size); + _file_offset += size; return ret; } ////////////////////////////////////////////////////////////////// -HttpMultiFormBody::HttpMultiFormBody(const HttpArgs &args,const string &filePath,const string &boundary){ - std::shared_ptr fp(fopen(filePath.data(), "rb"), [](FILE *fp) { - if(fp){ - fclose(fp); - } - }); - if(!fp){ + +HttpMultiFormBody::HttpMultiFormBody(const HttpArgs &args, const string &filePath, const string &boundary) { + _fileBody = std::make_shared(filePath); + if (_fileBody->remainSize() < 0) { throw std::invalid_argument(StrPrinter << "open file failed:" << filePath << " " << get_uv_errmsg()); } - _fileBody = std::make_shared(fp, 0, File::fileSize(fp.get())); auto fileName = filePath; auto pos = filePath.rfind('/'); - if(pos != string::npos){ + if (pos != string::npos) { fileName = filePath.substr(pos + 1); } - _bodyPrefix = multiFormBodyPrefix(args,boundary,fileName); + _bodyPrefix = multiFormBodyPrefix(args, boundary, fileName); _bodySuffix = multiFormBodySuffix(boundary); - _totalSize = _bodyPrefix.size() + _bodySuffix.size() + _fileBody->remainSize(); + _totalSize = _bodyPrefix.size() + _bodySuffix.size() + _fileBody->remainSize(); } -ssize_t HttpMultiFormBody::remainSize() { +int64_t HttpMultiFormBody::remainSize() { return _totalSize - _offset; } -Buffer::Ptr HttpMultiFormBody::readData(size_t size){ - if(_bodyPrefix.size()){ +Buffer::Ptr HttpMultiFormBody::readData(size_t size) { + if (_bodyPrefix.size()) { auto ret = std::make_shared(_bodyPrefix); _offset += _bodyPrefix.size(); _bodyPrefix.clear(); return ret; } - if(_fileBody->remainSize()){ + if (_fileBody->remainSize()) { auto ret = _fileBody->readData(size); - if(!ret){ + if (!ret) { //读取文件出现异常,提前中断 _offset = _totalSize; - }else{ + } else { _offset += ret->size(); } return ret; } - if(_bodySuffix.size()){ + if (_bodySuffix.size()) { auto ret = std::make_shared(_bodySuffix); _offset = _totalSize; _bodySuffix.clear(); @@ -230,7 +283,7 @@ Buffer::Ptr HttpMultiFormBody::readData(size_t size){ return nullptr; } -string HttpMultiFormBody::multiFormBodySuffix(const string &boundary){ +string HttpMultiFormBody::multiFormBodySuffix(const string &boundary) { string MPboundary = string("--") + boundary; string endMPboundary = MPboundary + "--"; _StrPrinter body; @@ -238,21 +291,23 @@ string HttpMultiFormBody::multiFormBodySuffix(const string &boundary){ return std::move(body); } -string HttpMultiFormBody::multiFormContentType(const string &boundary){ +string HttpMultiFormBody::multiFormContentType(const string &boundary) { return StrPrinter << "multipart/form-data; boundary=" << boundary; } -string HttpMultiFormBody::multiFormBodyPrefix(const HttpArgs &args,const string &boundary,const string &fileName){ +string HttpMultiFormBody::multiFormBodyPrefix(const HttpArgs &args, const string &boundary, const string &fileName) { string MPboundary = string("--") + boundary; _StrPrinter body; - for(auto &pr : args){ + for (auto &pr : args) { body << MPboundary << "\r\n"; body << "Content-Disposition: form-data; name=\"" << pr.first << "\"\r\n\r\n"; body << pr.second << "\r\n"; } body << MPboundary << "\r\n"; - body << "Content-Disposition: form-data; name=\"" << "file" << "\";filename=\"" << fileName << "\"\r\n"; - body << "Content-Type: application/octet-stream\r\n\r\n" ; + body << "Content-Disposition: form-data; name=\"" + << "file" + << "\";filename=\"" << fileName << "\"\r\n"; + body << "Content-Type: application/octet-stream\r\n\r\n"; return std::move(body); } @@ -260,7 +315,7 @@ HttpBufferBody::HttpBufferBody(Buffer::Ptr buffer) { _buffer = std::move(buffer); } -ssize_t HttpBufferBody::remainSize() { +int64_t HttpBufferBody::remainSize() { return _buffer ? _buffer->size() : 0; } @@ -268,4 +323,4 @@ Buffer::Ptr HttpBufferBody::readData(size_t size) { return Buffer::Ptr(std::move(_buffer)); } -}//namespace mediakit +} // namespace mediakit diff --git a/src/Http/HttpBody.h b/src/Http/HttpBody.h index 319bc1bb..301d4c8a 100644 --- a/src/Http/HttpBody.h +++ b/src/Http/HttpBody.h @@ -37,7 +37,7 @@ public: /** * 剩余数据大小,如果返回-1, 那么就不设置content-length */ - virtual ssize_t remainSize() { return 0;}; + virtual int64_t remainSize() { return 0;}; /** * 读取一定字节数,返回大小可能小于size @@ -77,7 +77,7 @@ public: HttpStringBody(std::string str); ~HttpStringBody() override = default; - ssize_t remainSize() override; + int64_t remainSize() override; toolkit::Buffer::Ptr readData(size_t size) override ; private: @@ -94,7 +94,7 @@ public: HttpBufferBody(toolkit::Buffer::Ptr buffer); ~HttpBufferBody() override = default; - ssize_t remainSize() override; + int64_t remainSize() override; toolkit::Buffer::Ptr readData(size_t size) override; private: @@ -104,32 +104,32 @@ private: /** * 文件类型的content */ -class HttpFileBody : public HttpBody{ +class HttpFileBody : public HttpBody { public: typedef std::shared_ptr Ptr; /** * 构造函数 - * @param fp 文件句柄,文件的偏移量必须为0 - * @param offset 相对文件头的偏移量 - * @param max_size 最大读取字节数,未判断是否大于文件真实大小 + * @param file_path 文件路径 * @param use_mmap 是否使用mmap方式访问文件 */ - HttpFileBody(const std::shared_ptr &fp, size_t offset, size_t max_size, bool use_mmap = true); HttpFileBody(const std::string &file_path, bool use_mmap = true); ~HttpFileBody() override = default; - ssize_t remainSize() override ; + /** + * 设置读取范围 + * @param offset 相对文件头的偏移量 + * @param max_size 最大读取字节数 + */ + void setRange(uint64_t offset, uint64_t max_size); + + int64_t remainSize() override; toolkit::Buffer::Ptr readData(size_t size) override; int sendFile(int fd) override; private: - void init(const std::shared_ptr &fp,size_t offset,size_t max_size, bool use_mmap); - -private: - size_t _max_size; - size_t _offset = 0; - size_t _file_offset = 0; + int64_t _read_to = 0; + uint64_t _file_offset = 0; std::shared_ptr _fp; std::shared_ptr _map_addr; toolkit::ResourcePool _pool; @@ -152,7 +152,7 @@ public: */ HttpMultiFormBody(const HttpArgs &args,const std::string &filePath,const std::string &boundary = "0xKhTmLbOuNdArY"); virtual ~HttpMultiFormBody(){} - ssize_t remainSize() override ; + int64_t remainSize() override ; toolkit::Buffer::Ptr readData(size_t size) override; public: @@ -161,8 +161,8 @@ public: static std::string multiFormContentType(const std::string &boundary); private: - size_t _offset = 0; - size_t _totalSize; + uint64_t _offset = 0; + int64_t _totalSize; std::string _bodyPrefix; std::string _bodySuffix; HttpFileBody::Ptr _fileBody; diff --git a/src/Http/HttpCookie.cpp b/src/Http/HttpCookie.cpp index ce7a0f89..579c3542 100644 --- a/src/Http/HttpCookie.cpp +++ b/src/Http/HttpCookie.cpp @@ -10,7 +10,7 @@ #include "HttpCookie.h" #include "Util/util.h" -#include "Util/logger.h" +#include "Util/onceToken.h" #if defined(_WIN32) #include "Util/strptime_win.h" @@ -21,28 +21,75 @@ using namespace std; namespace mediakit { -void HttpCookie::setPath(const string &path){ +void HttpCookie::setPath(const string &path) { _path = path; } -void HttpCookie::setHost(const string &host){ + +void HttpCookie::setHost(const string &host) { _host = host; } -static time_t timeStrToInt(const string &date){ - struct tm tt; - strptime(date.data(),"%a, %b %d %Y %H:%M:%S %Z",&tt); - return mktime(&tt); + +static long s_gmtoff = 0; //时间差 +static onceToken s_token([]() { +#ifdef _WIN32 + TIME_ZONE_INFORMATION tzinfo; + DWORD dwStandardDaylight; + long bias; + dwStandardDaylight = GetTimeZoneInformation(&tzinfo); + bias = tzinfo.Bias; + if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) { + bias += tzinfo.StandardBias; + } + if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) { + bias += tzinfo.DaylightBias; + } + s_gmtoff = -bias * 60; //时间差(分钟) +#else + s_gmtoff = getLocalTime(time(nullptr)).tm_gmtoff; +#endif // _WIN32 +}); + +// from https://gmbabar.wordpress.com/2010/12/01/mktime-slow-use-custom-function/#comment-58 +static time_t time_to_epoch(const struct tm *ltm, int utcdiff) { + const int mon_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + long tyears, tdays, leaps, utc_hrs; + int i; + + tyears = ltm->tm_year - 70; // tm->tm_year is from 1900. + leaps = (tyears + 2) / 4; // no of next two lines until year 2100. + // i = (ltm->tm_year – 100) / 100; + // leaps -= ( (i/4)*3 + i%4 ); + tdays = 0; + for (i = 0; i < ltm->tm_mon; i++) + tdays += mon_days[i]; + + tdays += ltm->tm_mday - 1; // days of month passed. + tdays = tdays + (tyears * 365) + leaps; + + utc_hrs = ltm->tm_hour + utcdiff; // for your time zone. + return (tdays * 86400) + (utc_hrs * 3600) + (ltm->tm_min * 60) + ltm->tm_sec; } -void HttpCookie::setExpires(const string &expires,const string &server_date){ + +static time_t timeStrToInt(const string &date) { + struct tm tt; + strptime(date.data(), "%a, %b %d %Y %H:%M:%S %Z", &tt); + // mktime内部有使用互斥锁,非常影响性能 + return time_to_epoch(&tt, s_gmtoff / 3600); // mktime(&tt); +} + +void HttpCookie::setExpires(const string &expires, const string &server_date) { _expire = timeStrToInt(expires); - if(!server_date.empty()){ - _expire = time(NULL) + (_expire - timeStrToInt(server_date)); + if (!server_date.empty()) { + _expire = time(NULL) + (_expire - timeStrToInt(server_date)); } } -void HttpCookie::setKeyVal(const string &key,const string &val){ + +void HttpCookie::setKeyVal(const string &key, const string &val) { _key = key; _val = val; } -HttpCookie::operator bool (){ + +HttpCookie::operator bool() { return !_host.empty() && !_key.empty() && !_val.empty() && (_expire > time(NULL)); } @@ -50,19 +97,18 @@ const string &HttpCookie::getVal() const { return _val; } -const string &HttpCookie::getKey() const{ +const string &HttpCookie::getKey() const { return _key; } - -HttpCookieStorage &HttpCookieStorage::Instance(){ +HttpCookieStorage &HttpCookieStorage::Instance() { static HttpCookieStorage instance; return instance; } void HttpCookieStorage::set(const HttpCookie::Ptr &cookie) { lock_guard lck(_mtx_cookie); - if(!cookie || !(*cookie)){ + if (!cookie || !(*cookie)) { return; } _all_cookie[cookie->_host][cookie->_path][cookie->_key] = cookie; @@ -71,20 +117,20 @@ void HttpCookieStorage::set(const HttpCookie::Ptr &cookie) { vector HttpCookieStorage::get(const string &host, const string &path) { vector ret(0); lock_guard lck(_mtx_cookie); - auto it = _all_cookie.find(host); - if(it == _all_cookie.end()){ + auto it = _all_cookie.find(host); + if (it == _all_cookie.end()) { //未找到该host相关记录 return ret; } //遍历该host下所有path - for(auto &pr : it->second){ - if(path.find(pr.first) != 0){ + for (auto &pr : it->second) { + if (path.find(pr.first) != 0) { //这个path不匹配 continue; } //遍历该path下的各个cookie - for(auto it_cookie = pr.second.begin() ; it_cookie != pr.second.end() ; ){ - if(!*(it_cookie->second)){ + for (auto it_cookie = pr.second.begin(); it_cookie != pr.second.end();) { + if (!*(it_cookie->second)) { //该cookie已经过期,移除之 it_cookie = pr.second.erase(it_cookie); continue; @@ -97,5 +143,4 @@ vector HttpCookieStorage::get(const string &host, const string return ret; } - } /* namespace mediakit */ diff --git a/src/Http/HttpCookieManager.cpp b/src/Http/HttpCookieManager.cpp index 868dea4b..2c514451 100644 --- a/src/Http/HttpCookieManager.cpp +++ b/src/Http/HttpCookieManager.cpp @@ -8,10 +8,10 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include "Util/util.h" -#include "Util/MD5.h" -#include "Common/config.h" #include "HttpCookieManager.h" +#include "Common/config.h" +#include "Util/MD5.h" +#include "Util/util.h" using namespace std; using namespace toolkit; @@ -19,27 +19,25 @@ using namespace toolkit; namespace mediakit { //////////////////////////////HttpServerCookie//////////////////////////////////// -HttpServerCookie::HttpServerCookie(const std::shared_ptr &manager, - const string &cookie_name, - const string &uid, - const string &cookie, - uint64_t max_elapsed){ +HttpServerCookie::HttpServerCookie( + const std::shared_ptr &manager, const string &cookie_name, const string &uid, + const string &cookie, uint64_t max_elapsed) { _uid = uid; _max_elapsed = max_elapsed; _cookie_uuid = cookie; _cookie_name = cookie_name; _manager = manager; - manager->onAddCookie(_cookie_name,_uid,_cookie_uuid); + manager->onAddCookie(_cookie_name, _uid, _cookie_uuid); } HttpServerCookie::~HttpServerCookie() { auto strongManager = _manager.lock(); - if(strongManager){ - strongManager->onDelCookie(_cookie_name,_uid,_cookie_uuid); + if (strongManager) { + strongManager->onDelCookie(_cookie_name, _uid, _cookie_uuid); } } -const string & HttpServerCookie::getUid() const{ +const string &HttpServerCookie::getUid() const { return _uid; } @@ -47,11 +45,11 @@ string HttpServerCookie::getCookie(const string &path) const { return (StrPrinter << _cookie_name << "=" << _cookie_uuid << ";expires=" << cookieExpireTime() << ";path=" << path); } -const string& HttpServerCookie::getCookie() const { +const string &HttpServerCookie::getCookie() const { return _cookie_uuid; } -const string& HttpServerCookie::getCookieName() const{ +const string &HttpServerCookie::getCookieName() const { return _cookie_name; } @@ -63,11 +61,11 @@ bool HttpServerCookie::isExpired() { return _ticker.elapsedTime() > _max_elapsed * 1000; } -std::shared_ptr > HttpServerCookie::getLock(){ - return std::make_shared >(_mtx); +void HttpServerCookie::setAttach(std::shared_ptr attach) { + _attach = std::move(attach); } -string HttpServerCookie::cookieExpireTime() const{ +string HttpServerCookie::cookieExpireTime() const { char buf[64]; time_t tt = time(NULL) + _max_elapsed; strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt)); @@ -78,10 +76,13 @@ INSTANCE_IMP(HttpCookieManager); HttpCookieManager::HttpCookieManager() { //定时删除过期的cookie,防止内存膨胀 - _timer = std::make_shared(10.0f,[this](){ - onManager(); - return true; - }, nullptr); + _timer = std::make_shared( + 10.0f, + [this]() { + onManager(); + return true; + }, + nullptr); } HttpCookieManager::~HttpCookieManager() { @@ -91,11 +92,11 @@ HttpCookieManager::~HttpCookieManager() { void HttpCookieManager::onManager() { lock_guard lck(_mtx_cookie); //先遍历所有类型 - for(auto it_name = _map_cookie.begin() ; it_name != _map_cookie.end() ;){ + for (auto it_name = _map_cookie.begin(); it_name != _map_cookie.end();) { //再遍历该类型下的所有cookie - for (auto it_cookie = it_name->second.begin() ; it_cookie != it_name->second.end() ; ){ - if(it_cookie->second->isExpired()){ - //cookie过期,移除记录 + for (auto it_cookie = it_name->second.begin(); it_cookie != it_name->second.end();) { + if (it_cookie->second->isExpired()) { + // cookie过期,移除记录 DebugL << it_cookie->second->getUid() << " cookie过期:" << it_cookie->second->getCookie(); it_cookie = it_name->second.erase(it_cookie); continue; @@ -103,7 +104,7 @@ void HttpCookieManager::onManager() { ++it_cookie; } - if(it_name->second.empty()){ + if (it_name->second.empty()) { //该类型下没有任何cooki记录,移除之 DebugL << "该path下没有任何cooki记录:" << it_name->first; it_name = _map_cookie.erase(it_name); @@ -113,36 +114,38 @@ void HttpCookieManager::onManager() { } } -HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name,const string &uidIn,uint64_t max_elapsed,int max_client) { +HttpServerCookie::Ptr HttpCookieManager::addCookie(const string &cookie_name, const string &uid_in, + uint64_t max_elapsed, std::shared_ptr attach, int max_client) { lock_guard lck(_mtx_cookie); auto cookie = _geneator.obtain(); - auto uid = uidIn.empty() ? cookie : uidIn; - auto oldCookie = getOldestCookie(cookie_name , uid, max_client); - if(!oldCookie.empty()){ + auto uid = uid_in.empty() ? cookie : uid_in; + auto oldCookie = getOldestCookie(cookie_name, uid, max_client); + if (!oldCookie.empty()) { //假如该账号已经登录了,那么删除老的cookie。 //目的是实现单账号多地登录时挤占登录 - delCookie(cookie_name,oldCookie); + delCookie(cookie_name, oldCookie); } - HttpServerCookie::Ptr data(new HttpServerCookie(shared_from_this(),cookie_name,uid,cookie,max_elapsed)); + HttpServerCookie::Ptr data(new HttpServerCookie(shared_from_this(), cookie_name, uid, cookie, max_elapsed)); + data->setAttach(std::move(attach)); //保存该账号下的新cookie _map_cookie[cookie_name][cookie] = data; return data; } -HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,const string &cookie) { +HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name, const string &cookie) { lock_guard lck(_mtx_cookie); auto it_name = _map_cookie.find(cookie_name); - if(it_name == _map_cookie.end()){ + if (it_name == _map_cookie.end()) { //不存在该类型的cookie return nullptr; } auto it_cookie = it_name->second.find(cookie); - if(it_cookie == it_name->second.end()){ + if (it_cookie == it_name->second.end()) { //该类型下没有对应的cookie return nullptr; } - if(it_cookie->second->isExpired()){ - //cookie过期 + if (it_cookie->second->isExpired()) { + // cookie过期 DebugL << "cookie过期:" << it_cookie->second->getCookie(); it_name->second.erase(it_cookie); return nullptr; @@ -150,7 +153,7 @@ HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,con return it_cookie->second; } -HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,const StrCaseMap &http_header) { +HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name, const StrCaseMap &http_header) { auto it = http_header.find("Cookie"); if (it == http_header.end()) { return nullptr; @@ -159,100 +162,100 @@ HttpServerCookie::Ptr HttpCookieManager::getCookie(const string &cookie_name,con if (!cookie.size()) { cookie = FindField(it->second.data(), (cookie_name + "=").data(), nullptr); } - if(cookie.empty()){ + if (cookie.empty()) { return nullptr; } - return HttpCookieManager::Instance().getCookie(cookie_name , cookie); + return HttpCookieManager::Instance().getCookie(cookie_name, cookie); } -HttpServerCookie::Ptr HttpCookieManager::getCookieByUid(const string &cookie_name,const string &uid){ - if(cookie_name.empty() || uid.empty()){ +HttpServerCookie::Ptr HttpCookieManager::getCookieByUid(const string &cookie_name, const string &uid) { + if (cookie_name.empty() || uid.empty()) { return nullptr; } - auto cookie = getOldestCookie(cookie_name,uid); - if(cookie.empty()){ + auto cookie = getOldestCookie(cookie_name, uid); + if (cookie.empty()) { return nullptr; } - return getCookie(cookie_name,cookie); + return getCookie(cookie_name, cookie); } bool HttpCookieManager::delCookie(const HttpServerCookie::Ptr &cookie) { - if(!cookie){ + if (!cookie) { return false; } - return delCookie(cookie->getCookieName(),cookie->getCookie()); + return delCookie(cookie->getCookieName(), cookie->getCookie()); } -bool HttpCookieManager::delCookie(const string &cookie_name,const string &cookie) { +bool HttpCookieManager::delCookie(const string &cookie_name, const string &cookie) { lock_guard lck(_mtx_cookie); auto it_name = _map_cookie.find(cookie_name); - if(it_name == _map_cookie.end()){ + if (it_name == _map_cookie.end()) { return false; } return it_name->second.erase(cookie); } -void HttpCookieManager::onAddCookie(const string &cookie_name,const string &uid,const string &cookie){ +void HttpCookieManager::onAddCookie(const string &cookie_name, const string &uid, const string &cookie) { //添加新的cookie,我们记录下这个uid下有哪些cookie,目的是实现单账号多地登录时挤占登录 lock_guard lck(_mtx_cookie); //相同用户下可以存在多个cookie(意味多地登录),这些cookie根据登录时间的早晚依次排序 _map_uid_to_cookie[cookie_name][uid][getCurrentMillisecond()] = cookie; } -void HttpCookieManager::onDelCookie(const string &cookie_name,const string &uid,const string &cookie){ + +void HttpCookieManager::onDelCookie(const string &cookie_name, const string &uid, const string &cookie) { lock_guard lck(_mtx_cookie); //回收随机字符串 _geneator.release(cookie); auto it_name = _map_uid_to_cookie.find(cookie_name); - if(it_name == _map_uid_to_cookie.end()){ + if (it_name == _map_uid_to_cookie.end()) { //该类型下未有任意用户登录 return; } auto it_uid = it_name->second.find(uid); - if(it_uid == it_name->second.end()){ + if (it_uid == it_name->second.end()) { //该用户尚未登录 return; } //遍历同一名用户下的所有客户端,移除命中的客户端 - for(auto it_cookie = it_uid->second.begin() ; it_cookie != it_uid->second.end() ; ++it_cookie ){ - if(it_cookie->second != cookie) { + for (auto it_cookie = it_uid->second.begin(); it_cookie != it_uid->second.end(); ++it_cookie) { + if (it_cookie->second != cookie) { //不是该cookie continue; } //移除该用户名下的某个cookie,这个设备cookie将失效 it_uid->second.erase(it_cookie); - if(it_uid->second.size() != 0) { + if (it_uid->second.size() != 0) { break; } //该用户名下没有任何设备在线,移除之 it_name->second.erase(it_uid); - if(it_name->second.size() != 0) { + if (it_name->second.size() != 0) { break; } //该类型下未有任何用户在线,移除之 _map_uid_to_cookie.erase(it_name); break; } - } -string HttpCookieManager::getOldestCookie(const string &cookie_name,const string &uid, int max_client){ +string HttpCookieManager::getOldestCookie(const string &cookie_name, const string &uid, int max_client) { lock_guard lck(_mtx_cookie); auto it_name = _map_uid_to_cookie.find(cookie_name); - if(it_name == _map_uid_to_cookie.end()){ + if (it_name == _map_uid_to_cookie.end()) { //不存在该类型的cookie return ""; } auto it_uid = it_name->second.find(uid); - if(it_uid == it_name->second.end()){ + if (it_uid == it_name->second.end()) { //该用户从未登录过 return ""; } - if(it_uid->second.size() < MAX(1,max_client)){ + if (it_uid->second.size() < MAX(1, max_client)) { //同一名用户下,客户端个数还没达到限制个数 return ""; } @@ -261,28 +264,29 @@ string HttpCookieManager::getOldestCookie(const string &cookie_name,const string } /////////////////////////////////RandStrGeneator//////////////////////////////////// -string RandStrGeneator::obtain(){ +string RandStrGeneator::obtain() { //获取唯一的防膨胀的随机字符串 - while (true){ + while (true) { auto str = obtain_l(); - if(_obtained.find(str) == _obtained.end()){ + if (_obtained.find(str) == _obtained.end()) { //没有重复 _obtained.emplace(str); return str; } } } -void RandStrGeneator::release(const string &str){ + +void RandStrGeneator::release(const string &str) { //从防膨胀库中移除 _obtained.erase(str); } -string RandStrGeneator::obtain_l(){ - //12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串 - auto str = makeRandStr(12,false); +string RandStrGeneator::obtain_l() { + // 12个伪随机字节 + 4个递增的整形字节,然后md5即为随机字符串 + auto str = makeRandStr(12, false); str.append((char *)&_index, sizeof(_index)); ++_index; return MD5(str).hexdigest(); } -}//namespace mediakit \ No newline at end of file +} // namespace mediakit \ No newline at end of file diff --git a/src/Http/HttpCookieManager.h b/src/Http/HttpCookieManager.h index 26e8a3a4..ccdc0fd0 100644 --- a/src/Http/HttpCookieManager.h +++ b/src/Http/HttpCookieManager.h @@ -11,13 +11,13 @@ #ifndef SRC_HTTP_COOKIEMANAGER_H #define SRC_HTTP_COOKIEMANAGER_H -#include -#include +#include "Common/Parser.h" +#include "Network/Socket.h" +#include "Util/TimeTicker.h" #include "Util/mini.h" #include "Util/util.h" -#include "Util/TimeTicker.h" -#include "Network/Socket.h" -#include "Common/Parser.h" +#include +#include #define COOKIE_DEFAULT_LIFE (7 * 24 * 60 * 60) @@ -28,9 +28,9 @@ class HttpCookieManager; /** * cookie对象,用于保存cookie的一些相关属性 */ -class HttpServerCookie : public toolkit::AnyStorage , public toolkit::noncopyable{ +class HttpServerCookie : public toolkit::noncopyable { public: - typedef std::shared_ptr Ptr; + using Ptr = std::shared_ptr; /** * 构建cookie * @param manager cookie管理者对象 @@ -40,12 +40,10 @@ public: * @param max_elapsed 最大过期时间,单位秒 */ - HttpServerCookie(const std::shared_ptr &manager, - const std::string &cookie_name, - const std::string &uid, - const std::string &cookie, - uint64_t max_elapsed); - ~HttpServerCookie() ; + HttpServerCookie( + const std::shared_ptr &manager, const std::string &cookie_name, const std::string &uid, + const std::string &cookie, uint64_t max_elapsed); + ~HttpServerCookie(); /** * 获取uid @@ -65,13 +63,13 @@ public: * 获取cookie随机字符串 * @return cookie随机字符串 */ - const std::string& getCookie() const; + const std::string &getCookie() const; /** * 获取该cookie名 * @return */ - const std::string& getCookieName() const; + const std::string &getCookieName() const; /** * 更新该cookie的过期时间,可以让此cookie不失效 @@ -85,26 +83,35 @@ public: bool isExpired(); /** - * 获取区域锁 - * @return + * 设置附加数据 */ - std::shared_ptr > getLock(); + void setAttach(std::shared_ptr attach); + + /* + * 获取附加数据 + */ + template + const T& getAttach() const { + return *static_cast(_attach.get()); + } + private: - std::string cookieExpireTime() const ; + std::string cookieExpireTime() const; + private: std::string _uid; std::string _cookie_name; std::string _cookie_uuid; uint64_t _max_elapsed; toolkit::Ticker _ticker; - std::recursive_mutex _mtx; + std::shared_ptr _attach; std::weak_ptr _manager; }; /** * cookie随机字符串生成器 */ -class RandStrGeneator{ +class RandStrGeneator { public: RandStrGeneator() = default; ~RandStrGeneator() = default; @@ -120,8 +127,10 @@ public: * @param str 随机字符串 */ void release(const std::string &str); + private: std::string obtain_l(); + private: //碰撞库 std::unordered_set _obtained; @@ -135,8 +144,8 @@ private: */ class HttpCookieManager : public std::enable_shared_from_this { public: - typedef std::shared_ptr Ptr; friend class HttpServerCookie; + using Ptr = std::shared_ptr; ~HttpCookieManager(); /** @@ -152,7 +161,10 @@ public: * @param max_elapsed 该cookie过期时间,单位秒 * @return cookie对象 */ - HttpServerCookie::Ptr addCookie(const std::string &cookie_name,const std::string &uid, uint64_t max_elapsed = COOKIE_DEFAULT_LIFE,int max_client = 1); + HttpServerCookie::Ptr addCookie( + const std::string &cookie_name, const std::string &uid, uint64_t max_elapsed = COOKIE_DEFAULT_LIFE, + std::shared_ptr attach = nullptr, + int max_client = 1); /** * 根据cookie随机字符串查找cookie对象 @@ -160,7 +172,7 @@ public: * @param cookie cookie随机字符串 * @return cookie对象,可以为nullptr */ - HttpServerCookie::Ptr getCookie(const std::string &cookie_name,const std::string &cookie); + HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const std::string &cookie); /** * 从http头中获取cookie对象 @@ -168,7 +180,7 @@ public: * @param http_header http头 * @return cookie对象 */ - HttpServerCookie::Ptr getCookie(const std::string &cookie_name,const StrCaseMap &http_header); + HttpServerCookie::Ptr getCookie(const std::string &cookie_name, const StrCaseMap &http_header); /** * 根据uid获取cookie @@ -176,7 +188,7 @@ public: * @param uid 用户id * @return cookie对象 */ - HttpServerCookie::Ptr getCookieByUid(const std::string &cookie_name,const std::string &uid); + HttpServerCookie::Ptr getCookieByUid(const std::string &cookie_name, const std::string &uid); /** * 删除cookie,用户登出时使用 @@ -184,8 +196,10 @@ public: * @return */ bool delCookie(const HttpServerCookie::Ptr &cookie); + private: HttpCookieManager(); + void onManager(); /** * 构造cookie对象时触发,目的是记录某账号下多个cookie @@ -193,7 +207,7 @@ private: * @param uid 用户id * @param cookie cookie随机字符串 */ - void onAddCookie(const std::string &cookie_name,const std::string &uid,const std::string &cookie); + void onAddCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie); /** * 析构cookie对象时触发 @@ -201,7 +215,7 @@ private: * @param uid 用户id * @param cookie cookie随机字符串 */ - void onDelCookie(const std::string &cookie_name,const std::string &uid,const std::string &cookie); + void onDelCookie(const std::string &cookie_name, const std::string &uid, const std::string &cookie); /** * 获取某用户名下最先登录时的cookie,目的是实现某用户下最多登录若干个设备 @@ -210,7 +224,7 @@ private: * @param max_client 最多登录的设备个数 * @return 最早的cookie随机字符串 */ - std::string getOldestCookie(const std::string &cookie_name,const std::string &uid, int max_client = 1); + std::string getOldestCookie(const std::string &cookie_name, const std::string &uid, int max_client = 1); /** * 删除cookie @@ -218,16 +232,21 @@ private: * @param cookie cookie随机字符串 * @return 成功true */ - bool delCookie(const std::string &cookie_name,const std::string &cookie); + bool delCookie(const std::string &cookie_name, const std::string &cookie); + private: - std::unordered_map >_map_cookie; - std::unordered_map > >_map_uid_to_cookie; + std::unordered_map< + std::string /*cookie_name*/, std::unordered_map> + _map_cookie; + std::unordered_map< + std::string /*cookie_name*/, + std::unordered_map>> + _map_uid_to_cookie; std::recursive_mutex _mtx_cookie; toolkit::Timer::Ptr _timer; RandStrGeneator _geneator; }; -}//namespace mediakit +} // namespace mediakit - -#endif //SRC_HTTP_COOKIEMANAGER_H +#endif // SRC_HTTP_COOKIEMANAGER_H diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 30a70f4b..0dd735dd 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -33,9 +33,6 @@ static const string kCookieName = "ZL_COOKIE"; static const string kHlsSuffix = "/hls.m3u8"; class HttpCookieAttachment { -public: - HttpCookieAttachment() {}; - ~HttpCookieAttachment() {}; public: //cookie生效作用域,本cookie只对该目录下的文件生效 string _path; @@ -192,13 +189,13 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st } //拦截hls的播放请求 -static bool emitHlsPlayed(const Parser &parser, const MediaInfo &mediaInfo, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){ +static bool emitHlsPlayed(const Parser &parser, const MediaInfo &media_info, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){ //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 Broadcast::AuthInvoker auth_invoker = [invoker](const string &err) { //cookie有效期为kHlsCookieSecond invoker(err, "", kHlsCookieSecond); }; - bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, mediaInfo, auth_invoker, static_cast(sender)); + bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, media_info, auth_invoker, static_cast(sender)); if (!flag) { //未开启鉴权,那么允许播放 auth_invoker(""); @@ -247,7 +244,7 @@ public: * 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码 * 5、触发kBroadcastHttpAccess事件 */ -static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaInfo &mediaInfo, bool is_dir, +static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaInfo &media_info, bool is_dir, const function &callback) { //获取用户唯一id auto uid = parser.Params(); @@ -265,13 +262,12 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI if (cookie) { //找到了cookie,对cookie上锁先 - auto lck = cookie->getLock(); - auto attachment = (*cookie)[kCookieName].get(); - if (path.find(attachment._path) == 0) { + auto& attach = cookie->getAttach(); + if (path.find(attach._path) == 0) { //上次cookie是限定本目录 - if (attachment._err_msg.empty()) { + if (attach._err_msg.empty()) { //上次鉴权成功 - if (attachment._is_hls) { + if (attach._is_hls) { //如果播放的是hls,那么刷新hls的cookie(获取ts文件也会刷新) cookie->updateTime(); cookie_from_header = false; @@ -282,7 +278,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI //上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 if (parser.Params().empty() || parser.Params() == cookie->getUid()) { //url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 - callback(attachment._err_msg, cookie_from_header ? nullptr : cookie); + callback(attach._err_msg, cookie_from_header ? nullptr : cookie); return; } } @@ -290,7 +286,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI HttpCookieManager::Instance().delCookie(cookie); } - bool is_hls = mediaInfo._schema == HLS_SCHEMA; + bool is_hls = media_info._schema == HLS_SCHEMA; SockInfoImp::Ptr info = std::make_shared(); info->_identifier = sender.getIdentifier(); @@ -300,10 +296,10 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI info->_local_port = sender.get_local_port(); //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo, info] - (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, media_info, info] + (const string &err_msg, const string &cookie_path_in, int life_second) { HttpServerCookie::Ptr cookie; - if (cookieLifeSecond) { + if (life_second) { //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 string cookie_path = cookie_path_in; if (cookie_path.empty()) { @@ -311,32 +307,28 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI cookie_path = is_dir ? path : path.substr(0, path.rfind("/") + 1); } - cookie = HttpCookieManager::Instance().addCookie(kCookieName, uid, cookieLifeSecond); - //对cookie上锁 - auto lck = cookie->getLock(); - HttpCookieAttachment attachment; + auto attach = std::make_shared(); //记录用户能访问的路径 - attachment._path = cookie_path; + attach->_path = cookie_path; //记录能否访问 - attachment._err_msg = errMsg; + attach->_err_msg = err_msg; //记录访问的是否为hls - attachment._is_hls = is_hls; + attach->_is_hls = is_hls; if (is_hls) { - //hls相关信息 - attachment._hls_data = std::make_shared(mediaInfo, info); - //hls未查找MediaSource - attachment._have_find_media_source = false; + // hls相关信息 + attach->_hls_data = std::make_shared(media_info, info); + // hls未查找MediaSource + attach->_have_find_media_source = false; } - (*cookie)[kCookieName].set(std::move(attachment)); - callback(errMsg, cookie); + callback(err_msg, HttpCookieManager::Instance().addCookie(kCookieName, uid, life_second, attach)); } else { - callback(errMsg, nullptr); + callback(err_msg, nullptr); } }; if (is_hls) { //是hls的播放鉴权,拦截之 - emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender); + emitHlsPlayed(parser, media_info, accessPathInvoker, sender); return; } @@ -370,13 +362,13 @@ static string pathCat(const string &a, const string &b){ * 访问文件 * @param sender 事件触发者 * @param parser http请求 - * @param mediaInfo http url信息 - * @param strFile 文件绝对路径 + * @param media_info http url信息 + * @param file_path 文件绝对路径 * @param cb 回调对象 */ -static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo &mediaInfo, const string &strFile, const HttpFileManager::invoker &cb) { - bool is_hls = end_with(strFile, kHlsSuffix); - bool file_exist = File::is_file(strFile.data()); +static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo &media_info, const string &file_path, const HttpFileManager::invoker &cb) { + bool is_hls = end_with(file_path, kHlsSuffix); + bool file_exist = File::is_file(file_path.data()); if (!is_hls && !file_exist) { //文件不存在且不是hls,那么直接返回404 sendNotFound(cb); @@ -385,13 +377,13 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo if (is_hls) { //hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS - const_cast(mediaInfo._schema) = HLS_SCHEMA; - replace(const_cast(mediaInfo._streamid), kHlsSuffix, ""); + const_cast(media_info._schema) = HLS_SCHEMA; + replace(const_cast(media_info._streamid), kHlsSuffix, ""); } weak_ptr weakSession = sender.shared_from_this(); //判断是否有权限访问该文件 - canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession , file_exist](const string &errMsg, const HttpServerCookie::Ptr &cookie) { + canAccessPath(sender, parser, media_info, false, [cb, file_path, parser, is_hls, media_info, weakSession , file_exist](const string &errMsg, const HttpServerCookie::Ptr &cookie) { auto strongSession = weakSession.lock(); if (!strongSession) { //http客户端已经断开,不需要回复 @@ -401,80 +393,82 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo //文件鉴权失败 StrCaseMap headerOut; if (cookie) { - auto lck = cookie->getLock(); - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); + headerOut["Set-Cookie"] = cookie->getCookie(cookie->getAttach()._path); } cb(401, "text/html", headerOut, std::make_shared(errMsg)); return; } - auto response_file = [file_exist, is_hls](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { + auto response_file = [file_exist, is_hls](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, + const string &file_path, const Parser &parser, const string &file_content = "") { StrCaseMap httpHeader; if (cookie) { - auto lck = cookie->getLock(); - httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); + httpHeader["Set-Cookie"] = cookie->getCookie(cookie->getAttach()._path); } HttpSession::HttpResponseInvoker invoker = [&](int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { if (cookie && file_exist) { - auto lck = cookie->getLock(); - auto is_hls = (*cookie)[kCookieName].get()._is_hls; - if (is_hls) { - (*cookie)[kCookieName].get()._hls_data->addByteUsage(body->remainSize()); + auto& attach = cookie->getAttach(); + if (attach._is_hls) { + attach._hls_data->addByteUsage(body->remainSize()); } } - cb(code, HttpFileManager::getContentType(strFile.data()), headerOut, body); + cb(code, HttpFileManager::getContentType(file_path.data()), headerOut, body); }; - invoker.responseFile(parser.getHeader(), httpHeader, strFile, !is_hls); + invoker.responseFile(parser.getHeader(), httpHeader, file_content.empty() ? file_path : file_content, !is_hls, file_content.empty()); }; if (!is_hls) { //不是hls,直接回复文件或404 - response_file(cookie, cb, strFile, parser); + response_file(cookie, cb, file_path, parser); return; } //是hls直播,判断HLS直播流是否已经注册 bool have_find_media_src = false; if (cookie) { - auto lck = cookie->getLock(); - have_find_media_src = (*cookie)[kCookieName].get()._have_find_media_source; + auto& attach = cookie->getAttach(); + have_find_media_src = attach._have_find_media_source; if (!have_find_media_src) { - (*cookie)[kCookieName].get()._have_find_media_source = true; + const_cast(attach)._have_find_media_source = true; + } else { + auto src = attach._hls_data->getMediaSource(); + if (src) { + //直接从内存获取m3u8索引文件(而不是从文件系统) + response_file(cookie, cb, file_path, parser, src->getIndexFile()); + return; + } } } if (have_find_media_src) { //之前该cookie已经通过MediaSource::findAsync查找过了,所以现在只以文件系统查找结果为准 - response_file(cookie, cb, strFile, parser); + response_file(cookie, cb, file_path, parser); return; } //hls文件不存在,我们等待其生成并延后回复 - MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { + MediaSource::findAsync(media_info, strongSession, [response_file, cookie, cb, file_path, parser](const MediaSource::Ptr &src) { if (cookie) { - auto lck = cookie->getLock(); //尝试添加HlsMediaSource的观看人数(HLS是按需生成的,这样可以触发HLS文件的生成) - (*cookie)[kCookieName].get()._hls_data->addByteUsage(0); - } - if (src && File::is_file(strFile.data())) { - //流和m3u8文件都存在,那么直接返回文件 - response_file(cookie, cb, strFile, parser); - return; + auto &attach = cookie->getAttach(); + attach._hls_data->addByteUsage(0); + attach._hls_data->setMediaSource(dynamic_pointer_cast(src)); } + auto hls = dynamic_pointer_cast(src); if (!hls) { //流不存在,那么直接返回文件(相当于纯粹的HLS文件服务器,但是会挂起播放器15秒左右(用于等待HLS流的注册)) - response_file(cookie, cb, strFile, parser); + response_file(cookie, cb, file_path, parser); return; } - //流存在,但是m3u8文件不存在,那么等待生成m3u8文件(HLS源注册后,并不会立即生成HLS文件,有人观看才会按需生成HLS文件) - hls->waitForFile([response_file, cookie, cb, strFile, parser]() { - response_file(cookie, cb, strFile, parser); + //可能异步获取m3u8索引文件 + hls->getIndexFile([response_file, file_path, cookie, cb, parser](const string &file) { + response_file(cookie, cb, file_path, parser, file); }); }); }); } -static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSession &sender){ +static string getFilePath(const Parser &parser,const MediaInfo &media_info, TcpSession &sender){ GET_CONFIG(bool, enableVhost, General::kEnableVhost); GET_CONFIG(string, rootPath, Http::kRootPath); GET_CONFIG_FUNC(StrCaseMap, virtualPathMap, Http::kVirtualPath, [](const string &str) { @@ -482,17 +476,17 @@ static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSe }); string url, path; - auto it = virtualPathMap.find(mediaInfo._app); + auto it = virtualPathMap.find(media_info._app); if (it != virtualPathMap.end()) { //访问的是virtualPath path = it->second; - url = parser.Url().substr(1 + mediaInfo._app.size()); + url = parser.Url().substr(1 + media_info._app.size()); } else { //访问的是rootPath path = rootPath; url = parser.Url(); } - auto ret = File::absolutePath(enableVhost ? mediaInfo._vhost + url : url, path); + auto ret = File::absolutePath(enableVhost ? media_info._vhost + url : url, path); NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, ret, static_cast(sender)); return ret; } @@ -505,33 +499,33 @@ static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSe */ void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const HttpFileManager::invoker &cb) { auto fullUrl = string(HTTP_SCHEMA) + "://" + parser["Host"] + parser.FullUrl(); - MediaInfo mediaInfo(fullUrl); - auto strFile = getFilePath(parser, mediaInfo, sender); + MediaInfo media_info(fullUrl); + auto file_path = getFilePath(parser, media_info, sender); //访问的是文件夹 - if (File::is_dir(strFile.data())) { - auto indexFile = searchIndexFile(strFile); + if (File::is_dir(file_path.data())) { + auto indexFile = searchIndexFile(file_path); if (!indexFile.empty()) { //发现该文件夹下有index文件 - strFile = pathCat(strFile, indexFile); + file_path = pathCat(file_path, indexFile); parser.setUrl(pathCat(parser.Url(), indexFile)); - accessFile(sender, parser, mediaInfo, strFile, cb); + accessFile(sender, parser, media_info, file_path, cb); return; } string strMenu; //生成文件夹菜单索引 - if (!makeFolderMenu(parser.Url(), strFile, strMenu)) { + if (!makeFolderMenu(parser.Url(), file_path, strMenu)) { //文件夹不存在 sendNotFound(cb); return; } //判断是否有权限访问该目录 - canAccessPath(sender, parser, mediaInfo, true, [strMenu, cb](const string &errMsg, const HttpServerCookie::Ptr &cookie) mutable{ + canAccessPath(sender, parser, media_info, true, [strMenu, cb](const string &errMsg, const HttpServerCookie::Ptr &cookie) mutable{ if (!errMsg.empty()) { strMenu = errMsg; } StrCaseMap headerOut; if (cookie) { - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); + headerOut["Set-Cookie"] = cookie->getCookie(cookie->getAttach()._path); } cb(errMsg.empty() ? 200 : 401, "text/html", headerOut, std::make_shared(strMenu)); }); @@ -539,7 +533,7 @@ void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const Htt } //访问的是文件 - accessFile(sender, parser, mediaInfo, strFile, cb); + accessFile(sender, parser, media_info, file_path, cb); }; @@ -579,16 +573,19 @@ HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::Htt void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, const StrCaseMap &responseHeader, - const string &filePath, - bool use_mmap) const { - StrCaseMap &httpHeader = const_cast(responseHeader); - std::shared_ptr fp(fopen(filePath.data(), "rb"), [](FILE *fp) { - if (fp) { - fclose(fp); - } - }); + const string &file, + bool use_mmap, + bool is_path) const { + if (!is_path) { + //file是文件内容 + (*this)(200, responseHeader, std::make_shared(file)); + return; + } - if (!fp) { + //file是文件路径 + StrCaseMap &httpHeader = const_cast(responseHeader); + auto fileBody = std::make_shared(file, use_mmap); + if (fileBody->remainSize() < 0) { //打开文件失败 GET_CONFIG(string, notFound, Http::kNotFound); GET_CONFIG(string, charSet, Http::kCharSet); @@ -600,29 +597,23 @@ void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, } auto &strRange = const_cast(requestHeader)["Range"]; - size_t iRangeStart = 0; - size_t iRangeEnd = 0; - size_t fileSize = File::fileSize(fp.get()); - - int code; - if (strRange.size() == 0) { - //全部下载 - code = 200; - iRangeEnd = fileSize - 1; - } else { + int code = 200; + if (!strRange.empty()) { //分节下载 code = 206; - iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data()); - iRangeEnd = atoll(FindField(strRange.data(), "-", nullptr).data()); + auto iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data()); + auto iRangeEnd = atoll(FindField(strRange.data(), "-", nullptr).data()); + auto fileSize = fileBody->remainSize(); if (iRangeEnd == 0) { iRangeEnd = fileSize - 1; } + //设置文件范围 + fileBody->setRange(iRangeStart, iRangeEnd - iRangeStart + 1); //分节下载返回Content-Range头 httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << fileSize << endl); } //回复文件 - HttpBody::Ptr fileBody = std::make_shared(fp, iRangeStart, iRangeEnd - iRangeStart + 1, use_mmap); (*this)(code, httpHeader, fileBody); } diff --git a/src/Http/HttpFileManager.h b/src/Http/HttpFileManager.h index 2e598aae..7a3766b1 100644 --- a/src/Http/HttpFileManager.h +++ b/src/Http/HttpFileManager.h @@ -35,7 +35,7 @@ public: void operator()(int code, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const; void operator()(int code, const StrCaseMap &headerOut, const std::string &body) const; - void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const std::string &filePath, bool use_mmap = true) const; + void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const std::string &file, bool use_mmap = true, bool is_path = true) const; operator bool(); private: HttpResponseInvokerLambda0 _lambad; diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index de9e4237..75f79a38 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -519,16 +519,16 @@ void HttpSession::sendResponse(int code, GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond); //body默认为空 - ssize_t size = 0; + int64_t size = 0; if (body && body->remainSize()) { //有body,获取body大小 size = body->remainSize(); } - if(no_content_length){ - //http-flv直播是Keep-Alive类型 + if (no_content_length) { + // http-flv直播是Keep-Alive类型 bClose = false; - }else if((size_t) size >= SIZE_MAX || size < 0 ){ + } else if ((size_t)size >= SIZE_MAX || size < 0) { //不固定长度的body,那么发送完body后应该关闭socket,以便浏览器做下载完毕的判断 bClose = true; } @@ -537,47 +537,47 @@ void HttpSession::sendResponse(int code, headerOut.emplace(kDate, dateStr()); headerOut.emplace(kServer, kServerName); headerOut.emplace(kConnection, bClose ? "close" : "keep-alive"); - if(!bClose){ + if (!bClose) { string keepAliveString = "timeout="; keepAliveString += to_string(keepAliveSec); keepAliveString += ", max=100"; - headerOut.emplace(kKeepAlive,std::move(keepAliveString)); + headerOut.emplace(kKeepAlive, std::move(keepAliveString)); } - if(!_origin.empty()){ + if (!_origin.empty()) { //设置跨域 - headerOut.emplace(kAccessControlAllowOrigin,_origin); + headerOut.emplace(kAccessControlAllowOrigin, _origin); headerOut.emplace(kAccessControlAllowCredentials, "true"); } - if(!no_content_length && size >= 0 && (size_t)size < SIZE_MAX){ + if (!no_content_length && size >= 0 && (size_t)size < SIZE_MAX) { //文件长度为固定值,且不是http-flv强制设置Content-Length headerOut[kContentLength] = to_string(size); } - if(size && !pcContentType){ + if (size && !pcContentType) { //有body时,设置缺省类型 pcContentType = "text/plain"; } - if((size || no_content_length) && pcContentType){ + if ((size || no_content_length) && pcContentType) { //有body时,设置文件类型 string strContentType = pcContentType; strContentType += "; charset="; strContentType += charSet; - headerOut.emplace(kContentType,std::move(strContentType)); + headerOut.emplace(kContentType, std::move(strContentType)); } //发送http头 string str; str.reserve(256); - str += "HTTP/1.1 " ; + str += "HTTP/1.1 "; str += to_string(code); str += ' '; - str += getHttpStatusMessage(code) ; + str += getHttpStatusMessage(code); str += "\r\n"; for (auto &pr : header) { - str += pr.first ; + str += pr.first; str += ": "; str += pr.second; str += "\r\n"; @@ -586,18 +586,21 @@ void HttpSession::sendResponse(int code, SockSender::send(std::move(str)); _ticker.resetTime(); - if(!size){ + if (!size) { //没有body - if(bClose){ + if (bClose) { shutdown(SockException(Err_shutdown,StrPrinter << "close connection after send http header completed with status code:" << code)); } return; } +#if 0 + //sendfile跟共享mmap相比并没有性能上的优势,相反,sendfile还有功能上的缺陷,先屏蔽 if (typeid(*this) == typeid(HttpSession) && !body->sendFile(getSock()->rawFD())) { - //http支持sendfile优化 + // http支持sendfile优化 return; } +#endif GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); if (body->remainSize() > sendBufSize) { @@ -607,9 +610,7 @@ void HttpSession::sendResponse(int code, //发送http body AsyncSenderData::Ptr data = std::make_shared(shared_from_this(), body, bClose); - getSock()->setOnFlush([data]() { - return AsyncSender::onSocketFlushed(data); - }); + getSock()->setOnFlush([data]() { return AsyncSender::onSocketFlushed(data); }); AsyncSender::onSocketFlushed(data); } diff --git a/src/Http/TsPlayer.cpp b/src/Http/TsPlayer.cpp index 21b81da4..7dbbef69 100644 --- a/src/Http/TsPlayer.cpp +++ b/src/Http/TsPlayer.cpp @@ -20,6 +20,7 @@ TsPlayer::TsPlayer(const EventPoller::Ptr &poller) : HttpTSPlayer(poller, true) void TsPlayer::play(const string &url) { TraceL << "play http-ts: " << url; _play_result = false; + _benchmark_mode = (*this)[Client::kBenchmarkMode].as(); setHeaderTimeout((*this)[Client::kTimeoutMS].as()); setBodyTimeout((*this)[Client::kMediaTimeoutMS].as()); setMethod("GET"); @@ -45,7 +46,9 @@ void TsPlayer::onResponseBody(const char *buf, size_t size) { _play_result = true; onPlayResult(SockException(Err_success, "play http-ts success")); } - HttpTSPlayer::onResponseBody(buf, size); + if (!_benchmark_mode) { + HttpTSPlayer::onResponseBody(buf, size); + } } } // namespace mediakit \ No newline at end of file diff --git a/src/Http/TsPlayer.h b/src/Http/TsPlayer.h index 4b0656f8..dfe69996 100644 --- a/src/Http/TsPlayer.h +++ b/src/Http/TsPlayer.h @@ -16,7 +16,7 @@ namespace mediakit { -class TsPlayer : public HttpTSPlayer , public PlayerBase { +class TsPlayer : public HttpTSPlayer, public PlayerBase { public: TsPlayer(const toolkit::EventPoller::Ptr &poller); ~TsPlayer() override = default; @@ -37,6 +37,7 @@ protected: private: bool _play_result = true; + bool _benchmark_mode = false; }; } // namespace mediakit diff --git a/src/Http/TsplayerImp.cpp b/src/Http/TsplayerImp.cpp index b12584bb..1ef6ef49 100644 --- a/src/Http/TsplayerImp.cpp +++ b/src/Http/TsplayerImp.cpp @@ -33,7 +33,8 @@ void TsPlayerImp::addTrackCompleted() { } void TsPlayerImp::onPlayResult(const SockException &ex) { - if (ex) { + auto benchmark_mode = (*this)[Client::kBenchmarkMode].as(); + if (ex || benchmark_mode) { PlayerImp::onPlayResult(ex); } else { auto demuxer = std::make_shared(); @@ -47,7 +48,10 @@ void TsPlayerImp::onShutdown(const SockException &ex) { _demuxer = nullptr; } -vector TsPlayerImp::getTracks(bool ready) const { +vector TsPlayerImp::getTracks(bool ready) const { + if (!_demuxer) { + return vector(); + } return static_pointer_cast(_demuxer)->getTracks(ready); } diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index f16f61c0..e8e90692 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -70,7 +70,7 @@ void HlsMaker::makeIndexFile(bool eof) { snprintf(file_content, sizeof(file_content), "#EXT-X-ENDLIST\n"); m3u8.append(file_content); } - onWriteHls(m3u8.data(), m3u8.size()); + onWriteHls(m3u8); } diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index 633b1dbd..f3223d77 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -72,10 +72,8 @@ protected: /** * 写m3u8文件回调 - * @param data - * @param len */ - virtual void onWriteHls(const char *data, size_t len) = 0; + virtual void onWriteHls(const std::string &data) = 0; /** * 上一个 ts 切片写入完成, 可在这里进行通知处理 diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index db69664a..429e0318 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -111,13 +111,13 @@ void HlsMakerImp::onWriteSegment(const char *data, size_t len) { } } -void HlsMakerImp::onWriteHls(const char *data, size_t len) { +void HlsMakerImp::onWriteHls(const std::string &data) { auto hls = makeFile(_path_hls); if (hls) { - fwrite(data, len, 1, hls.get()); + fwrite(data.data(), data.size(), 1, hls.get()); hls.reset(); if (_media_src) { - _media_src->registHls(true); + _media_src->registHls(data); } } else { WarnL << "create hls file failed," << _path_hls << " " << get_uv_errmsg(); diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index 44dea72b..d401e08a 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -53,7 +53,7 @@ protected: std::string onOpenSegment(uint64_t index) override ; void onDelSegment(uint64_t index) override; void onWriteSegment(const char *data, size_t len) override; - void onWriteHls(const char *data, size_t len) override; + void onWriteHls(const std::string &data) override; void onFlushLastSegment(uint32_t duration_ms) override; private: diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index f1684f79..505dbd40 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -12,7 +12,7 @@ using namespace toolkit; -namespace mediakit{ +namespace mediakit { HlsCookieData::HlsCookieData(const MediaInfo &info, const std::shared_ptr &sock_info) { _info = info; @@ -21,15 +21,15 @@ HlsCookieData::HlsCookieData(const MediaInfo &info, const std::shared_ptr(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); - if(src){ +void HlsCookieData::addReaderCount() { + if (!*_added) { + auto src = std::dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA, _info._vhost, _info._app, _info._streamid)); + if (src) { *_added = true; _ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller()); auto added = _added; - _ring_reader->setDetachCB([added](){ - //HlsMediaSource已经销毁 + _ring_reader->setDetachCB([added]() { + // HlsMediaSource已经销毁 *added = false; }); } @@ -39,14 +39,14 @@ void HlsCookieData::addReaderCount(){ HlsCookieData::~HlsCookieData() { if (*_added) { uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000; - WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port() << ") " - << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid + WarnL << _sock_info->getIdentifier() << "(" << _sock_info->get_peer_ip() << ":" << _sock_info->get_peer_port() + << ") " << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid << ")断开,耗时(s):" << duration; GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); uint64_t bytes = _bytes.load(); if (bytes >= iFlowThreshold * 1024) { - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, bytes, duration, true, static_cast(*_sock_info)); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, bytes, duration, true, static_cast(*_sock_info)); } } } @@ -57,6 +57,12 @@ void HlsCookieData::addByteUsage(size_t bytes) { _ticker.resetTime(); } +void HlsCookieData::setMediaSource(const HlsMediaSource::Ptr &src) { + _src = src; +} -}//namespace mediakit +HlsMediaSource::Ptr HlsCookieData::getMediaSource() const { + return _src.lock(); +} +} // namespace mediakit diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 3a069f7f..e0cc6bdf 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -11,11 +11,11 @@ #ifndef ZLMEDIAKIT_HLSMEDIASOURCE_H #define ZLMEDIAKIT_HLSMEDIASOURCE_H -#include -#include "Util/TimeTicker.h" #include "Common/MediaSource.h" +#include "Util/TimeTicker.h" +#include -namespace mediakit{ +namespace mediakit { class HlsMediaSource : public MediaSource { public: @@ -24,28 +24,25 @@ public: using RingType = toolkit::RingBuffer; using Ptr = std::shared_ptr; - HlsMediaSource(const std::string &vhost, const std::string &app, const std::string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){} + HlsMediaSource(const std::string &vhost, const std::string &app, const std::string &stream_id) + : MediaSource(HLS_SCHEMA, vhost, app, stream_id) {} ~HlsMediaSource() override = default; /** * 获取媒体源的环形缓冲 */ - const RingType::Ptr &getRing() const { - return _ring; - } + const RingType::Ptr &getRing() const { return _ring; } /** * 获取播放器个数 */ - int readerCount() override { - return _ring ? _ring->readerCount() : 0; - } + int readerCount() override { return _ring ? _ring->readerCount() : 0; } /** * 生成m3u8文件时触发 - * @param file_created 是否产生了hls文件 + * @param index_file m3u8文件内容 */ - void registHls(bool file_created){ + void registHls(std::string index_file) { if (!_is_regist) { _is_regist = true; std::weak_ptr weakSelf = std::dynamic_pointer_cast(shared_from_this()); @@ -61,56 +58,68 @@ public: regist(); } - if (!file_created) { - //没产生文件 + if (index_file.empty()) { + //没产生索引文件, 只是为了触发媒体注册 return; } - //m3u8文件生成,发送给播放器 - decltype(_list_cb) copy; - { - std::lock_guard lck(_mtx_cb); - copy.swap(_list_cb); - } - copy.for_each([](const std::function &cb) { - cb(); - }); + + //赋值m3u8索引文件内容 + std::lock_guard lck(_mtx_index); + _index_file = std::move(index_file); + + _list_cb.for_each([&](const std::function &cb) { cb(_index_file); }); + _list_cb.clear(); } - void waitForFile(std::function cb) { + void getIndexFile(std::function cb) { + std::lock_guard lck(_mtx_index); + if (!_index_file.empty()) { + cb(_index_file); + return; + } //等待生成m3u8文件 - std::lock_guard lck(_mtx_cb); _list_cb.emplace_back(std::move(cb)); } - void onSegmentSize(size_t bytes) { - _speed[TrackVideo] += bytes; + std::string getIndexFile() const { + std::lock_guard lck(_mtx_index); + return _index_file; } + void onSegmentSize(size_t bytes) { _speed[TrackVideo] += bytes; } + private: bool _is_regist = false; RingType::Ptr _ring; - std::mutex _mtx_cb; - toolkit::List > _list_cb; + + std::string _index_file; + mutable std::mutex _mtx_index; + toolkit::List > _list_cb; }; -class HlsCookieData{ +class HlsCookieData { public: - typedef std::shared_ptr Ptr; + using Ptr = std::shared_ptr; + HlsCookieData(const MediaInfo &info, const std::shared_ptr &sock_info); ~HlsCookieData(); + void addByteUsage(size_t bytes); + void setMediaSource(const HlsMediaSource::Ptr &src); + HlsMediaSource::Ptr getMediaSource() const; private: void addReaderCount(); private: - std::atomic _bytes {0}; + std::atomic _bytes { 0 }; MediaInfo _info; std::shared_ptr _added; toolkit::Ticker _ticker; + std::weak_ptr _src; std::shared_ptr _sock_info; HlsMediaSource::RingType::RingReader::Ptr _ring_reader; }; -}//namespace mediakit -#endif //ZLMEDIAKIT_HLSMEDIASOURCE_H +} // namespace mediakit +#endif // ZLMEDIAKIT_HLSMEDIASOURCE_H diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index ff038558..7ef3b63a 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -39,7 +39,7 @@ public: setDelegate(listener); _hls->getMediaSource()->setListener(shared_from_this()); //先注册媒体流,后续可以按需生成 - _hls->getMediaSource()->registHls(false); + _hls->getMediaSource()->registHls(""); } int readerCount() {